Realizar uma consulta em JPA com uma condição WHERE em um objeto filho

25 respostas Resolvido
eclipselinkjpajava
Rodrigomarden

Estou realizando uma consulta em JPA para me trazer uma lista de objetos e quando eu clicar em um objeto, ele vai me trazer uma lista de objetos que são relacionadas a esse objeto.

O relacionamento é esse abaixo, só que o JPA está gerando a consulta de forma automática, e eu não sei que método ele utiliza para fazer isso, então não consigo adicionar uma condição, como por exemplo: WHERE COLUNA = ?.
Existe algum método próprio do JPA como o @OrderBy que eu possa chamar e criar essa condição?

@OneToMany(cascade = CascadeType.ALL)
    @OrderBy(value = "parcela")
    @JoinColumn(name = "CODIGO_OBRA", referencedColumnName = "CODIGO")
    private List<ResPagamentoParcela> resPagamentoParcelaList;

25 Respostas

M

Rodrigo,

Pelo que eu saiba n existe anotação @where no JPA, somente pelo hibernate.

Mas vc pode utilizar o where quando for montar a query.

Pode mostrar o método q vc utiliza para fazer a busca da entidade, para poder te ajudar melhor.

Rodrigomarden

Valeu pela resposta Matheus,

Então, vou descrever o passo a passo

Primeiro eu busco uma lista de objetos A e gero a query com o método abaixo.

javax.persistence.Query q = getEntityManager().createQuery(sb.toString());

Aqui eu consigo fazer o controle com o WHERE que eu quero para esse objeto A, só que, a partir dessa lista, eu seleciono 1 objeto que irá me trazer mais informações para edição, onde irá me trazer outra lista de objetos B, sendo assim existe o relacionamento OneToMany como eu trouxe acima.
Essa segunda consulta é gerada a partir do JPA direto, por conta do relacionamento, então não fui eu que a criei, mas sim o relacionamento, então eu não sei como colocar essa condição.

javaflex

Usa HQL.

Rodrigomarden

O problema é que eu não sei onde é feita a chamada dessa query para que eu possa criar outra ou alterar a que já existe.

javaflex

https://www.tutorialspoint.com/pg/jpa/jpa_jpql.htm#

Se nao sabe onde é, debuga ou faz do zero. Eu nem usuaria jpa, faria diretamente via SQL com jdbcTemplate, muito mais eficiente e sem quebra cabeças.

Rodrigomarden

O problema de usar uma query é que geraria uma má performance, pois traria lista de listas, da forma que eu estou fazendo, por exemplo, eu busco 10 objetos A e em cada objeto A tem 10 objetos B também, só que ele só me trás 10 objetos A e quando eu escolho 1 objeto A, ele me trás os 10 objetos de B. Se eu fizer por query, ele vai me trazer direto 10 objetos A e 100 objetos B.
Claro que na prática seria muito mais objetos dentro dessa lista, o que poderia piorar o desempenho.

Rodrigomarden

O que faz a consulta é o relacionamento que eu mandei ali em cima. Eu só queria saber como o JPA faz isso e se tem como eu alterar de alguma forma.

javaflex

Basta usar HQL/JPQL ou SQL.

M

Não necessariamente, vc pode utilizar o where para trazer exatamente os dados que vc precisa. Basta saber utilizar bem os conceito do SQL.

Se vc quer q ele n traga a lista, utilize o fetch = FetchType.LAZY dentro do @JoinColumn, desse modo a lista só será preenchida qndo vc precisar.

Rodrigomarden

Mas é exatamente isso que eu estou fazendo, só que quando faço a consulta gerada pelo relacionamento eu não consigo gerar a condição que eu quero.

javaflex

Concordo com @matheusYudi, nao tem sentido isso. Na query voce pode ter total controle do que precisa ser retornado, principalmente se for via SQL e jogando em um “DTO”, fica bem performático. Pode ser via JPQL também, embora eu ache mais engessado.

Rodrigomarden

O problema é que eu não sei de onde a query está sendo gerada pra que eu possa alterar.

Aqui quando eu clicar no botão de edição ele me leva pra outra página.

Código do botão de edição:

<p:commandButton icon="ui-icone ui-icon-pencil" action="Edit?faces-redirect=true&amp;includeViewParams=true" styleClass="botaoCelg" >
            <f:setPropertyActionListener value="#{item}"  target="#{resObraController.selected}" />
        </p:commandButton>

E me é direcionado para outra página onde já contém as informações e a lista de objetos B com o seguinte link: http://localhost:8080/SGT-SGI/pagina/ressarcimento/Edit.jsf?codigo=3612035, só que a query que faz essa busca eu desconheço de onde venha, não há nenhuma chamada no código de uma query, por isso a minha dúvida.

M

Vamos lá, vou considerar que vc começando a aprender o conceito do JPA seja em um curso ou na faculdade e esse código já estava semi pronto.

