您的浏览器过于古老 & 陈旧。为了更好的访问体验, 请 升级你的浏览器
Coder 发布于2020年10月14日 20:49 最近更新于 2020年10月15日 22:24

如何使用 Hibernate JPA CriteriaBuilder 实现 IN 查询子句?

62 次浏览 读完需要≈ 5 分钟 HibernateJava

我准备使用 JPA 2.0 的 CriteriaBuilder API 实现如下SQL查询语句:

SELECT * FROM tbl_emp WHERE dept_id = 1 AND group_id IN (1, 3, 5)

我的Java代码大致如下:

public void queryDemo(EntityManager em) {
	Integer[] groupIds = { 1, 3, 5 };

	CriteriaBuilder cb = em.getCriteriaBuilder();
	CriteriaQuery<Emp> query = cb.createQuery(Emp.class);
	Root<Emp> root = query.from(Emp.class);

	query.where(
			// dept.id = 1
			cb.equal(root.get("dept").get("id"), 1),
			// AND groupId IN (1, 3, 5)
			cb.in(root.get("groupId").in(groupIds))
	);

	em.createQuery(query)
			.getResultList()
			.forEach(emp -> System.out.println(emp.getName()));
}

不幸的是,执行上述代码,结果却抛出如下异常:

Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: in near line 1, column 154 [select generatedAlias0 from org.lesson001.www.bean.Emp as generatedAlias0 where ( generatedAlias0.dept.id=1 ) and ( generatedAlias0.groupId in (1, 3, 5) in (null) )]
	at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:74)
	at org.hibernate.hql.internal.ast.ErrorTracker.throwQueryException(ErrorTracker.java:93)
	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:301)
	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:189)
	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144)
	at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113)
	at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73)
	at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:162)
	at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:604)
	at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:716)

请问,在Hibernate中,如何使用 JPA CriteriaBuilder 实现 IN 查询呢?

对了,我使用的是当前最新的 Hibernate 5.4.22.Final。

1 个回答

Ready · 9天前

将你代码中的cb.in(root.get("groupId").in(groupIds))改为root.get("groupId").in(groupIds)即可(意即去掉CriteriaBuilder.in()方法的调用)。

在 Hibernate中,要使用 JPA CriteriaBuilder API 构造SQL的一个 IN 约束条件子句,就是要构造一个绑定了集合参数的org.hibernate.query.criteria.internal.predicate.InPredicate对象实例。

以你的上述代码为例,我们可以使用如下3种方式之一达到该目的:

1、直接使用javax.persistence.criteria.Expression.in(Object...) 方法获得InPredicate对象实例

// 该行代码返回的即是 InPredicate 对象
Predicate groupIdInClause = root.get("groupId").in(groupIds);

query.where(
		// dept.id = 1
		cb.equal(root.get("dept").get("id"), 1),
		// AND groupId IN (1, 3, 5)
		groupIdInClause
);

2、(多次)调用javax.persistence.criteria.CriteriaBuilder.In.value(Object)绑定一或多个值

In<Integer> groupIdInClause = cb.in(root.get("groupId"));
// 通过循环调用 In.value() 方法绑定集合中的多个值
for (Integer id : groupIds) {			
	groupIdInClause.value(id);
}

query.where(
		// dept.id = 1
		cb.equal(root.get("dept").get("id"), 1),
		// AND groupId IN (1, 3, 5)
		groupIdInClause
);

3、调用javax.persistence.criteria.CriteriaBuilder.In.value(javax.persistence.criteria.Expression)直接绑定整个集合

In<List<Integer>> groupIdInClause = cb.in(root.get("groupId"));
// 由于是字面参数,因此需要使用 CriteriaBuilder.literal() 转为 Expression 对象
// 由于 CriteriaBuilder.literal() 不支持数组值,因此需要使用 Arrays.asList() 转为 List 集合
groupIdInClause.value(cb.literal(Arrays.asList(groupIds)));

query.where(
		// dept.id = 1
		cb.equal(root.get("dept").get("id"), 1),
		// AND groupId IN (1, 3, 5)
		groupIdInClause
);

以上三种方法,任意其一即可。

你的代码中出现了混用,当然就会报错。

Ready [答主] · 9天前

注意:推荐使用第三种方式,因为只有第三种方式生成的SQL语句是基于占位符的,前面两种都是基于字面值。 以上述代码为例,前面两者生成的SQL语句是:group_id in (1, 3, 5),第三种生成的SQL语句是:group_id in (?, ?, ?)

撰写答案