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

时间:2013-02-27 21:58:39

标签: 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 = ?) AND ((t1.id = t0.id) AND (t0.DTYPE = ?)))

(正确填写?个广告位。)

在Informix 11.70上,我们收到一个错误,即子查询无法访问正在更改的表。

以下是关于Informix的子查询限制的文档:http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=%2Fcom.ibm.sqls.doc%2Fids_sqs_2005.htm

其他数据库也对像这样的子查询有限制,所以尽管这表现为Informix问题,但我确信如果我们针对MySQL运行这个问题,我们会得到类似的错误。

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

2 个答案:

答案 0 :(得分:1)

而不是:

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_subclass使用别名。这是允许在11.70。 我假设“id”是主键(或至少是唯一标识符)。

答案 1 :(得分:1)

找到答案。看起来EclipseLink必须处理MySQL的这种情况,它也有类似的问题。

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

  1. supportsLocalTemporaryTables():这需要返回true
  2. shouldAlwaysUseTempStorageForModifyAll():这需要返回true
  3. dontBindUpdateAllQueryUsingTempTables需要返回true
  4. getCreateTempTableSqlPrefix():这需要返回CREATE TEMP TABLE  
  5. getCreateTempTableSqlSuffix():这需要返回  WITH NO LOG
  6. isInformixOuterJoin():需要返回false
  7. getTempTableForTable(DatabaseTable):这需要这样做:

    return new DatabaseTable("TL_" + table.getName(), "" /* no table qualifier */, table.shouldUseDelimiters(), this.getStartDelimiter(), this.getEndDelimiter());
    
  8. 此外,您还需要覆盖以下方法以及正确的InformixPlatform行为:

    1. appendBoolean(Boolean, Writer):库存Informix平台没有正确写出布尔文字。你需要这样做:

      if (Boolean.TRUE.equals(booleanValue)) {
        writer.write("'t'");
      } else {
        writer.write("'f'");
      }
      
    2. 您需要覆盖writeUpdateOriginalFromTempTableSql,以便它包含与H2Platform的覆盖相同的代码:

      @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(")");
      }
      
    3. 最后,您的构造函数需要调用this.setShouldBindLiterals(false)

      通过这些更改,似乎Informix很高兴。