Bem,
para começarmos vamos definir algumas nomenclaturas usadas em padrões de
projeto de desenvolvimento.
Java
Bean – Uma classe Java simples, com propriedades privadas,
métodos de acesso, construtor padrão. Daí saem ideias como Bean CDI,
ManagedBean JSF, etc. Também são chamados de POJO (Plain Old Java
Object), que quer dizer nada mais nada menos que “bom e” Velho Objeto
Java Padrão.
PO ou
Entidade – Um bean Java que pode ser persistido no banco de dados.
Também conhecido como PO (Persistent Object). Nomenclaturas muito usadas quando
se trabalha com frameworks de persistência ORM como o Hibernate. Trata-se
de uma classe Java com propriedades privadas, métodos de acesso, construtor
padrão, e métodos de identidade como equals
e hashCode. Também devem implementar a interface Serializable. Podem
ter sobrecarga de construtores e alguns métodos específicos da classe em alguns
casos. Suas propriedades são persistidas no banco de dados.
VO
(Value Object) – São objetos Java usados para guardar valores em memória,
geralmente para usar na camada de visão, não sendo persistidos no banco de
dados. Quando se há a necessidade de persistir parte de suas propriedades ou
todas elas, elas são passadas para uma instancia de uma Entidade. São muito
semelhantes (se não iguais) ao uso dos DTO’s (Data Transfer
Object). Eu particularmente considero exatamente a mesma coisa.
Dada a devida explicação, vemos que basicamente as Entidades tem suas propriedades persistidas no banco enquanto os VO’s são usados para exibir dados na camada de visão ou coisas semelhantes, desde que não envolva persistência.
A
primeira pergunta é: Por quê?
Se eu
posso usar o objeto que é a própria entidade para exibir dados na camada de
visão ou fazer qualquer outra coisa, por que usar um VO
ou DTO?
Basicamente
por 2 motivos:
1.
A Entidade é engessada.
Uma Entidade se comporta como uma estrutura engessada na hora
das consultas, dificultando queries nativas, e impedindo
de mesclar propriedades de Entidades diferentes. Te obriga a montar as
Entidades que compõe a Entidade principal, o que pode trazer um grande
encadeamento dependendo da complexidade das consultas.
2.
Melhora de performance. A Entidade pode
ser muito grande e pesada, e você pode estar precisando apenas de 5 ou 10
propriedades, que podem inclusive serem montadas em Tipos Nativos do Java,
diminuindo o grau de encadeamento destas, o que melhora a performance.
A
segunda é: Quando usar VO e Quando usar Entidade?
Use
Entidades quando os dados forem ser persistidos ou quando de fato você
for carregar todas ou a maioria das propriedades, ou a entidade for simples.
Use VOs quando
for criar Relatórios, disponibilizar os dados em um WebService, quando quiser
juntar dados de diferentes Entidades, etc.; e quando precisar fazer consultas
nativas complexas.
A
terceira e mais importante é: como usar?
Existem algumas formas de preencher o VO a partir
de uma consulta, e isso muda dependendo da forma como a consulta é feita ou o
VO é construído. Por exemplo, se a consulta é nativa ou não, se o VO está ou
não anotado com anotações do JPA, se um construtor é passado dentro da
consulta, etc. Na tabela abaixo vou listar as 3 formas, depois irei dar um
exemplo de cada uma delas:
VO
|
Como preencher na consulta SQL
|
VO com
anotações JPA
|
Consulta nativa passando a
classe para o EntityManager. Ex: “entityManager.createQuery(consulta,
classeVO.class)”
|
VO sem
anotações JPA
|
Consulta
JPQL usando o construtor do VO na consulta, sem passar a classe para o
entityManager.
|
VO sem
anotações JPA
|
Consulta
nativa sem passar a classe do VO para o entityManager e sem usar construtor do
VO na consulta, porém preenchendo o VO percorrendo o resultList que será uma
lista de Object onde cada Object desse é um Array de Object (Object[]) e em
cada posição do array se encontra uma propriedade retornada na consulta.
|
Vamos
analisar primeiro um VO com anotações do JPA e a consulta nativa. Segue o VO:
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class VeiculoVO implements Serializable
{
private static final long serialVersionUID = -5022603734460617635L;
@Id
@Column(name = "SEQ_VEICULO")
private Long seqVeiculo;
@Column(name = "SEQ_LICENCA")
private Long seqLicenca;
@Column(name = "VEI_DSC_PLACA")
private String placa;
@Column(name = "COR")
private String cor;
@Column(name = "MODELO")
private String modelo;
public VeiculoVO()
{
}
public VeiculoVO(final Long seqVeiculo, final Long seqLicenca, final String placa, final String cor, final String modelo)
{
super();
this.seqVeiculo = seqVeiculo;
this.seqLicenca = seqLicenca;
this.placa = placa;
this.cor = cor;
this.modelo = modelo;
}
// demais getters and setters,
equals e hashCode
}
Note
que por ser um VO ele pode conter campos de diferentes Entidades, uma vez que
seu papel é justamente ser um auxiliar. Apesar de anotado com as anotações do
JPA, não informei nenhuma tabela justamente porque ele não tem esse vínculo no
banco de dados.
Os nomes dado aos campos na anotação
@Column devem ser os mesmos retornados na consulta!
Vamos
ao exemplo de uso: uma consulta nativa
onde a classe é passada para o
EntityManager junto com o SQL no momento da criação da query:
final StringBuilder sb = new StringBuilder();
sb.append(" SELECT "
+ " (SELECT LIV.SEQ_LICENCA_AMBIENTAL
FROM EMT_EMPRESA_TRANSBORDO EMT "
+ " INNER
JOIN LIA_LICENCA_VEICULO LIV ON LIV.EMT_SEQ_EMPRESA = EMT.EMT_SEQ_EMPRESA
"
+ " INNER
JOIN VEI_VEICULO VEI2 ON VEI2.LIV_SEQ_LICENCA_VEICULO = LIV.LIA_SEQ_LICENCA_VEICULO
"
+ " WHERE
VEI2.VEI_DSC_PLACA = VEI.VEI_DSC_PLACA) AS SEQ_LICENCA, "
+ "
VEI.VEI_SEQ_VEICULO AS SEQ_VEICULO, VEI.VEI_DSC_PLACA AS VEI_DSC_PLACA,
"
+ " VEI.VEI_DSC_COR
AS COR, VEI.VEI_DSC_MODELO AS MODELO "
+ " FROM EMT_EMPRESA_TRANSBORDO EMT "
+ " INNER JOIN LIV_LICENCA_VEICULO LIV ON LIA.EMT_SEQ_EMPRESA
= EMT.EMT_SEQ_EMPRESA "
+ " INNER JOIN VEI_VEICULO VEI ON VEI.SEQ_LICENCA_VEICULO =
LIV.SEQ_LICENCA_VEICULO "
+ " INNER JOIN CON_CONTRATO CON ON EMT.CON_SEQ_CONTRATO =
CON.CON_SEQ_CONTRATO "
+ " INNER JOIN CLI_CLIENTE CLI ON CON.CLI_SEQ_CLIENTE =
CLI.CLI_SEQ_CLIENTE ");
final Query query = entityManager.createNativeQuery(sb.toString(),
VeiculoVO.class);
return query.getResultList();
Na
Entidade Veiculo, a parte que está grifada de vermelho na verdade é uma
Entidade chamada de Licenca, que guarda dados referentes a licença do veículo,
mas no VO VeiculoVO só era necessário carregar o ID dessa entidade.
Agora
vamos para um segundo caso, que não tem as anotações do JPA no VO, mas usamos JPQL:
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
public class VeiculoVO implements Serializable
{
private static final long serialVersionUID = -5022603734460617635L;
private Long seqVeiculo;
private Long licSeqLicenca;
private String placa;
private String cor;
private String modelo;
public VeiculoVO()
{
}
public VeiculoVO(final Long seqVeiculo, final String placa, final Long seqLicenca)
{
super();
this.seqVeiculo
= seqVeiculo;
this.placa
= placa;
this.seqLicenca
= seqLicenca;
}
public VeiculoVO(final Long seqVeiculo, final Long seqLicenca, final String placa, final String cor, final String modelo)
{
super();
this.seqVeiculo = seqVeiculo;
this.seqLicenca = seqLicenca;
this.placa = placa;
this.cor = cor;
this.modelo = modelo;
}
// demais getters and setters,
equals e hashCode
}
Note
que agora o VO não tem anotações JPA e nós queremos trazer apenas 3 campos dele
dessa vez: a placa e o sequencial do veículo e o sequencial da licença. Para isso
adicionamos o construtor adequado.
Vamos a
consulta JPQL:
final StringBuilder sb = new StringBuilder();
sb.append(" SELECT NEW br.com.meuprojeto.modelo.VeiculoVO(
vei.seqVeiculo,
vei.placaVeiculo, liv.licencaVeiculo) FROM Veiculo vei INNER JOIN vei.licencaVeiculo liv
");
final Query query = entityManager.createQuery(sb.toString());
return query.getResultList();
Agora
vamos para o terceiro caso, onde não temos as anotações do JPA no VO, e usamos
uma consulta nativa, observe que assumimos que o VO possui os getters and
setters necessários:
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
public class VeiculoVO implements Serializable
{
private static final long serialVersionUID = -5022603734460617635L;
private Long seqVeiculo;
private Long licSeqLicenca;
private String placa;
private String cor;
private String modelo;
public VeiculoVO()
{
}
// construtores
// demais getters and setters, equals e hashCode
}
Vamos a
consulta SQL:
final StringBuilder sb = new StringBuilder();
sb.append(" SELECT VEI.VEI_SEQ_VEICULO,
"
+ " VEI.VEI_DSC_PLACA FROM EMT_EMPRESA_TRANSBORDO EMT
"
+ " INNER JOIN LIV_LICENCA_VEICULO LIA ON LIV.EMT_SEQ_EMPRESA
= EMT.EMT_SEQ_EMPRESA "
+ " INNER JOIN VEI_VEICULO VEI ON VEI.SEQ_LICENCA_VEICULO =
LIV.SEQ_LICENCA_VEICULO ");
final Query query = entityManager.createNativeQuery(sb.toString());
final
List<?> resultList = query.getResultList();
Após a
consulta SQL, teremos o resultList que é uma lista de Object onde cada Object
desse é um Array de Object (Object[]) e em cada posição do array se encontra
uma propriedade retornada na consulta. temos que trabalhar esses dados
percorrendo a lista, recuperando os dados e colocando no VO:
final
List<VeiculoVO> lstVeiculoVO = new
ArrayList<>();
if ( resultList != null && resultList.size() > 0 ) {
for ( final Object value : resultList ) {
final VeiculoVO auxiliarVO = new VeiculoVO();
final Object[] result = (Object[]) value;
auxiliarVO.setSeqVeiculo( ( (BigDecimal) result[0] ).longValue() );
auxiliarVO.setPlaca( ( (String) result[1] ).toString() );
lstVeiculoVO.add( auxiliarVO );
}
}
return lstVeiculoVO;
Pronto,
essas são as formas de se trabalhar com VO no JPA. Espero que faça bom
proveito!
Muito bacana suas explicações.
ResponderExcluirEu preciso aprender como popular um "VeiculoVo" que tem como atributo um "List" e recebe no construtor, mas já tentei de tudo e não consegui. Pode me dar um exemplo. Agradeço muito...Valeu!!!
Que bom que gostou! Não da pra você carregar a lista na consulta principal se ela for nativa. No relacionamento um para muitos quando o JPA faz isso pra você ele faz a carga da lista e depois seta na seu bean, quando você faz manualmente, tem que fazer a consulta da lista separada e depois setar no seu bean. Usando consulta JPQL você pode usar FETCH JOIN. Também pode usar o Hibernate.initialize(List) passando sua lista após ter o objeto principal carregado.
ExcluirBom artigo, parabéns.
ResponderExcluirObrigado :)
Excluir