Mapear varias tabelas com a mesma classe JPA.

Java

06/07/2013

Bom dia a todos, pessoal estou precisando fazer o mapeamento de varias tabelas na mesma classe.
Ou se tiver como alterar o nome da tabela na anotação Table(name="pedidos"), em tempo de execução também server.

EX: Tenho tabela de pedidos e pedidositens, pra ficar otimizado devido o grande volume de dados tem que ser dividida por mês, ou seja pedidos_201301, pedidos_201302, ..., o mesmo para tabela de pedidositens.
Pensei em usar herença nessas tabelas entidades, mas ai o sistema ira ter muitas classes, o que achei muito trabalhoso.

Desde já obrigado pela atenção.
Tecnologias envolvidas, JPA, JSF 2.0, PrimeFaces, Tomcat 6.


Fernando Silva

Fernando Silva

Curtidas 0

Respostas

Marcelo Senaga

Marcelo Senaga

06/07/2013

E como está a definição das classes e das tabelas no banco de dados?
O melhor seria o mapeamento 1x1, quanto mais classes, melhor. Cada classe deve representar um conceito diferente do sistema.
GOSTEI 0
Fernando Silva

Fernando Silva

06/07/2013

E como está a definição das classes e das tabelas no banco de dados?

Então as tabelas são grandes pra postar, mas em resumo são tabelas comuns, porém todas tabelas comuns terão a mesma estrutura a exemplo destas que postei pedidos e itens.

Só irei dividir pra ficar fácil a manutenção, e rápido os selects, até pra fazer limpeza de dados ficará melhor.
 


O melhor seria o mapeamento 1x1, quanto mais classes, melhor. Cada classe deve representar um conceito diferente do sistema.


Neste caso em 5 anos eu teria:
Pedidos 5 * 12 = 60
ItensPedidos 5 * 12 = 60
Vendas 5 * 12 = 60
ItensVendas 5 * 12 = 60
Inventario 5 * 12 = 60
Orçamento 5 * 12 = 60
OrçamentoItens 5 * 12 = 60
MovProdutos 5 * 12 = 60
Cupons 5 * 12 = 60
ItensCupons 5 * 12 = 60

Olhando para meus diagramas verifiquei que tenho estas tabelas acima pra criar e todas terão muitos registros.
O que totalizaria 600 classes entidades em 5 anos, não seria muito ?
Fico com medo de fazer algo que fique meio fora de padrão.


GOSTEI 0
Fernando Silva

Fernando Silva

06/07/2013

Vou postar as classes mapeadas pra ver se ajuda:

