【问题标题】:JPA passing list to IN clause in named native queryJPA将列表传递给命名本机查询中的IN子句
【发布时间】:2011-09-10 19:00:24
【问题描述】:

我知道我可以将列表传递给 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

【问题讨论】:

  • 您是否尝试过使用数据库客户端执行完全相同的查询?
  • 你不应该使用括号 (?userIdList) 只需删除括号就可以了

标签: jpa arraylist in-clause nativequery


【解决方案1】:

你可以传入一个列表作为参数,但是:

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

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

【讨论】:

    【解决方案2】:

    以上接受的答案不正确,导致我偏离轨道好几天!!

    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. Reference 1
    2. Reference 2 which mentioned which cases paranthesis works which giving the list as a parameter

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

    【讨论】:

    • 这些是 JPA/JPQL 查询,而不是本机 SQL 查询,现在是。问题是关于本机查询,接受的答案甚至指出了这种差异,还是我在这里误读了什么?
    • “列表不是本机 SQL 查询的有效参数” - 这是上一个答案的特定部分,由于本机查询中允许使用列表,因此这是不正确的。我作为示例给出的查询是本机查询(这是我在代码中使用的一小部分)。它有效。
    • 你的例子说jpql,它显示了一个没有SELECT ...的查询,这使得它在我知道的至少一些数据库中无效,你使用createQuery而不是createNativeQuery。也许我们在这里谈论的不是同一件事。本机 == SQL。你的答案 == JPQL != SQL。
    • 我试图传达一个广泛的观点,即使用本机查询是可能的。我现在修改了答案,因为其中一些部分可能会让您感到困惑。希望对您有所帮助 - 感谢您的反馈。
    • 对我有用,但前提是列表至少有 1 个条目。
    【解决方案3】:

    你应该这样做:

    String userIds ="1,2,3,4,5"; List&lt;String&gt; 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)")

    【讨论】:

      【解决方案4】:

      在 jpa 中,它对我有用

      @Query(nativeQuery =true,value = "SELECT * FROM Employee as e WHERE e.employeeName IN (:names)")  
      List<Employee> findByEmployeeName(@Param("names") List<String> names);
      

      【讨论】:

        【解决方案5】:

        可以这么简单:

        @Query(nativeQuery =true,value = "SELECT * FROM Employee as e WHERE e.employeeName IN (:names)")  
         List<Employee> findByEmployeeName(@Param("names") List<String> names);
        

        【讨论】:

          【解决方案6】:

          使用标准 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();
          

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

          【讨论】:

            【解决方案7】:

            在我的情况下(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: ERROR: operator does not exist: numeric = numeric[]

            'IN(?)' 必须换成 '= ANY(?)'

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

            【讨论】:

              【解决方案8】:

              目前我在 Hibernate 中使用 JPA 2.1

              我还将 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);
              

              【讨论】:

                【解决方案9】:

                使用休眠、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);
                

                【讨论】:

                  【解决方案10】:

                  根据您的数据库/提供程序/驱动程序/等,实际上,您可以将列表作为绑定参数传递给 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/

                  【讨论】:

                    【解决方案11】:

                    你可以试试这个 :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"
                    )
                    

                    【讨论】:

                      【解决方案12】:

                      在 JPA2 中使用 Hibernate 作为提供程序进行了尝试,似乎 hibernate 确实支持获取“IN”列表并且它可以工作。 (至少对于命名查询,我相信它与命名原生查询类似) 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 (?, ?, ?)
                      

                      【讨论】:

                        【解决方案13】:

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

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

                        JPQL 支持,但 SQL 不支持,因此您可以使用 JPQL 代替本机查询。

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

                        【讨论】:

                        • 注意:此答案不正确。请参阅下面的答案以及参考资料。
                        猜你喜欢
                        • 2021-11-18
                        • 2014-07-13
                        • 2020-04-23
                        • 2011-05-21
                        • 2021-11-11
                        • 1970-01-01
                        • 2012-09-21
                        • 1970-01-01
                        相关资源
                        最近更新 更多