【问题标题】:MySQL Syntax Error by combining CASE and LIMIT (only sometimes) - generated by JPA结合 CASE 和 LIMIT 的 MySQL 语法错误(仅有时) - 由 JPA 生成
【发布时间】: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-problemtransaction-problem 有时会导致 SQL 构建错误,此时将发送来自不同客户端的许多请求。但我没有发现问题。 有人有想法吗?我会测试它来更新这个问题。

【问题讨论】:

  • 您正在尝试做的事情很奇怪。恕我直言,错了。 ORDER BY - 这意味着您有 1000 行带有 ID 的行 - 并希望它们按 ID 排序。通过您尝试动态设置 ORDER 的标准?这意味着您希望某些行按 ID 排序,而其他行按名称排序???所以你必须将你的 CASE 移动到 SELECT 语句。并在那里进行任何转换,并按此数据表的任何非常静态的东西排序:-)

标签: java mysql jpa eclipselink


【解决方案1】:
 String query = "SELECT ga , "
            + " CASE WHEN (ga.changeTime > ga.createTime) THEN ga.changeTime ELSE ga.createTime END as myorder"
            + " FROM EntityGenerierteAufgabe ga"
            + " WHERE ga.benutzer=:benutzer "
            + " ORDER BY myorder DESC";

【讨论】:

  • 谢谢。它不能解决有时会导致错误构建 SQL 的问题。请参阅我的问题中的更新 1。
  • 所以在您的错误消息中我们可以看到ORDER BY CASE WHEN 这不是我的查询。我的查询使用ORDER BY myorder,所以您可能还有其他查询,所以请告诉我那个 - 我可以尝试解决这个问题
  • 开个玩笑。我正在使用您的 JPQL,它会生成上面的 SQL:SELECT ga, CASE WHEN (ga.changeTime &gt; ga.createTime) THEN ga.changeTime ELSE ga.createTime END AS myorder FROM EntityGenerierteAufgabe ga WHERE ga.benutzer=:benutzer ORDER BY myorder DESC
  • 我正在使用您的 JPQL,它会生成上面的 SQL:SELECT ga, CASE WHEN (ga.changeTime &gt; ga.createTime) THEN ga.changeTime ELSE ga.createTime END AS myorder FROM EntityGenerierteAufgabe ga WHERE ga.benutzer=:benutzer ORDER BY myorder DESC
  • 对我来说,使用什么来生成查询字符串并不重要。最终生成了吗?等于我的查询?如果是 - 那么您永远不会收到像您在 update 1 中发布的错误消息
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-16
  • 1970-01-01
相关资源
最近更新 更多