Jpa 2.0 如何让EclipseLink为UPDATE WHERE子句输出有效的Informix SQL?

Jpa 2.0 如何让EclipseLink为UPDATE WHERE子句输出有效的Informix SQL?,jpa-2.0,eclipselink,informix,Jpa 2.0,Eclipselink,Informix,我们有这样一个命名查询: UPDATE Foo f SET f.x = 0 WHERE f.x = :invoiceId Foo在本例中是一个具有超类的实体,使用每类表继承策略 EclipseLink生成的SQL是: UPDATE foo_subclass SET x = ? WHERE EXISTS(SELECT t0.id FROM foo_superclass t0, foo_subclass t1 WHERE ((t1.x

我们有这样一个命名查询:

UPDATE Foo f SET f.x = 0 WHERE f.x = :invoiceId
Foo
在本例中是一个具有超类的实体,使用每类表继承策略

EclipseLink生成的SQL是:

UPDATE foo_subclass SET x = ?
 WHERE EXISTS(SELECT t0.id
                FROM foo_superclass t0, foo_subclass t1
               WHERE ((t1.x = ?) AND ((t1.id = t0.id) AND (t0.DTYPE = ?)))
(已正确填写
插槽。)

在Informix11.70上,我们得到一个错误,子查询无法访问正在更改的表

以下是关于Informix子查询限制的文档:

其他数据库也有这样的子查询限制,所以尽管这表现为Informix问题,但我敢肯定,如果我们针对MySQL运行此操作,我们将得到类似的错误

我如何让EclipseLink遵守这些限制?我是否应该使用更好的查询?

而不是:

UPDATE foo_subclass SET x = ?
 WHERE EXISTS(SELECT t0.id
                FROM foo_superclass t0, foo_subclass t1
               WHERE ((t1.x = ?) AND ((t1.id = t0.id) AND (t0.DTYPE = ?)))
这样做:

UPDATE
  foo_subclass SET x = ?
WHERE
  foo_subclass.x = ? AND
  EXISTS(SELECT t0.id
         FROM foo_superclass t0
         WHERE ((foo_subclass.id = t0.id) AND (t0.DTYPE = ?))
请注意,在11.50上,不能对foo_子类使用别名。11点70分允许。
我假设“id”是主键(或者至少是唯一标识符)。

找到了答案。看起来EclipseLink不得不为MySQL处理这个问题,MySQL也有类似的问题

答案是,在
InformixPlatform
子类中,您需要重写以下方法来解决此问题:

  • supportsLocalTemporaryTables()
    :需要返回
    true
  • 应始终使用etempstorageformodifyall()
    :这需要返回
    true
  • dontbindustpdateallquerysingtestables
    需要返回
    true
  • getCreateTestableSqlPrefix()
    :这需要返回
    创建临时表
  • getCreateTestableSqlSuffix()
    :需要返回不带日志的
  • IsInformixOutJoin()
    :需要返回
    false
  • getTempTableForTable(DatabaseTable)
    :需要执行以下操作:

    return new DatabaseTable("TL_" + table.getName(), "" /* no table qualifier */, table.shouldUseDelimiters(), this.getStartDelimiter(), this.getEndDelimiter());
    
    if (Boolean.TRUE.equals(booleanValue)) {
      writer.write("'t'");
    } else {
      writer.write("'f'");
    }
    
  • 此外,您还需要重写以下方法以实现正确的
    InformixPlatform
    行为:

  • appendBoolean(Boolean,Writer)
    :股票Informix平台无法正确写出布尔文本。您需要这样做:

    return new DatabaseTable("TL_" + table.getName(), "" /* no table qualifier */, table.shouldUseDelimiters(), this.getStartDelimiter(), this.getEndDelimiter());
    
    if (Boolean.TRUE.equals(booleanValue)) {
      writer.write("'t'");
    } else {
      writer.write("'f'");
    }
    
  • 您需要重写
    writeUpdateOriginalFromTestableSQL
    ,以便它包含与
    H2平台的重写相同的代码:

    @Override
    public void writeUpdateOriginalFromTempTableSql(final Writer writer, final DatabaseTable table, final Collection pkFields, final Collection assignedFields) throws IOException {
      writer.write("UPDATE ");
      final String tableName = table.getQualifiedNameDelimited(this);    
      writer.write(tableName);
      writer.write(" SET ");
      final int size = assignedFields.size();
      if (size > 1) {
        writer.write("(");            
      }
      writeFieldsList(writer, assignedFields, this);
      if (size > 1) {
        writer.write(")");            
      }
      writer.write(" = (SELECT ");        
      writeFieldsList(writer, assignedFields, this);
      writer.write(" FROM ");
      final String tempTableName = this.getTempTableForTable(table).getQualifiedNameDelimited(this);
      writer.write(tempTableName);
      writeAutoJoinWhereClause(writer, null, tableName, pkFields, this);
      writer.write(") WHERE EXISTS(SELECT ");
      writer.write(((DatabaseField)pkFields.iterator().next()).getNameDelimited(this));
      writer.write(" FROM ");
      writer.write(tempTableName);
      writeAutoJoinWhereClause(writer, null, tableName, pkFields, this);
      writer.write(")");
    }
    
  • 最后,构造函数需要调用this.setShouldBindLiterals(false)


    有了这些变化,Informix似乎很高兴。

    谢谢,但我想我做不到。EclipseLink控制SQL语法。我们的对手是11.70。是的,在我的例子中,
    id
    是这个表的主键。请问语法问题是什么?为什么Informix不喜欢EclipseLink创建的SQL?@lairdelson:这不是语法问题;这是一个语义问题。这些限制背后的问题是,在处理查询时,表是否会更改,从而导致更新数据集中的稳定性问题。这些问题有多严重,以及是否有适当的解决办法,都有待讨论,但这种限制自古以来就存在了。@Lairdelson:我很好奇为什么需要EXISTS查询。如果表(
    foo_超类
    foo_子类
    )处于语义完整性状态-没有损坏(或丢失)的外键约束,则无需检查子类中的行是否属于子类,是吗?或者,如果有的话,关于这两个表的模式,我们还不知道什么?JPA中的批量更新操作的是类,而不是表。它只允许接触Foo类,如果没有exists将其连接到根表以便它可以使用该类型进行筛选,它就无法做到这一点。其他类或其他应用程序也可能以某种不受此查询影响的方式使用foo_子类表?您引用了11.50信息中心,所以我猜您使用的是11.50。然而,由于11.70()中的等效页面包含相同的信息,因此这一次可能不是主要问题(但始终说明您正在使用的Informix版本,以及平台;这通常很重要);EclipseLink在connect上检测到哪个数据库平台(应该打印到日志中)?它能正确地检测Informix吗?它能。股票
    InformixPlatform
    有几个问题,所以我实际上有一个
    SessionCustomizer
    ,它安装了我的子类(一方面覆盖
    appendbooleanasveral
    )。在大多数情况下,它们是相同的;还有更多的东西要放在这里。(对于后面的方法)还需要覆盖其他几种方法,包括临时表前缀和后缀方法(首先要添加
    而不在末尾添加日志)。现在Informix平台正在插入之前删除临时表。下一步要解决这个问题。很明显,这个平台已经很久没有经过测试了。接下来,Informix不支持使用模式/所有者前缀限定
    TEMP TABLE
    名称。这——再加上EclipseLink忽略(!)实际尝试
    CREATE TEMP TABLE
    操作的语句中的所有错误(!!),意味着由于尝试创建带有模式前缀的TEMP TABLE而导致的语法错误被隐藏/吞没。平台的更多补丁即将发布。