1

次のエンティティがあります。

@Entity
@Table(name = "sales", schema = "cust_tables")
@Multitenant(MultitenantType.TABLE_PER_TENANT)
@TenantTableDiscriminator(contextProperty = "customer_schema", type = TenantTableDiscriminatorType.SCHEMA)
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Sale implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "Sale")
    @TableGenerator(name = "Sale", schema = "thehub", allocationSize = 5)
    @Column(name = "id", unique = true, nullable = false, updatable = false)
    @XmlElement
    private Long id;

永続操作中、楽観的ロックの失敗時に意図的にトランザクションのロールバックを許可することがあります。

ただし、このシナリオでの JPA/EclipseLink の動作には驚かされます。JPA は、期待どおり、シーケンスから に値を割り当てますid。ただし、ロールバック中、この値は「空白」にはなりません。次に永続化しようとすると、値idが既に存在します。JPA は、シーケンスから新しい番号を取得することをスキップし、エンティティの永続化を試みます。

SQL整合性制約の例外で失敗しています:

Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130226-e0971b1): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '51' for key 'PRIMARY'
Error Code: 1062

私は何を間違っていますか?

4

2 に答える 2

2

JPA トランザクションが失敗すると、すべてのオブジェクトが切り離されます。トランザクションを再送信することはできません。

新しい永続コンテキストを作成する必要があり、オブジェクトを再作成するか、既存のものを慎重にマージして新しい永続コンテキストにマージし、それらの ID を無効にする必要があります。また、更新されたオブジェクトについては、バージョン フィールドを元に戻す必要があります。

JPA は、トランザクションを再送信する簡単な方法を提供していません。EclipseLink のネイティブ API は commitAndResumeOnFailure() を提供しますが、これは JPA には公開されません。おそらく、JPAで失敗したトランザクションを再送信できるようにするための何らかのオプションを持つように、拡張要求をログに記録します。

于 2013-04-03T13:26:58.190 に答える
0

OK、私はこれを理解することができました。同じ問題に遭遇する可能性のある人を助けるために、ここに結果を投稿します.

デバッガーを使用すると、トランザクションのロールバック中でも、EclipseLink が実際に entity.id フィールドに値を割り当てていることがわかります。あなたが私のようであれば、id フィールドには注釈が付けられ、ID は不変です。つまり、getter だけがあり、setter はありません。EclipseLink はリフレクションを使用してフィールドに値を割り当てることができますが、公開されている API は id フィールドの変更を防ぎます。(これは「良いこと」です)。

EclipseLink は、設計またはバグにより、ID フィールドに値を割り当て、その値を ID シーケンスから削除するように見えます。したがって、トランザクションのロールバックは、シーケンス番号に穴を開けます (これはそれほど大きな犯罪ではありません)。

EclipseLinkは順序番号表を読み取り、いくつかの順序番号をメモリーにキャッシュしてから、キャッシュが空になったときに再度読み取り、次の順序の開始値をコミットすることに注意してください。

どういうわけか、トランザクションのロールバックとサーバーの再起動の組み合わせにより、シーケンス番号テーブルは実際に使用されているシーケンス番号よりも遅れました。そのため、バグはトランザクションのロールバック中に ID を割り当てず、バグはシーケンス番号を追跡できません。私が最終的に得たのは、シーケンス番号テーブルの値が 50 であるということでしたが、データベース内の最大番号のエンティティは 51 でした。解決策は、シーケンス テーブルをallocationSize(5)の 2 倍に更新し、60 に設定してバウンスすることでした。私のサーバー。

残念ながら、このバグを再現することはできません。:( EclipseLinkがどのようにして不正な状態になったのかわかりません。

于 2013-04-03T15:31:55.103 に答える