50

我知道我可以将列表传递给 JPA 中的命名查询,但是 NamedNativeQuery 怎么样?我尝试了很多方法,但仍然不能将列表传递给 NamedNativeQuery。任何人都知道如何将列表传递给 NamedNativeQuery 中的 in 子句?非常感谢!

NamedNativeQuery 如下:

@NamedNativeQuery(
   name="User.findByUserIdList", 
   query="select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
         "where u.user_id in (?userIdList)"
)

它被称为:

List<Object[]> userList = em.createNamedQuery("User.findByUserIdList").setParameter("userIdList", list).getResultList();

然而结果并不如我所料。

System.out.println(userList.size());  //output 1

Object[] user = userList.get(0);
System.out.println(user.length);   //expected 5 but result is 3
System.out.println(user[0]);       //output MDAVERSION which is not a user_id
System.out.println(user[1]);       //output 5
System.out.println(user[2]);       //output 7
4

13 回答 13

41

上面接受的答案不正确,导致我偏离轨道很多天!

JPA 和 Hibernate 都使用 Query 在本机查询中接受集合。

你只需要做

String nativeQuery = "Select * from A where name in :names"; //use (:names) for older versions of hibernate
Query q = em.createNativeQuery(nativeQuery);
q.setParameter("names", l);

另请参阅此处建议相同的答案(我从其中一个中选择了上面的示例)

  1. 参考1
  2. 参考文献 2 提到了哪些情况下括号有效,将列表作为参数

*请注意,这些参考资料是关于 jpql 查询的,但集合的使用也适用于本机查询。

于 2018-06-11T08:56:16.447 回答
23

列表不是原生 SQL 查询的有效参数,因为它不能在 JDBC 中绑定。您需要为列表中的每个参数都有一个参数。

u.user_id 在哪里 (?id1, ?id2)

这通过 JPQL 支持,但不支持 SQL,因此您可以使用 JPQL 而不是本机查询。

一些 JPA 提供程序可能支持这一点,因此您可能希望向您的提供程序记录错误。

于 2011-06-08T13:02:35.413 回答
9

实际上,根据您的数据库/提供程序/驱动程序/等,您可以将列表作为绑定参数传递给 JPA 本机查询。

例如,使用 Postgres 和 EclipseLink,以下工作(返回 true),演示多维数组以及如何获得双精度数组。(SELECT pg_type.* FROM pg_catalog.pg_type为其他类型做;可能带有 的那些_,但在使用前将其剥离。)

Array test = entityManager.unwrap(Connection.class).createArrayOf("float8", new Double[][] { { 1.0, 2.5 }, { 4.1, 5.0 } });
Object result = entityManager.createNativeQuery("SELECT ARRAY[[CAST(1.0 as double precision), 2.5],[4.1, 5.0]] = ?").setParameter(1, test).getSingleResult();

演员表在那里,所以文字数组是双精度数而不是数字。

更重要的是-我不知道您如何或是否可以进行命名查询;我认为这取决于,也许。但我认为以下将适用于 Array 的东西。

Array list = entityManager.unwrap(Connection.class).createArrayOf("int8", arrayOfUserIds);
List<Object[]> userList = entityManager.createNativeQuery("select u.* from user u "+
     "where u.user_id = ANY(?)")
     .setParameter(1, list)
     .getResultList();

我没有与 OP 相同的架构,所以我没有完全检查过,但我认为它应该可以工作——至少在 Postgres 和 EclipseLink 上。

此外,密钥位于:http ://tonaconsulting.com/postgres-and-multi-dimensions-arrays-in-jdbc/

于 2018-03-08T21:34:28.487 回答
7

使用休眠、JPA 2.1 和 deltaspike 数据,我可以在包含 IN 子句的查询中将列表作为参数传递。我的查询如下。

@Query(value = "SELECT DISTINCT r.* FROM EVENT AS r JOIN EVENT AS t on r.COR_UUID = t.COR_UUID where " +
        "r.eventType='Creation' and t.eventType = 'Reception' and r.EVENT_UUID in ?1", isNative = true)
public List<EventT> findDeliveredCreatedEvents(List<String> eventIds);
于 2018-06-12T20:17:38.843 回答
2

在我的情况下(EclipseLink,PostGreSQL)这有效:

    ServerSession serverSession = this.entityManager.unwrap(ServerSession.class);
    Accessor accessor = serverSession.getAccessor();
    accessor.reestablishConnection(serverSession);
    BigDecimal result;
    try {
        Array jiraIssues = accessor.getConnection().createArrayOf("numeric", mandayWorkLogQueryModel.getJiraIssues().toArray());
        Query nativeQuery = this.entityManager.createNativeQuery(projectMandayWorkLogQueryProvider.provide(mandayWorkLogQueryModel));
        nativeQuery.setParameter(1,mandayWorkLogQueryModel.getPsymbol());
        nativeQuery.setParameter(2,jiraIssues);
        nativeQuery.setParameter(3,mandayWorkLogQueryModel.getFrom());
        nativeQuery.setParameter(4,mandayWorkLogQueryModel.getTo());
        result = (BigDecimal) nativeQuery.getSingleResult();
    } catch (Exception e) {
        throw new DataAccessException(e);
    }

    return result;

同样在查询中不能使用 IN(?) 因为你会得到如下错误:

引起:org.postgresql.util.PSQLException:错误:运算符不存在:数字=数字[]

'IN(?)' 必须交换为 '= ANY(?)'

我的解决方案是基于 Erhannis 的概念。

于 2019-07-27T08:26:57.910 回答
2

目前我将 JPA 2.1 与 Hibernate 一起使用