Primeiramente, o @OneToMany por padrão vem como fetch Lazy, isso quer dizer q qndo vc faz uma consulta no banco pelo seu objeto (vou considerar q ele se chama Pagamento) ele devolve os atributos, porém se vc ver o valor da lista resPagamentoParcelaList n vai ter nenhum valor nele a não ser se sua consulta no objeto Pagamento estiver explicitamente trazendo o dado do objeto ResPagamentoParcela.

Tendo isso em mente vc precisa entender o motivo do resPagamentoParcelaList estar na sua entidade de Pagamento, faça as perguntas, eu preciso dela? Para q eu preciso dela? Qndo eu n preciso dela? Qndo eu n preciso dela?
Parece ser perguntas idiotas mas elas realmente ajudam a entender a regra e nos guiar melhor. Dps de responder essas perguntas comece a entender o código busque entender o que cada método faz, comece pelos seus controllers e vai descendo. No começo é dificil msmo, tdo acaba parecendo grego mas sempre q encontrar algo q n conheça busque na internet e até o momento q vc vai encontrar onde está sendo feito a persistência dos dados, isso vai te facilitar mto n só a alterar isso mas entender o q está acontecendo e qndo tiver outro problema estar mais fácil onde ajustar.

Não faz mto tempo q eu comecei a desenvolver por isso entendo um pouco as dores de qm está começando, sei q talvez n consegui te ajudar a resolver esse problema em especifico, mas espero q tenha dado uma clareada no conceito.

javaflex

Pesquisa sobre jpa show sql e debuga até achar a linha que faz a query.

Dependendo do que for, pode ser mais rápido fazer do zero. Faz um SQL específico pra atender a funcionalidade e pronto, sem mistério. Nesse tempo que passou já teria feito isso.

Rodrigomarden
@OneToMany(cascade = CascadeType.ALL)
    @OrderBy(value = "parcela")
    @JoinColumn(name = "CODIGO_OBRA", referencedColumnName = "CODIGO")
    private List<ResPagamentoParcela> resPagamentoParcelaList;

Sabe me dizer se tem alguma notação que sempre que esse relacionamento for usado, ele utilizar uma NamedQuery?

Como eu disse, eu criei a query, só que não sei onde chamar ela, porque ela é feita somente através desse relacionamento, a query gerada é SELECT * FROM PAGAMENTO WHERE CODIGO_OBRA = ?
Tem como fazer com que sempre que esse relacionamento for feito, ser através de uma query especifica?

javaflex

Tem a anotação @where(clause = “…”), pesquise sobre.
Mas deixar isso amarrado na entidade eu acho esquisito.

Bom, já deixei minha opinião, não era pra ser complicado, e sim atender cada funcionalidade de forma específica, sem essa engenharia toda que só faz uma coisa impactar a outra e ficar nessas complicacoes de n configuracoes. Se a tela precisa da grid tal, faz um select com estrutura específica pra isso, nao tem mistério.

Rodrigomarden

Esse é o problema, eu queria algo que fosse pra TUDO, pois esse where é pra ver se aquele item está ativo ou não, eu usaria em TODOS os relacionamento que eu tenho e resultaria em todas as telas que eu tenho, fazer isso na mão criando query para cada uma delas daria muito trabalho.

javaflex
Solucao aceita

Cria uma view no banco que só retorna os ativos.

Rodrigomarden

Não era o que eu estava procurando, mas resolve meu problema. :slight_smile:

Muito obrigado pela paciência.

javaflex

O que voce está procurando pode ser a anotação @where. A view foi só minha opinião do que uso na minha experiência, por ser muito mais prático, atende n sistemas inclusive.

Rodrigomarden

Sim, mas eu uso somente JPA e não Hibernate.

Eu usava View para outras coisas, mas não me veio a mente usar para isso também, vai ser uma mão na roda, porque vou poder usar para levantamento de dados de forma mais rápida.

javaflex

Entendi, essa anotação é do hibernate mesmo. Entao view é mesmo a melhor solução neste caso e muitos outros.

Rodrigomarden

@javaflex

Com a solução da VIEW, consegui resolver parte do problema, só que agora quando eu altero a coluna ativo para false, ele some na VIEW, mas no meu objeto ainda permanece.

Eu tentei usar o método refresh do JPA, só que ele tá me retornando o erro: Caused by: java.lang.IllegalArgumentException: Cannot refresh unmanaged object: br.com.celg.entidade.ResObra[ codigo=3612030 ].

Eu já li vários fóruns falando sobre, mas nenhum me ajudou.Pelo o que eu entendi é porque o objeto está no estado DETACHED e para usar o comando refresh ele precisa estar MANAGED.
Como eu faria isso? tentei dar usar o método find para buscar novamente no banco de dados e assim utilizar o refresh, mas ainda assim continua o mesmo erro.

javaflex

Nao misture as coisas, a view é pra consulta, pra alteração use a tabela.

javaflex

Se nao for isso, tente mapear a classe da view com @Immutable.

Criado 12 de agosto de 2019
Ultima resposta 20 de ago. de 2019
Respostas 25
Participantes 3