JPA + Selecionando campo

21 respostas
U

Bom dia pessoal,

Futucando JPA, hj me deparei com uma situação que me deixou intrigado.

Exemplo:
Tenho uma entidade chamada CLIENTES com os atributos ID, Nome, Endereco e CPF

Pra eu rotornar uma lista dos clientes é tranquilo, mas se por exemplo quero puxar uma
lista só com ID e Nome, é possível? como?
Eu tentei aqui, mas só dá erro na construção do EntityManager.

Falew.

21 Respostas

L

poste aqui como vc está montando a sua query.

U

Ola Ibosco,

//Tenho uma lista completa
em.createQuery("Select pj  FROM PessoaJuridica pj).getResultList();

//Aqui queria selecionar algumas campos
em.createQuery("Select pj.ID, pj.Fantasia  FROM PessoaJuridica pj).getResultList();

Algo mais ou menos assim.
A minha intensão é, ao abrir um cadastro de clientes e precisar fazer uma busca de
um cliente, abrir um JDialog com alguns campos, e carregar o objeto inteiro seria algo
iviável, no meu caso.

L

O que acontece é o seguinte:

o método "getResultList()" retorna um List de Object,
e não da classe model que você está querendo.

Eu fiz alguns testes aqui, e quando tento selecionar alguns campos apenas, como você está fazendo,
ao fazer a iteração da lista de objetos que o JPA me traz, ele levanta uma ClassCastException, ou seja,
como o Object que ele retorna não possui o mesmo número nem os mesmos campos que a minha classe possui,
ele não consegue fazer o "casting". Criei uma base de testes aqui , veja abaixo o que tentei fazer:

ASSIM LEVANTOU EXCEÇÃO:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa-hibernate");
		EntityManager em = emf.createEntityManager();

		Query query = em.createQuery("SELECT c.nmCliente, c.nrCnpj from Cliente c");
		List <Cliente> list;
		list = (List<Cliente>) query.getResultList();

		em.close();
		emf.close();
		
		for(Cliente c : list)
			System.out.println( "NOME CLIENTE: "+ c.getNmCliente()+ "CNPJ: "+c.getNrCnpj());

ASSIM NÃO LEVANTOU EXCEÇÃO:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa-hibernate");
		EntityManager em = emf.createEntityManager();

		Query query = em.createQuery("SELECT c from Cliente c");
		List <Cliente> list;
		list = (List<Cliente>) query.getResultList();

		em.close();
		emf.close();
		
		for(Cliente c : list)
			System.out.println( "NOME CLIENTE: "+ c.getNmCliente()+ "CNPJ: "+c.getNrCnpj());
U

Pois é, aí fica então a questão: até onde vale a pena usar essas tecnologias?

Pq, se quero simplesmente trazer um ou dois campos de uma tabela com 20 campos, eu acabo
trafegando 18 campos em vão e dependendo do volume de dados, isso pode prejudicar
a performance, “sem sentido”.

E

E se você criar uma @NamedQuery dentro de sua entidade.
Tipo:
@NamedQuery (name = “Proprietario.findAll”, query = “SELECT c.nmCliente, c.nrCnpj FROM cliente c”)

Já tentou.

A

ola..
da para fazer sim... mas ai vc tem que transformar sua List num array de Objeto
e cada posição do array do Objeto retorna outro array de objeto, que correspondem aos atributos da sua consulta... entende?!?!?

exemplo:
List messages = em.createQuery("select m.id, m.text from Message m order by m.text asc").getResultList();

	        Object[] obj = messages.toArray();//transformar sua List num array de Objeto
	        
	        Object[] o = (Object[]) obj[0];//cada posição do array do Objeto retorna outro array de objeto, peguei a pos 0
	        
	        Long id = (Long) o[0];
	        String text = (String) o[1];
	        System.out.println("id : "+id+" text "+text);
console:
Hibernate: 
    /* select
        m.id,
        m.text 
    from
        Message m 
    order by
        m.text asc */ select
            message0_.MESSAGE_ID as col_0_0_,
            message0_.MESSAGE_TEXT as col_1_0_ 
        from
            MESSAGES message0_ 
        order by
            message0_.MESSAGE_TEXT asc

id : 1 text Hello Word JPA
U

Elias,

Nunca tentei não.
Mas n daria no mesmo, a diferença n seria apenas por jah ter uma consulta pronta (nomeada)?

U

Felipe,

No meu caso, o erro tá já na criação da consulta, ou seja, n consegui fazer como no seu exemplo.

List query =  em.createQuery("Select t.ID, t.Descricao  FROM Conta t").getResultList();

        Object [] obj = query.toArray();
debug:
[TopLink Info]: 2009.01.12 03:44:55.011--ServerSession(28346522)--TopLink, version: Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))
[TopLink Info]: 2009.01.12 03:44:55.568--ServerSession(28346522)--file:/media/ARQUIVOS/Projetos2009/Phenix/src/-PhenixPU login successful
Exception in thread "main" java.lang.IllegalArgumentException: An exception occured while creating a query in EntityManager
        at oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerImpl.createQuery(EntityManagerImpl.java:209)
        at phenix.Main.main(Main.java:51)
