无状态EJB之间可能的竞争条件导致JPA生成错误的SQL

时间:2015-03-02 09:10:15

标签: java-ee jpa ejb race-condition

我在这个主题上提到我的最后一个问题,你可以在这里找到:MySQL Syntax Error by combining CASE and LIMIT (only sometimes) - generated by JPA

错误发生但我发现了一些有趣的事实。但首先这里是我的结构: 我使用的是JSF 2.2,Java EE,JPA和MySQL。 Web应用程序在Glassfish 4.1服务器中运行。

JSF-View从以下View-Controller调用列表:

@javax.faces.view.ViewScoped
@Named
public class ControllerErfolgskontrolle extends ControllerAbstract {

  @Inject
  private SvEnGenerierteAufgabe svEnGenerierteAufgabe;

  private List<EntityGenerierteAufgabe> generierteAufgabenBenutzer;

  @PostConstruct
  private void doInit() {
    generierteAufgabenBenutzer = svEnGenerierteAufgabe.getByLimitedSorted(
            getControllerSession().getBenutzer());
  }

  public List<EntityGenerierteAufgabe> getGenerierteAufgabenBenutzer() {
    return generierteAufgabenBenutzer;
  }
}

View-Contorller从EJB SvEnGenrierteAufgabe收到相关列表(generierteAufgabenBenutzer):

@javax.ejb.Stateless
public class SvEnGenerierteAufgabe {

  @PersistenceContext
  EntityManager em;

  public List<EntityGenerierteAufgabe> getByLimitedSorted(EntityBenutzer benutzer) {

    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";
    List<EntityGenerierteAufgabe> aufgaben = new ArrayList<>();
    List<Object[]> results = null;

    results = em.createQuery(query)
            .setParameter("benutzer", benutzer)
            .setMaxResults(6)
            .getResultList();

    for (Object[] result : results) {
      aufgaben.add((EntityGenerierteAufgabe) result[0]);
    }

    return aufgaben;
  }
}

在大多数情况下,一切正常并且符合预期。但是,当我通过jMeter对至少两个不同的线程进行压力测试时,EntityManager SOMETIMES无法以正确的方式创建MySQL-Query,并导致MySQLSyntaxErrorException,如下所示:

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
Error Code: 1064
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, 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 ?, ?
    bind => [3 parameters bound]
Query: ReportQuery(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, 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 ?, ?")
    at ...

实际语法错误是WHEN CREATETIME THEN部分,根本不属于那里。

我假设某些无状态实例与不同的EntityManager实例之间存在竞争条件,这导致EntityManager有时会构建错误的SQL。

这只是一个想法,但当我用@ javax.ejb.Singleton替换@ javax.ejb.Stateless时一切正常。所以我的问题是:我做错了什么?据我所知,有一个无状态实例池,每个客户端(在本例中为ViewController)都为方法调用获取自己的实例,然后实例返回池中。

1 个答案:

答案 0 :(得分:0)

我做了一些测试: 我使用 Hibernate 作为JPA-Provider而不是 EclipseLink ,结果是错误不再发生。

那么EclipseLink实现中是否存在可能的错误?