Search code examples
hibernatejpajpa-2.0criteriaquery

How do I write a JPA criteria query that matches a collection exactly?


I’m using JPA 2.0 with Hibernate 4.1.0.Final. I have a couple of classes, Groups and GroupMembers. Each GroupMember is tied to a user object

@Entity
@Table(name = "group")
public class Group    
{
    @Id
    @NotNull
    @GeneratedValue(generator = "uuid-strategy")
    @Column(name = "ID")
    private String id;
    …


    @OneToMany(mappedBy = "group")
    private Set<GroupMember> members;



@Entity
@Table(name = "sb_msg_group_member")
public class GroupMember
{
    …

    @ManyToOne
    @JoinColumn(name = "USER_ID", nullable = false, updatable = true)
    private User user;

Is it possible to write a JPA criteria query that given a java.util.Set of User objects, would return the groups whose members match exactly that Set? I tried the below …

    final CriteriaBuilder builder = m_entityManager.getCriteriaBuilder();
    CriteriaQuery<Group> criteria = builder.createQuery(Group.class);
    final Root<Group> groupRoot = criteria.from(Group.class);
    final Set<GroupMember> groupMembers = new HashSet<GroupMember>();
    for (final User user : users)
    {
        final GroupMember groupMember = new GroupMember();
        groupMember.setUser(user);
        groupMembers.add(groupMember);
    }   // for
    criteria.where(builder.equal(groupRoot.get(Group_.members), groupMembers));
    final TypedQuery<Group> results = m_entityManager.createQuery(criteria);

but it fails with the exception …

javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1074)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:988)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:974)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:919)
    at com.mysql.jdbc.PreparedStatement.checkAllParametersSet(PreparedStatement.java:2611)
    at com.mysql.jdbc.PreparedStatement.fillSendPacket(PreparedStatement.java:2586)
    at com.mysql.jdbc.PreparedStatement.fillSendPacket(PreparedStatement.java:2510)
    at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2259)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:56)
    at org.hibernate.loader.Loader.getResultSet(Loader.java:2040)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1837)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1816)
    at org.hibernate.loader.Loader.doQuery(Loader.java:900)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:342)
    at org.hibernate.loader.Loader.doList(Loader.java:2526)
    at org.hibernate.loader.Loader.doList(Loader.java:2512)
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2342)
    at org.hibernate.loader.Loader.list(Loader.java:2337)
    at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:495)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:357)
    at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:195)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1275)
    at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)
    at org.hibernate.ejb.QueryImpl.getSingleResult(QueryImpl.java:287)
    at org.hibernate.ejb.criteria.CriteriaQueryCompiler$3.getSingleResult(CriteriaQueryCompiler.java:258)
    at org.mainco.subco.messaging.repo.MessageDaoImpl.findGroupByMembers(MessageDaoImpl.java:144)
    at org.mainco.subco.messaging.repo.MessageDaoIT.testFindGroupByMembers(MessageDaoIT.java:83)

Solution

  • You will need different roots for each and every user, as each of them is different from the others (untested):

    final CriteriaBuilder builder = m_entityManager.getCriteriaBuilder();
    CriteriaQuery<Group> criteria = builder.createQuery(Group.class);
    final List<Predicate> predicates = new ArrayList<Predicate>();
    final Root<Group> group = criteria.from(Group.class);
    for (final User user : users)
    {
        final Root<GroupMember> memberRoot = group.join(Group_.members);
        final Predicate p = builder.equal(memberRoot.get(GroupMember_.user), user);
        predicates.add(p);
    }   // for
    predicates.add(builder.equals(builder.count(group.get(Group_.members)),users.size()))
    criteria.where(builder.and(predicates.toArray(new Predicate[predicates.size()])));
    final TypedQuery<Group> results = m_entityManager.createQuery(criteria);