【问题标题】:JPA EclipseLink - Get multiple objects by primary key maintaining orderJPA EclipseLink - 通过主键维护顺序获取多个对象
【发布时间】:2016-09-15 08:54:25
【问题描述】:

我使用 EclipseLink 作为 JPA 实现,我需要使用主键(数字 id)获取多个对象。但我还需要维护给定的 id 顺序。

使用原生mySQL可以使用ORDER BY FIELD获得这种行为

SELECT id FROM table WHERE id IN(9,5,2,6) ORDER BY FIELD(id,9,5,2,6);

我现在正在尝试使用 JPA 实现来复制此查询。正如this thread 已经建立的那样,不支持ORDER BY FIELD,所以我使用JPA native query 采用了更底层的方法。

我尝试使用参数查询而不是使用原始语句来达到这个目标。第一个实现是这样的

Class<?> clazz = ...;
List<Long> ids = ...;

EntityManagerFactory emf = ...;
EntityManager em = emf.createEntityManager();
String statement = "SELECT * FROM table WHERE id IN (?)";

Query createNativeQuery = em.createNativeQuery(statement, clazz);
createNativeQuery.setParameter(1, ids);
List resultList = createNativeQuery.getResultList();

如您所见,ORDER 子句尚不存在,第一步我只是尝试使用ids 列表和IN 运算符使参数查询工作。在setParameter 方法中,我尝试提供List 对象,一个逗号分隔的列表(作为字符串),但它们都不起作用。最后,它们都以 sql 语法错误结束。 我也尝试使用括号,有或没有,但没有任何效果。

这是我做的一些测试

String statement = "SELECT * FROM " + tableName + " WHERE id IN (?)";
Query createNativeQuery = emJpa.createNativeQuery(statement, this.em.getClassObject());
createNativeQuery.setParameter(1, ids);

查询没有给出任何错误,但没有给出结果。

String statement = "SELECT * FROM " + tableName + " WHERE id IN (?)";
Query createNativeQuery = emJpa.createNativeQuery(statement, this.em.getClassObject());
createNativeQuery.setParameter(1, Joiner.on(",").join(ids));

只给出了一个结果,但是为查询提供了7个id

来自this topic 我也尝试使用?1 而不是?,但没有任何变化。有没有办法让nativeQuery 使用 ID 列表?

目前我使用的是完整的原始 SQL 语句

String joinedId = Joiner.on(",").join(ids);
String statement = "SELECT * FROM " + tableName + " WHERE id IN (" + joinedId + ") ORDER BY FIELD(id," + joinedId + ")";
Query createNativeQuery = emJpa.createNativeQuery(statement, this.em.getClassObject());
createNativeQuery.getResultList();

但一开始我是从参数查​​询开始,用于优化和性能相关的每次语句的解析。


编辑

在 Chris 的建议下,我尝试了使用 FUNCTION 运算符的 TypedQuery(因为我使用的是最新的 EclipseLink)。这是生成的代码

List<Long> ids = ...;
Class<?> clazz = ...;
String statement = "SELECT e FROM " + clazz.getSimpleName() + " e WHERE e.id IN (:idList) ORDER BY FUNCTION('FIELD', e.id, :idList)";

EntityManagerFactory emf = ...;
EntityManager em = emf.createEntityManager();

TypedQuery<?> query = em.createQuery(statement, clazz);
query.setParameter("idList", ids);

List resultList = query.getResultList();

这是执行此代码时的错误

Local Exception Stack: 
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.3.v20160428-59c81c5): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Operand should contain 1 column(s)
Error Code: 1241
Call: SELECT ... all the fields ... FROM webcontent_type WHERE (ID IN ((?,?,?,?,?,?,?))) ORDER BY FIELD(ID, (?,?,?,?,?,?,?))
    bind => [14 parameters bound]
Query: ReadAllQuery(referenceClass=WebContentType sql="SELECT ... all the fields ... FROM webcontent_type WHERE (ID IN (?)) ORDER BY FIELD(ID, ?)")

编辑 2

试过不带括号还是有错误

SELECT e FROM FrameWorkUser e WHERE e.id IN :idList ORDER BY FUNCTION('FIELD', e.id, :idList)

我必须说,对于一个元素的列表,代码可以工作,但对于另一个 10 个元素的列表,则会出现错误

javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.3.v20160428-59c81c5): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Operand should contain 1 column(s)
Error Code: 1241
Call: SELECT .... FROM webcontent_type WHERE (ID IN (?,?,?,?,?,?,?)) ORDER BY FIELD(ID, (?,?,?,?,?,?,?))
    bind => [14 parameters bound]
Query: ReadAllQuery(referenceClass=WebContentType sql="SELECT .... FROM webcontent_type WHERE (ID IN ?) ORDER BY FIELD(ID, ?)")
    at org.eclipse.persistence.internal.jpa.QueryImpl.getDetailedException(QueryImpl.java:382)
    at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:260)
    at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:473)

似乎即使没有括号,结果语句也有它们

【问题讨论】:

标签: java mysql sql jpa eclipselink


【解决方案1】:

如果您要使用本机查询,则必须完全按照为数据库创建 SQL 的方式进行操作 - 这意味着您必须将列表分解为其组件参数,因为 JPA 提供程序不会更改 SQL你。大多数提供者处理 JPQL 中的列表,因此“从实体 e 中选择 e,其中 e.id in (:idList)”将在 EclipseLink 中工作。

您缺少的一点是“FIELD”不是 JPQL 构造。为此,您必须使用 JPQL 2.1 FUNCTION 运算符。比如:

"Select e from Entity e where e.id in :idList order by FUNCTION('FIELD', e.id, :idList)" 

【讨论】:

  • 我尝试了您建议的代码(我不知道FUNCTION 子句)。您可以在主要问题中阅读结果,我在底部添加了新部分
  • 我的错误,我已经稍微纠正了答案:不要在您的 JPQL 中使用 ()。它应该只是“在:idList 中”。不过,您可能仍然需要分解 FUNCTION 调用的列表,或者稍微查看 EclipseLink 的调用以解决它所存在的问题 - 我不再使用源代码,所以我建议查看错误看看是否有一个可能有解决方法。
  • 我删除了括号,但出现了同样的错误(请参阅主要问题的 EDIT2)。正如您所建议的, FUNCTION 调用中的列表不起作用。您是否发现与此问题相关的任何错误?我试图找到一些东西,但我没有得到任何结果
  • 您的第二次编辑显示它适用于“in”子句,而 FUNCTION 子句的问题是它缺少右括号。您可以在 EclipseLink 源代码中解决此问题,但我无法帮助您,抱歉。
  • 抱歉,缺少右括号是复制粘贴错误。未绑定的查询显示正确的括号,但错误仍然相同。我现在用正确的日志更新了 EDIT2 部分
猜你喜欢
  • 2015-04-25
  • 1970-01-01
  • 2013-10-20
  • 1970-01-01
  • 1970-01-01
  • 2016-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多