Como realizar esta consulta utilizando Hibernate JPA?

Possuo uma classe “Aluno” mapeada como “tb_aluno” e uma classe TurmaPeriodo mapeada como “tb_turma_periodo”.
Estas classes se relacionam da forma muitos para muitos (Many To Many) e a classe aluno contém uma coleção de TurmaPeriodo.

@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name="tb_alunos_turmas_periodo", joinColumns={
    @JoinColumn(name="aluno_id", nullable=false)
}, inverseJoinColumns={
    @JoinColumn(name="turma_periodo_id", nullable=false)
})
private Set<TurmaPeriodo> turmasPeriodo;

O problema surge no momento em que eu desejo obter todos os alunos que possuem uma determinada TurmaPeriodo. Como a tabela de relacionamento é criada pelo próprio hibernate, não vejo uma forma de acessá-la utilizando código JPA. A forma que encontrei para fazer foi a seguinte:

CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Aluno> criteriaQuery = builder.createQuery(Aluno.class);
Root<Aluno> aluno = criteriaQuery.from(Aluno.class);
criteriaQuery.select(aluno);
                    
Subquery<TurmaPeriodo> subquery = criteriaQuery.subquery(TurmaPeriodo.class);
Root<TurmaPeriodo> rootTurmaPeriodo = subquery.from(TurmaPeriodo.class);
subquery.select(rootTurmaPeriodo);
        
ParameterExpression<Integer> paramTurmaPeriodo = builder.parameter(Integer.class, "turmaPeriodo");
subquery.where(builder.equal(rootTurmaPeriodo.get("id"), paramTurmaPeriodo));
criteriaQuery.where(builder.exists(subquery));

TypedQuery<Aluno> query = getEntityManager().createQuery(criteriaQuery);
query.setParameter("turmaPeriodo", turmaPeriodo.getId());
List<Aluno> alunos = query.getResultList();

Infelizmente esta abordagem não deu certo, e não importa qual id de TurmaPeriodo eu passe, a consulta sempre me retorna o conjunto com todos os alunos cadastrados. O motivo disto eu já entendi, a cláusula EXISTS verifica que a turma existe e me retorna todos os alunos, pois não estou delimitando a igualdade no where interno. Porém não consegui encontrar uma solução para isso. Segue abaixo o código gerado pelo hibernate:

select
        aluno0_.id as id1_7_,
        aluno0_2_.email as email2_7_,
        aluno0_2_.foto as foto3_7_,
        aluno0_2_.nome as nome4_7_,
        aluno0_1_.cpf as cpf1_8_,
        aluno0_1_.data_expedicao as data_exp2_8_,
        aluno0_1_.data_nascimento as data_nas3_8_,
        aluno0_1_.orgao_expedidor as orgao_ex4_8_,
        aluno0_1_.rg as rg5_8_,
        aluno0_.data_matricula as data_mat1_0_ 
    from
        tb_aluno aluno0_ 
    inner join
        tb_pessoa_fisica aluno0_1_ 
            on aluno0_.id=aluno0_1_.id 
    inner join
        tb_pessoa aluno0_2_ 
            on aluno0_.id=aluno0_2_.id 
    where
        exists (
            select
                turmaperio1_.id 
            from
                tb_turma_periodo turmaperio1_ 
            where
                turmaperio1_.id=?
        )

Qualquer ajuda é bem vinda.

1 curtida

Quando trabalho num sistema e aparece uma situação onde será utilizado um relacionamento many-to-many, tenho o hábito de criar uma classe à parte com uma chave composta por FK’s de cada entidade relacionada, ex: Aluno -> Aluno.id - TurmaPeriodo.id <- TurmaPeriodo.

Utilizando essa classe à parte (em vez de usar um Set), fica mais fácil de criar as consultas. Talvez se você usar essa abordagem, ficará mais fácil para resolver seu problema.

thiagosampaio,

Primeiramente, o EXISTS é necessário?

Em segundo, vou propor uma ideia, mas não tive como testar (estou digitando o código direto, por isso deve conter erros - faz os ajustes necessários)

CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Aluno> criteriaQuery = builder.createQuery(Aluno.class);
Root<TurmaPeriodo> rootTurmaPeriodo = criteriaQuery.from(TurmaPeriodo.class);
criteriaQuery.where(builder.equal(rootTurmaPeriodo.get(TurmaPeriodo_.id),turmaPeriodo.id())));
SetJoin<Aluno,TurmaPeriodo> alunos = criteriaQuery.join(Aluno_.turmasPeriodo);
CriteriaQuery<aluno> alunosSelect = criteriaQuery.select(alunos);
TypedQuery<aluno> query = getEntityManager().createQuery(alunosSelect);
return query.getResultList();