【发布时间】:2015-02-24 16:14:06
【问题描述】:
我正在使用 Java EE 上下文中的 MySQL 数据库和 JPA 开发 JSF-Web 应用程序。
我正在使用以下 JPQL 从数据库中获取一些数据。 EntityGenerierteAufgabe 具有字段 createTime 和 changeTime。 CreateTime 在写入数据库条目时设置。在这种情况下,changeTime 为 NULL。当数据库条目更新时,changeTime 也将被设置。现在我想从这个表中选择并在 changeTime 下排序条目,如果设置,否则按 createTime。并且我想限制结果的数量。
String query = "SELECT ga FROM EntityGenerierteAufgabe ga "
+ "WHERE ga.benutzer=:benutzer "
+ "ORDER BY CASE WHEN (ga.changeTime > ga.createTime) THEN ga.changeTime ELSE ga.createTime END DESC";
aufgaben = em.createQuery(query)
.setParameter("benutzer", benutzer)
.setMaxResults(6)
.getResultList();
此 JPQL 对一个用户 (BrowserSession) 运行良好。如果我使用 jMeter 进行压力测试并使用多个线程(多个 BrowserSession)并调用调用此 JPQL 的 JSF 页面,则多次导致有时出现以下错误:
Call: SELECT ID AS a1, BEARBEITUNGSZEIT AS a2, CHANGETIME AS a3, CREATETIME AS a4, FORTSCHRITT AS a5, ISTBEENDET AS a6, AUFGABENTYP_ID AS a7, LEVEL_ID AS a8, BENUTZER_ID AS a9 FROM ENTITYGENERIERTEAUFGABE WHERE (BENUTZER_ID = ?) ORDER BY CASE WHEN (CHANGETIME > CREATETIME) THEN CHANGETIME WHEN CREATETIME THEN DESC LIMIT ?, ?
bind => [3 parameters bound]
Query: ReadAllQuery(referenceClass=EntityGenerierteAufgabe sql="SELECT ID AS a1, BEARBEITUNGSZEIT AS a2, CHANGETIME AS a3, CREATETIME AS a4, FORTSCHRITT AS a5, ISTBEENDET AS a6, AUFGABENTYP_ID AS a7, LEVEL_ID AS a8, BENUTZER_ID AS a9 FROM ENTITYGENERIERTEAUFGABE WHERE (BENUTZER_ID = ?) ORDER BY CASE WHEN (CHANGETIME > CREATETIME) THEN CHANGETIME WHEN CREATETIME THEN DESC LIMIT ?, ?")
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:340)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:682)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:558)
at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2002)
at org.eclipse.persistence.sessions.server.ServerSession.executeCall(ServerSession.java:570)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeSelectCall(DatasourceCallQueryMechanism.java:299)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.selectAllRows(DatasourceCallQueryMechanism.java:694)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectAllRowsFromTable(ExpressionQueryMechanism.java:2738)
at org.eclipse.persistence.internal.querie
Information: s.ExpressionQueryMechanism.selectAllRows(ExpressionQueryMechanism.java:2691)
at org.eclipse.persistence.queries.ReadAllQuery.executeObjectLevelReadQuery(ReadAllQuery.java:495)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1168)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1127)
at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:403)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1215)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1804)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1786)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1751)
at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:258)
... 124 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DESC LIMIT 0, 6' at line 1
at sun.reflect.GeneratedConstructorAccessor204.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:377)
at com.mysql.jdbc.Util.getInstance(Util.java:360)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:978)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3887)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3823)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2435)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2582)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2530)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1907)
at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2030)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeSelect(DatabaseAccessor.java:1007)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:642)
... 144 more
如您所见,SQL 查询未正确构建。里面有错误:ORDER BY CASE WHEN (CHANGETIME > CREATETIME) THEN CHANGETIME WHEN CREATETIME THEN DESC LIMIT ?, ?
这很奇怪,因为它有时有效,但有时无效。
一些额外的信息: 我正在使用 JSF 2.2。当相关的 ViewController 被实例化时,会调用带有 JPQL 的方法:
@javax.faces.view.ViewScoped
@Named
public class ControllerErfolgskontrolle implements Serializable {
@Inject
private SvEnGenerierteAufgabe svEnGenerierteAufgabe;
private List<EntityGenerierteAufgabe> generierteAufgabenBenutzer;
@PostConstruct
private void doInit() {
generierteAufgabenBenutzer = svEnGenerierteAufgabe.getByLimitedSorted(
getControllerSession().getBenutzer());
}
...
}
以及相关的 SvEnGenerierteAufgabe-Class 以及创建 JPQL 的方法:
@Stateless
public class SvEnGenerierteAufgabe {
@PersistenceContext
EntityManager em;
public List<EntityGenerierteAufgabe> getByLimitedSorted(EntityBenutzer benutzer) {
List<EntityGenerierteAufgabe> aufgaben = new ArrayList<>();
String query = "SELECT ga FROM EntityGenerierteAufgabe ga "
+ "WHERE ga.benutzer=:benutzer "
+ "ORDER BY CASE WHEN (ga.changeTime > ga.createTime) THEN ga.changeTime ELSE ga.createTime END DESC";
aufgaben = em.createQuery(query)
.setParameter("benutzer", benutzer)
.setMaxResults(6)
.getResultList();
return aufgaben;
}
}
我正在使用以下版本: - Glassfish 4.1(构建 13) - Eclipse 持久性服务 - 2.5.2.v20140319-9ad6abd - MySQL 版本:5.5.27 - mysql-connector-java-5.1.34
当我没有设置限制 (setMaxResults(...)) 或删除排序时,我可以处理来自不同 jMeter-Test-Threads 的多个调用。两者一起在之前描述的某些情况下不起作用。
更新 1:
我之前也测试过 Alex JPQL。我现在又做了一次。它会生成以下生成的 SQL:
SELECT ID AS a1, ..., CHANGETIME AS a3, CREATETIME AS a4, ...,
CASE WHEN (CHANGETIME > CREATETIME) THEN CHANGETIME ELSE CREATETIME END
FROM ENTITYGENERIERTEAUFGABE WHERE (BENUTZER_ID = ?) ORDER BY CASE
WHEN (CHANGETIME > CREATETIME) THEN CHANGETIME ELSE CREATETIME END DESC LIMIT ?, ?
这也很好用。但是在运行渗透测试时我遇到了同样的问题。有时超过 1600 个请求(两个并行线程,每个发送 800 个请求)工作正常,有时我会遇到这个问题:
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FROM ENTITYGENERIERTEAUFGABE WHERE (BENUTZER_ID = 2) ORDER BY CASE WHEN (CHANGE' at line 1
以及生成的 SQL(在第一个 THEN CHANGETIME 之后是 WHEN CREATETIME 之后):
SELECT ID AS a1, BEARBEITUNGSZEIT AS a2, CHANGETIME AS a3, CREATETIME AS a4, FORTSCHRITT AS a5, ISTBEENDET AS a6, AUFGABENTYP_ID AS a7, LEVEL_ID AS a8, BENUTZER_ID AS a9, CASE WHEN (CHANGETIME > CREATETIME) THEN CHANGETIME WHEN CREATETIME THEN FROM ENTITYGENERIERTEAUFGABE WHERE (BENUTZER_ID = ?) ORDER BY CASE WHEN (CHANGETIME > CREATETIME) THEN CHANGETIME WHEN CREATETIME THEN DESC LIMIT ?, ?
我认为在某个地方存在 synchronazition-problem 或 transaction-problem 有时会导致 SQL 构建错误,此时将发送来自不同客户端的许多请求。但我没有发现问题。 有人有想法吗?我会测试它来更新这个问题。
【问题讨论】:
-
您正在尝试做的事情很奇怪。恕我直言,错了。 ORDER BY - 这意味着您有 1000 行带有 ID 的行 - 并希望它们按 ID 排序。通过您尝试动态设置 ORDER 的标准?这意味着您希望某些行按 ID 排序,而其他行按名称排序???所以你必须将你的 CASE 移动到 SELECT 语句。并在那里进行任何转换,并按此数据表的任何非常静态的东西排序:-)
标签: java mysql jpa eclipselink