我还将 IN 条件与本机查询一起使用。我的查询示例

SELECT ... WHERE table_name.id IN (?1)

我注意到由于James描述的限制,不可能传递像 "id_1, id_2, id_3" 这样的字符串

但是当您使用 jpa 2.1 + hibernate 时,可以传递字符串值列表。对于我的情况,下一个代码是有效的:

    List<String> idList = new ArrayList<>();
    idList.add("344710");
    idList.add("574477");
    idList.add("508290");

    query.setParameter(1, idList);
于 2018-06-29T14:17:09.407 回答
1

在 JPA2 中使用 Hibernate 作为提供者进行了尝试,似乎 hibernate 确实支持获取“IN”列表并且它可以工作。(至少对于命名查询,我相信它与命名的 NATIVE 查询类似)hibernate 在内部所做的是生成动态参数,在 IN 内部与传入列表中的元素数相同。

所以在你上面的例子中

List<Object[]> userList = em.createNamedQuery("User.findByUserIdList").setParameter("userIdList", list).getResultList();

如果列表有 2 个元素,则查询将如下所示

select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
         "where u.user_id in (?, ?)

如果它有 3 个元素,它看起来像

select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
         "where u.user_id in (?, ?, ?)
于 2013-03-04T22:20:47.013 回答
1

在 jpa 中,它对我有用

@Query(nativeQuery =true,value = "SELECT * FROM Employee as e WHERE e.employeeName IN (:names)")  
List<Employee> findByEmployeeName(@Param("names") List<String> names);
于 2021-01-14T02:02:02.903 回答
1

你应该做这个:

String userIds ="1,2,3,4,5"; List<String> userIdList= Stream.of(userIds.split(",")).collect(Collectors.toList());

然后,在查询中传递 like 参数,如下所示:

@NamedNativeQuery(name="User.findByUserIdList", query="select u.user_id, u.dob, u.name, u.sex, u.address from user u where u.user_id in (?userIdList)")

于 2021-02-03T23:35:55.243 回答
0

使用标准 JPA 是不可能的。Hibernate 提供了专有方法setParameterList(),但它仅适用于 Hibernate 会话,在 JPA 中不可用EntityManager

我为 Hibernate 提出了以下解决方法,它并不理想,但几乎是标准的 JPA 代码,并且具有一些不错的属性。

对于初学者,您可以将命名的本机查询很好地分隔在一个orm.xml文件中:

<named-native-query name="Item.FIND_BY_COLORS" result-class="com.example.Item">
    <query>
        SELECT i.*
        FROM item i
        WHERE i.color IN ('blue',':colors')
        AND i.shape = :shape
    </query>
</named-native-query>

占位符用单引号括起来,因此它是一个有效的本机 JPA 查询。它在不设置参数列表的情况下运行,并且当围绕它设置其他匹配的颜色参数时仍然会返回正确的结果。

在您的 DAO 或存储库类中设置参数列表:

@SuppressWarnings("unchecked")
public List<Item> findByColors(List<String> colors) {
    String sql = getQueryString(Item.FIND_BY_COLORS, Item.class);
    sql = setParameterList(sql, "colors", colors);

    return entityManager
            .createNativeQuery(sql, Item.class)
            .setParameter("shape", 'BOX')
            .getResultList();
}

无需手动构建查询字符串。您可以像往常一样设置任何其他参数。

辅助方法:

String setParameterList(String sql, String name, Collection<String> values) {
    return sql.replaceFirst(":" + name, String.join("','", values));
}

String getQueryString(String queryName, Class<?> resultClass) {
    return entityManager
            .createNamedQuery(queryName, resultClass)
            .unwrap(org.hibernate.query.Query.class) // Provider specific
            .getQueryString();
}

所以基本上我们从 读取查询字符串orm.xml,手动设置参数列表,然后创建本机 JPA 查询。不幸的是,createNativeQuery().getResultList()即使我们将结果类传递给它,它也会返回一个无类型查询和无类型列表。因此@SuppressWarnings("unchecked").

缺点:对于除 Hibernate 之外的 JPA 提供者来说,在不执行查询的情况下展开查询可能更复杂或更不可能。例如,以下可能适用于 EclipseLink(未经测试,取自Can I get the SQL string from a JPA query object?):

Session session = em.unwrap(JpaEntityManager.class).getActiveSession();
DatabaseQuery databaseQuery =     query.unwrap(EJBQueryImpl.class).getDatabaseQuery();
databaseQuery.prepareCall(session, new DatabaseRecord());
Record r = databaseQuery.getTranslationRow();
String bound = databaseQuery.getTranslatedSQLString(session, r);
String sqlString = databaseQuery.getSQLString();

另一种方法可能是将查询存储在文本文件中并添加代码以从那里读取它。

于 2020-02-08T21:20:24.033 回答
0

可以很简单:

@Query(nativeQuery =true,value = "SELECT * FROM Employee as e WHERE e.employeeName IN (:names)")  
 List<Employee> findByEmployeeName(@Param("names") List<String> names);
于 2020-07-14T15:16:08.003 回答
0

您可以将列表作为参数传递,但是:

  • 如果您创建 a@NamedNativeQuery并使用.createNamedQuery(),则不使用命名参数,而是使用?1(位置参数)。它从 1 开始,而不是 0。

  • 如果你使用.createNativeQuery(String),你可以使用命名参数。

于 2021-09-17T11:03:38.203 回答
-5

你可以试试这个:userIdList而不是(?userIdList)

@NamedNativeQuery(
       name="User.findByUserIdList", 
       query="select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
             "where u.user_id in :userIdList"
)
于 2017-07-18T09:53:24.080 回答