//Aqui a de pedido as mensais será igual
public class ComPedido7 implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "idcodigo7")
    private Integer idcodigo7;
    @JoinColumn(name = "fkfornecedor7", referencedColumnName = "idcodigo15")
    @OneToOne()
    private GerFornecedor15  fkfornecedor7;
    @Basic(optional = false)
    @Column(name = "fkempresa7")
    private int fkempresa7;
    @Column(name = "fkusuinc7")
    private Integer fkusuinc7;
    @Column(name = "fkusualt7")
    private Integer fkusualt7;
    @Basic(optional = false)
    @Column(name = "datainc7")
    @Temporal(TemporalType.TIMESTAMP)
    private Date datainc7;
    @Column(name = "dataalt7")
    @Temporal(TemporalType.TIMESTAMP)
    private Date dataalt7;
    @Column(name = "preventrega7")
    @Temporal(TemporalType.DATE)
    private Date preventrega7;
    @Column(name = "obs7")
    private String obs7;
    // @Max(value=?)  @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
    @Column(name = "vlrfrete7")
    private BigDecimal vlrfrete7;
    @Basic(optional = false)
    @Column(name = "statusbaixa7")
    private String statusbaixa7;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "comPedido7")
    private List<ComPedidoItens8> comPedidoItens8List;
    @JoinColumn(name = "fkconfigpedido7", referencedColumnName = "idcodigo9")
    @ManyToOne(optional = false)
    private ComConfpedido9 fkconfigpedido7;
    @Transient
    private String nomeFornecedor;
    @Transient
    private Integer configPedidoString;

}
//Aqui as tabelas de itensPedidos
public class ComPedidoItens8 implements Serializable {
    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected ComPedidoItens8PK comPedidoItens8PK;
    @Basic(optional = false)
    @Column(name = "seqitem8")
    private int seqitem8;
    // @Max(value=?)  @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
    @Column(name = "qtditem8")
    private BigDecimal qtditem8;
    @Column(name = "qtdembitem8")
    private Integer qtdembitem8;
    @Column(name = "custoitem8")
    private BigDecimal custoitem8;
    @Column(name = "ipiitemper8")
    private BigDecimal ipiitemper8;
    @Column(name = "icmsitem8")
    private BigDecimal icmsitem8;
    @Column(name = "stitem8")
    private BigDecimal stitem8;
    @Column(name = "databaixa8")
    @Temporal(TemporalType.TIMESTAMP)
    private Date databaixa8;
    @Basic(optional = false)
    @Column(name = "statusbaixa8")
    private String statusbaixa8;
    @Column(name = "qtdbaixada8")
    private BigDecimal qtdbaixada8;
    @Column(name = "custobaixado8")
    private BigDecimal custobaixado8;
    @JoinColumn(name = "fkitem8", referencedColumnName = "idcodigo1", insertable = false, updatable = false)
    @ManyToOne(optional = false)
    private EstProdutos1 estProdutos1;
    @JoinColumn(name = "idfkpedido8", referencedColumnName = "idcodigo7", insertable = false, updatable = false)
    @ManyToOne(optional = false)
    private ComPedido7 comPedido7;
}
GOSTEI 0
Marcelo Senaga

Marcelo Senaga

06/07/2013

Entendi o seu problema. Veja se esse link pode te ajudar:

http://ossevagolb.blogspot.com.br/2010/12/mapear-e-preciso-xml-e-so-um-meio.html

Procure sobre mapeamento dinamico em hibernate.
GOSTEI 0
Fernando Silva

Fernando Silva

06/07/2013

Entendi o seu problema. Veja se esse link pode te ajudar:

http://ossevagolb.blogspot.com.br/2010/12/mapear-e-preciso-xml-e-so-um-meio.html

Procure sobre mapeamento dinamico em hibernate.



Seguinte olhei as soluções que indicou, mas pensei em usar outro recurso direto no banco.
1 - Particionar tabelas;
2 - Colocar triggers para direcionar os inserts para as tabelas certas.

O que você acha ??
GOSTEI 0
Anthony Accioly

Anthony Accioly

06/07/2013


Seguinte olhei as soluções que indicou, mas pensei em usar outro recurso direto no banco.
1 - Particionar tabelas;
2 - Colocar triggers para direcionar os inserts para as tabelas certas.

O que você acha ??


Uma maneira de alcançar o resultado almejado seria criar views e mapeá-las para suas respectivas entidades. e.g., uma view com a união de todas as tabelas antigas apenas para consultas históricas (não performático) e outra view "apontando" para a tabela física mais atual para operações de escrita e o grosso das consultas. Do lado do JPA você teria apenas duas entidades PedidoHistorico (apontando para a view da união) e PedidoAtual.

