使用Hibernate进行“IN”查询

47

我有一个ID字符串列表,想要使用Hibernate获取这些ID对应的行。 TrackedItem是一个Hibernate/JPA实体(如果我的命名混淆了,请见谅)。

我的代码如下:

String idsText = "380, 382, 386";
ArrayList<Long> ids = new ArrayList<Long>();

for (String i : idsText.split(","))
{
    ids.add(Long.getLong(i));
}

List<TrackedItem> items = TrackedItem.find("id IN (?)", ids).fetch();

但是这个方法失败了:JPAQueryException发生:在models.TrackedItem上执行查询时出错,其中id IN (?):java.util.ArrayList无法强制转换为java.lang.Long

我该如何让IN部分工作?谢谢。


Long.getLong(i)并不是你想象中的那样。请使用Long.parseLong(i)代替。参见https://dev59.com/R2s05IYBdhLWcg3wNPS7 - Natix
3个回答

80

你的 JPQL 查询语法不正确。要么使用(具有位置参数):

List<Long> ids = Arrays.asList(380L, 382L, 386L);
Query query = em.createQuery("FROM TrackedItem item WHERE item.id IN (?1)");
query.setParameterList(1, ids)
List<TrackedItem> items = query.getResultList();
或者(使用命名参数):
List<Long> ids = Arrays.asList(380L, 382L, 386L);
Query query = em.createQuery("FROM TrackedItem item WHERE item.id IN :ids");
query.setParameterList("ids", ids)
List<TrackedItem> items = query.getResultList();

以下是关于参数的JPA 1.0规范中相关的部分:

4.6.4.1 位置参数

以下规则适用于位置参数。

  • 输入参数由问号(?)前缀跟着一个整数来表示,例如:?1
  • 输入参数从1开始编号。
    请注意,在查询字符串中相同的参数可能会被多次使用,并且参数在查询字符串内的使用顺序不一定要符合位置参数的顺序。

4.6.4.2 命名参数

命名参数是以“:”符号为前缀的标识符。它遵循第4.4.1节定义的标识符规则。命名参数区分大小写。

示例:

SELECT c
FROM Customer c
WHERE c.status = :stat

第3.6.1节描述了命名查询参数的绑定API。


2
ids列表应该设置为query.setParameterList("ids", idsList);而不是query.setParameter("ids", ids)。 - Buminda
如何将String[]数组1设置为查询参数。 - priyadarshini
在我的情况下,我不得不使用setParameterList函数和类型参数才能使其工作:.setParameterList("ids", ids, BigIntegerType.INSTANCE) - Walid

12

如果你不幸使用旧的非JPA Hibernate,那么以下方法适用于你:

Query query = session.createQuery("FROM TrackedItem item WHERE item.id IN (:items)");
query.setParameterList("items", Arrays.asList(380L, 382L, 386L));

@SuppressWarnings("unchecked")
List<TrackedItem> results = query.list();

倒霉?年龄大了?o_O 相比于JPA,裸露的Hibernate具有更多的功能(例如,请参见Aleksandar Radulović的答案)。此外,你的回答似乎是已接受答案的重复。 - Stanislav Bashkyrtsev
当我写这篇文章的时候,裸露的Hibernate需要使用XML映射而不是注释。此外,被接受的答案已经被编辑以匹配我的答案所说的内容。多年来很多事情都可能发生变化。 - depsypher
裸的Hibernate在你的回答之前就有注释了 :) 但这并不与答案相矛盾(只是与你最后的评论相矛盾)。我只是不明白为什么XML映射被认为是“不幸的方式”,因为它更灵活、更清晰。顺便说一下,接受的答案自第一个版本以来并没有改变太多——方法的名称是错误的,但这值得一条评论。 - Stanislav Bashkyrtsev
我的主要原因添加这个答案是为了更正错误的方法名称,但即使在采纳的答案中已经修复,我仍然认为用Hibernate API获取此列表仍然有用。或许有点微不足道,但query.getResultList()与query.list()之间的区别仍然很有帮助。如果自2015年以来发生了变化,我会删除我的答案。 - depsypher

1
即使您的查询正确执行,如果查询参数包含太多值,则可能会遇到错误。
如果您使用的是Hibernate 5.1或更新版本,则解决此问题的一种可能方法是使用Session.byMultipleIds()。
session
    .byMultipleIds(TrackedItem.class)
    .multiLoad(1L, 2L, 3L);

了解更多信息,请参见https://thoughts-on-java.org/fetch-multiple-entities-id-hibernate/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接