Caused by: Exception [TOPLINK-8030] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.EJBQLException
Exception Description: Error compiling the query [Select t.ID, t.Descricao  FROM Conta t], line 1, column 16: unknown state or association field [Descricao] of class [br.com.yeld.phenix.entidade.Conta].
        at oracle.toplink.essentials.exceptions.EJBQLException.unknownAttribute(EJBQLException.java:474)
        at oracle.toplink.essentials.internal.parsing.DotNode.validate(DotNode.java:101)
        at oracle.toplink.essentials.internal.parsing.SelectNode.validate(SelectNode.java:329)
        at oracle.toplink.essentials.internal.parsing.ParseTree.validate(ParseTree.java:229)
        at oracle.toplink.essentials.internal.parsing.ParseTree.validate(ParseTree.java:211)
        at oracle.toplink.essentials.internal.parsing.ParseTree.validate(ParseTree.java:201)
        at oracle.toplink.essentials.internal.parsing.EJBQLParseTree.populateReadQueryInternal(EJBQLParseTree.java:134)
        at oracle.toplink.essentials.internal.parsing.EJBQLParseTree.populateQuery(EJBQLParseTree.java:108)
        at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:219)
        at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:189)
        at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:153)
        at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.<init>(EJBQueryImpl.java:114)
        at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.<init>(EJBQueryImpl.java:99)
        at oracle.toplink.essentials.internal.ejb.cmp3.EJBQueryImpl.<init>(EJBQueryImpl.java:86)
        at oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerImpl.createQuery(EntityManagerImpl.java:204)
A

mas existe a propriedade Descricao?
não teria que ficar assim sua query:

List query =  em.createQuery("Select t.id, t.descricao  FROM Conta t").getResultList();

com os nomes das propriedades em minusculo? ja tentou?

U

P… m…

Foi mal…
Compilou, vou testar.
Mas q eh f… ficar trabalhando com objetos é, vai rolar muito box e uboxing na minha aplicação, pq
vou precisar muito de usar esse tipo de esquema.

Cara, tô acostumado com o C#, um caso desse eu faria assim:

//dc eh meu DataContext, algo como EntityManager
                var query = (from c in dc.Conta                             
                            select new { c.ID, c.Descricao }).ToList();

                foreach (var c in query)
                      ........
//Aqui n preciso trabalhar com cast, os tipos são os definidos na classe.
//e posso usar a "query" como fonte de dados pra qq bagaça.

De vez em qdo a Microsoft acerta em alguma coisa *rrr, o LINQ e métodos anônimos foram um bom acerto.

Mas quero, “a todo custo” fazer esta minha aplicação em Java, quero me livrar da MS *r

Valeu a ajuda Felipe, qq coisa, posto aqui novamente.

U

Felipe,

Tô postando outra dúvida q acho q vc vai sacar!

L

O retorno é sempre um objeto. Se quer retornar só um pedaço do objeto, terá que usar um DTO e instanciá-lo na própria query, assim:

em.createQuery("SELECT new meu.pacote.ClienteDTO(c.nmCliente, c.nrCnpj) from Cliente c").getResultList();

Obviamente, precisará de um ClienteDTO com construtor com dois argumentos e tipos compatíveis com os retornados pelas “colunas” da query.

U

Eu sei q “tudo” é objeto, mas qdo é tipado diminui trabalho e consome menos recursos.
Esta solução apresentada retornaria um List Object[] tb? Ele me pareceu mais limpa.

L

UpTheIrons:
Eu sei q “tudo” é objeto, mas qdo é tipado diminui trabalho e consome menos recursos.
Esta solução apresentada retornaria um List Object[] tb? Ele me pareceu mais limpa.

Retorna uma lista de ClienteDTO, portanto, dá pra fazer o cast para List. Teste e veja.

U

Vou tentar aqui, tô rastejando no java ainda…

U

Leonardo,

Funcionou blzinha mesmo!
Ainda tô em “fase de testes”, mas esse código ficou bom!

Eu postei algo sobre relacionamento N-N q tô apanhando aqui, se vc puder dar uma luz.

Valeu velhinho!

M

Você pode usar um Map pra resolver essa questão:

public List<MinhaClasse> pesquisar(String query, Map<String, Object> parametros){
  Query q = getEntityManager().createQuery(query);

  //separa os parâmetros
  for(String chave: parametros.KeySet()){
    q.setParameter(chave, parametros.get(chave));
  }

  return q.getResultList();
}

Dessa forma vc pode transmitir quantos parâmetros forem necessários na pesquisa, trazendo os valores pretendidos…

U

Opa, tava vendo sua solução, tenho reolhar o código pra entender *r.

Mas você tem sabe da performance desse modelo?

M

UpTheIrons:
Opa, tava vendo sua solução, tenho reolhar o código pra entender *r.

Mas você tem sabe da performance desse modelo?

Como vc está usando as Annotations de JPA, essa questão de carregar todos os dados sem necessidade, pode ser evitado usando LAZY. Dessa forma, o conteúdo só será trazido quando necessário.

Dá uma pesquisada sobre FetchType.LAZY e sobre Open Session in View, vc não terá problemas de performance, pois só usará o conteúdo que desejas, que neste caso é o codigo e o nome…

U

Opa, de volta…

Então, eu já tinha lido algo sobre o Lazy, mas se entendi direito ele não carrega (caso não haja necessidade)
as entidades associadas, não!?

Eu até criei outra classe para funcionar como DTO, como sugerido aqui, mas o prob dessa solução é que vou
ter muitas classes “repetidas”, não gostaria, mas estou fazendo assim.

Usando Array de objetos deixaria esta parte mais limpa, mas trab muito com cast e não sei até onde isso pode
influenciar na performance.

Na verdade, acho q vou ter q fazer uns testes mais pesados com as duas soluções acima citadas.

Falew!

M

Pra evitar repetições use Dao Generico, procure sobre isso. Estarei viajando hoje, então vou passar um tempinho fora, mas se vc pesquisar vai encontrar o que estou dizendo e vai solucionar o problema!

t+

Criado 12 de janeiro de 2009
Ultima resposta 16 de jan. de 2009
Respostas 21
Participantes 6