Pensando no Oracle, você pode criar jobs (http://docs.oracle.com/cd/E11882_01/server.112/e25494/scheduse002.htm) para atualizar as duas views mensalmente, de maneira que tudo fica 100% transparente para a aplicação.

Observação: Criar novas tabelas para diminuir o número de linhas analisado por consulta é, de certa maneira, reinventar a roda (e fazer manualmente algo que poderia ser automatizado). O nome do que vocês estão fazendo é particionamento horizontal (http://en.wikipedia.org/wiki/Partition_(database)); mais especificamente, particionamento por range de datas distribuídos em um único nó do banco de dados [o que não é escalável no longo prazo). No caso, se você puder adaptar o mecanismo de armazenamento à aplicação ao invés de adaptar a aplicação ao mecanismo de armazenamento, há várias opções que estendem a funcionalidade básica do ORM para trabalhar com particionamento. Seguem exemplos:

Hibernate Shards: [url]http://www.hibernate.org/subprojects/shards.html[/url]
Eclipse Link:[url]http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Data_Partitioning[/url]
GOSTEI 0
Marcelo Senaga

Marcelo Senaga

06/07/2013


Seguinte olhei as soluções que indicou, mas pensei em usar outro recurso direto no banco.
1 - Particionar tabelas;
2 - Colocar triggers para direcionar os inserts para as tabelas certas.

O que você acha ??


Uma maneira de alcançar o resultado almejado seria criar views e mapeá-las para suas respectivas entidades. e.g., uma view com a união de todas as tabelas antigas apenas para consultas históricas (não performático) e outra view "apontando" para a tabela física mais atual para operações de escrita e o grosso das consultas. Do lado do JPA você teria apenas duas entidades PedidoHistorico (apontando para a view da união) e PedidoAtual.

Pensando no Oracle, você pode criar jobs (http://docs.oracle.com/cd/E11882_01/server.112/e25494/scheduse002.htm) para atualizar as duas views mensalmente, de maneira que tudo fica 100% transparente para a aplicação.

Observação: Criar novas tabelas para diminuir o número de linhas analisado por consulta é, de certa maneira, reinventar a roda (e fazer manualmente algo que poderia ser automatizado). O nome do que vocês estão fazendo é particionamento horizontal (http://en.wikipedia.org/wiki/Partition_(database)); mais especificamente, particionamento por range de datas distribuídos em um único nó do banco de dados [o que não é escalável no longo prazo). No caso, se você puder adaptar o mecanismo de armazenamento à aplicação ao invés de adaptar a aplicação ao mecanismo de armazenamento, há várias opções que estendem a funcionalidade básica do ORM para trabalhar com particionamento. Seguem exemplos:

Hibernate Shards: [url]http://www.hibernate.org/subprojects/shards.html[/url]
Eclipse Link:[url]http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Data_Partitioning[/url]


Excelente postagem! Vou dar uma olhada no Hibernate Shards.
GOSTEI 0
Fernando Silva

Fernando Silva

06/07/2013

Então pessoal seguindo a ideia que o Anthony Accioly, de deixar o mecanismo de armazenamento cuidar dos dados, criei uma function no banco que vai direcionar os "inserts" para as devidas tabelas, pretendo fazer o mesmo para as demais operações do CRUD.

abaixo a função.

Obrigado a todos que ajudaram.


CREATE OR REPLACE FUNCTION pedido_ajusta_insert_antes()
  RETURNS trigger AS
$BODY$
    DECLARE nomeTabela varchar(100);
    BEGIN
        nomeTabela := ('pedido_'||Trim(to_char(DATE_PART('MONTH', Date(NEW.data_pedido)),'00'))||Trim(to_char(DATE_PART('YEAR', Date(NEW.data_pedido)),'9999')));
        IF (TG_OP = 'INSERT') THEN
		EXECUTE 'INSERT INTO '||nomeTabela||'(id_pedido, data_pedido, id_cliente, id_vendedor) '
			'VALUES ( '||nextval('pedido_id_pedido_seq'::regclass)|| ' , '||NEW.data_pedido||' , '||NEW.id_cliente||' , '||NEW.id_vendedor||' )';
		return NEW;
        END IF;
    END;
  $BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION pedido_ajusta_insert_antes()
  OWNER TO postgres;
GOSTEI 0
José

José

06/07/2013

Tento entendido que a duvida inicial foi sanada, estou dando o tópico por concluído.
Caso a duvida não tenha sido sanada totalmente, peça para reabrirmos o tópico, ou fique a vontade para abrir um novo.

GOSTEI 0
POSTAR