JPA + Selecionando campo

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.

poste aqui como vc está montando a sua query.

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.

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:

	[code]
            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());
           [/code]

ASSIM NÃO LEVANTOU EXCEÇÃO:

            [code]
            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());[/code]

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 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.

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:

[code]
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[/code]

Elias,

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

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();

[code]

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.(EJBQueryImpl.java:114)
at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.(EJBQueryImpl.java:99)
at oracle.toplink.essentials.internal.ejb.cmp3.EJBQueryImpl.(EJBQueryImpl.java:86)
at oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerImpl.createQuery(EntityManagerImpl.java:204)[/code]

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?

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.

Felipe,

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

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.

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.

[quote=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.[/quote]

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

Vou tentar aqui, tô rastejando no java ainda…

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!

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…

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

Mas você tem sabe da performance desse modelo?

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

Mas você tem sabe da performance desse modelo?[/quote]

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…