如何编写与集合完全匹配的 JPA 条件查询?

Posted

技术标签:

【中文标题】如何编写与集合完全匹配的 JPA 条件查询?【英文标题】:How do I write a JPA criteria query that matches a collection exactly? 【发布时间】:2014-06-06 21:14:30 【问题描述】:

我正在使用 JPA 2.0 和 Hibernate 4.1.0.Final。我有几个类,Groups 和 GroupMembers。每个 GroupMember 都绑定到一个用户对象

@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;

是否可以编写一个 JPA 条件查询,给定一个 java.util.Set 的用户对象,将返回其成员与该 Set 完全匹配的组?我尝试了以下...

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

但它失败了,但有例外……

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)

【问题讨论】:

【参考方案1】:

每个用户都需要不同的根,因为每个用户都与其他用户不同(未经测试):

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

【讨论】:

不幸的是,这会导致“javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: No value specified for parameter 2”异常。产生的查询是“选择 group0_.ID 作为 ID68_,group0_.CLAs-s-rOOM_ID 作为 CLAs-s-rOOM3_68_,group0_.NAME 作为 NAME68_,group0_.CREATOR 作为 CREATOR68_ from sb_msg_group group0_ cross join sb_msg_group_member members1_ where group0_.ID=members1_.GROUP_ID and .=? limit ? " 嗯,参数 2 是limit ? 位,您是否尝试在查询中添加query.setMaxResults(...)?更重要的是,什么是 GroupMember(我假设它是 JPA 实体),为什么会产生 and .=? 位? 我不知道“.=”是什么意思是,我剪切并粘贴了您的代码,这就是它产生的。是的,GroupMember 是一个 JPA 实体,我在问题中定义了它,但如果我需要添加更多信息,请告诉我,我可以处理。 为了纠正编译错误,我更改了“final Root group = criteria.from(Group.class);”到“最终 Root group = criteria.from(Group.class);”和“最终谓词 p = builder.equal(groupRoot.get(GroupMember_.user), user);”到“最终谓词 p = builder.equal(memberRoot.get(GroupMember_.user), user);”但出现异常,“您的 SQL 语法有错误;请查看与您的 MySQL 服务器版本相对应的手册,以了解在 ')) 和 groupmembe2_.USER_ID='userid' and (groupmembe2_.ID in ( .))' 在第 1 行"。 你能在这里发布完整的 SQL 吗?【参考方案2】:

如何在 SQL 中创建一个过程并使用 JPA 调用它?我创建了以下调用我创建的登录过程。

    @Query(nativeQuery = true,value = "call Login_User(:username,:pass)")  // call stored procedure 
    int loginUser(@Param("username")String username,@Param("pass")String pass);

我创建的 SQL 过程如下:

DELIMITER //
CREATE PROCEDURE Login_User(IN username CHAR(12), IN pass text(255))
BEGIN
    DECLARE password text(18);
    SELECT User_Password INTO password  FROM user WHERE User_ID = username;
    select if(STRCMP(pass,password)= 0,1,0) as str_compare;
END//
DELIMITER ;

希望这会有所帮助。干杯! 我愿意了解更多关于相同的信息。我知道我的更像是一种解决方法而不是解决方案:)

【讨论】:

以上是关于如何编写与集合完全匹配的 JPA 条件查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何编写没有联接的 JPA 2.1 更新条件查询?

JPA分页查询与条件分页查询

Spring data jpa @OneToMany 在一的一端进行查询()对集合属性设置条件查询)

JPA分页查询与条件分页查询

spring-data-jpa动态条件查询

MongoDB:查询以匹配数组中的每个元素与条件