【发布时间】: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)
似乎即使没有括号,结果语句也有它们
【问题讨论】:
-
JPA 原生查询不支持集合,请参阅此处stackoverflow.com/questions/21484176/…
-
有没有办法在将它作为参数传递之前将它作为字符串加入列表?
标签: java mysql sql jpa eclipselink