1

私は単純なエンティティを持っています(簡潔にするためにいくつかの無関係なフィールドは省略されています):

@Entity
@Table(name = "tenant")
public class Tenant {

    @Id
    private String id;

    @Version @Column
    private long version = 0;

    @Column(name = "init_in_progress")
    private Boolean initializationInProgress;

}

次のような JPA 更新クエリを使用するように設定trueして、すべてのテナントで一括更新を実行したいと考えています。initializationInProgress

    entityManager.createQuery(
            "UPDATE Tenant t " +
            "SET t.initializationInProgress = :ip")
        .setParameter("ip", initializationInProgress)
        .executeUpdate();

そして、これは でうまく動作しEclipseLink 2.2.1ます。しかし、 version2.3.3で同じことをしようとすると、エラーが発生します:

Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.3.v20120629-r11760): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: more than one row returned by a subquery used as an expression
Error Code: 0
Call: UPDATE tenant SET version = (SELECT (version + ?) FROM tenant WHERE ID = tenant.ID), init_in_progress = ?
    bind => [1, true]
Query: UpdateAllQuery(referenceClass=Tenant sql="UPDATE tenant SET version = (SELECT (version + ?) FROM tenant WHERE ID = tenant.ID), init_in_progress = ?")
    at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:333)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.processExceptionForCommError(DatabaseAccessor.java:1494)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:838)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:906)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:592)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:535)
    at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:1717)
    at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:253)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:207)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:193)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeNoSelectCall(DatasourceCallQueryMechanism.java:236)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.updateAll(DatasourceCallQueryMechanism.java:789)
    at org.eclipse.persistence.queries.UpdateAllQuery.executeDatabaseQuery(UpdateAllQuery.java:153)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:844)
    at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:743)
    at org.eclipse.persistence.queries.ModifyAllQuery.executeInUnitOfWork(ModifyAllQuery.java:145)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2871)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1516)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1498)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1463)
    at org.eclipse.persistence.internal.jpa.EJBQueryImpl.executeUpdate(EJBQueryImpl.java:540)
    at com.example.dao.impl.tenant.TenantRepositoryImpl.updateAllTenantsInitializationStatus(TenantRepositoryImpl.java:100)

誰もこのバグに精通していますか? 既知の回避策/修正はありますか (バージョンを変更しないことを除くEclipseLink)? ご助力いただきありがとうございます。

ところで、私は使用してPostgreSQL 9.1います。

4

1 に答える 1

1

これは最初、同じ ID を持つ複数の行があるデータの問題のように見えましたが、tenant実際には EclipseLink クエリの問題です。これは生成されたクエリであるため、(深刻な) EclipseLink のバグを抱えているようです。

更新:バグ 393223 については、この EclipseLink bugzilla エントリを参照してください。

クエリは実際には への内部参照をエイリアスする必要がありますtenant。たとえば、次のようになります。

UPDATE tenant SET version = (SELECT (t2.version + ?) FROM tenant t2 WHERE t2.ID = tenant.ID), init_in_progress = ?

このエイリアスを追加しないと、常に true になることをID = tenant.ID意味するtenant.ID = tenant.IDため、サブクエリは のすべての行に一致しtenantます。

このデモを観察してください:

CREATE TABLE tenant ( ID integer primary key, version integer );
INSERT INTO tenant ( id, version ) values (1,0), (2,0), (3,0);

BEGIN;

PREPARE testq(integer) AS 
UPDATE tenant SET version = (SELECT (version + $1) FROM tenant WHERE ID = tenant.ID);

regress=> EXECUTE testq(1);
ERROR:  more than one row returned by a subquery used as an expression

ROLLBACK;

修正されたクエリ:

BEGIN;

PREPARE testq2(integer) AS 
UPDATE tenant SET version = (SELECT (version + $1) FROM tenant t2 WHERE t2.ID = tenant.ID);

regress=> EXECUTE testq2(1);
UPDATE 3

ROLLBACK;

これは EclipseLink のバグのようです。ネイティブ SQL として一括更新を行う以外に、コードでできることはあまりないと思います。

于 2012-11-09T01:42:35.737 に答える