296

多対一の関係を含む JPA 永続オブジェクト モデルがあります: an Accounthas many Transactions。ATransactionは 1 つ持っていAccountます。

コードのスニペットを次に示します。

@Entity
public class Transaction {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne(cascade = {CascadeType.ALL},fetch= FetchType.EAGER)
    private Account fromAccount;
....

@Entity
public class Account {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @OneToMany(cascade = {CascadeType.ALL},fetch= FetchType.EAGER, mappedBy = "fromAccount")
    private Set<Transaction> transactions;

オブジェクトを作成しAccount、それにトランザクションを追加し、Accountオブジェクトを正しく保持できます。しかし、既存の永続化されたアカウントを使用してトランザクションを作成し、トランザクションを永続化すると、例外が発生します。

原因: org.hibernate.PersistentObjectException: 切り離されたエンティティが永続化に渡されました: org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:141) の com.paulsanwald.Account

Accountそのため、トランザクションを含むを永続化できますが、 Account. Accountが添付されていない可能性があるためだと思いましたが、このコードでも同じ例外が発生します。

if (account.getId()!=null) {
    account = entityManager.merge(account);
}
Transaction transaction = new Transaction(account,"other stuff");
 // the below fails with a "detached entity" message. why?
entityManager.persist(transaction);

すでに永続化されているオブジェクトTransactionに関連付けられているを正しく保存するにはどうすればよいですか?Account

4

21 に答える 21

348

解決策は簡単です。またはのCascadeType.MERGE代わりに を使用するだけです。CascadeType.PERSISTCascadeType.ALL

私は同じ問題を抱えていてCascadeType.MERGE、私のために働いています。

あなたがソートされていることを願っています。

于 2015-03-24T14:28:51.240 に答える
145

これは、典型的な双方向の整合性の問題です。このリンクだけでなく、このリンクでもよく説明されています。

前の2つのリンクの記事のように、双方向の関係の両側でセッターを修正する必要があります。片側のセッターの例は、このリンクにあります。

多側のセッターの例は、このリンクにあります。

セッターを修正した後、エンティティアクセスタイプを「プロパティ」として宣言します。「プロパティ」アクセスタイプを宣言するためのベストプラクティスは、すべてのアノテーションをメンバープロパティから対応するゲッターに移動することです。注意すべき重要な点は、エンティティクラス内で「フィールド」と「プロパティ」のアクセスタイプを混在させないことです。そうしないと、動作がJSR-317仕様で定義されません。

于 2012-11-21T19:45:10.423 に答える
15

マージの使用は危険でトリッキーなので、あなたのケースでは汚い回避策です。エンティティ オブジェクトをマージに渡すと、トランザクションへのアタッチが停止され、代わりに新しいアタッチされたエンティティが返されることを少なくとも覚えておく必要があります。これは、誰かがまだ古いエンティティ オブジェクトを所有している場合、それに対する変更は黙って無視され、コミット時に破棄されることを意味します。

ここには完全なコードが表示されていないため、トランザクション パターンを再確認することはできません。このような状況に陥る 1 つの方法は、マージと永続化を実行するときにアクティブなトランザクションがない場合です。その場合、永続化プロバイダーは、実行するすべての JPA 操作に対して新しいトランザクションを開き、呼び出しが戻る前にすぐにコミットして閉じることが期待されます。この場合、最初のトランザクションでマージが実行され、マージ メソッドが返された後、トランザクションが完了して閉じられ、返されたエンティティが切り離されます。その下の永続化は 2 番目のトランザクションを開き、切り離されたエンティティを参照しようとすると、例外が発生します。自分が何をしているのかよくわかっていない限り、常にコードをトランザクション内にラップしてください。

コンテナ管理のトランザクションを使用すると、次のようになります。注: これは、メソッドがセッション Bean 内にあり、ローカルまたはリモート インターフェイスを介して呼び出されることを前提としています。

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void storeAccount(Account account) {
    ...

    if (account.getId()!=null) {
        account = entityManager.merge(account);
    }

    Transaction transaction = new Transaction(account,"other stuff");

    entityManager.persist(account);
}
于 2015-07-13T13:15:09.080 に答える
14

おそらくこの場合account、マージ ロジックを使用してオブジェクトを取得し、persist新しいオブジェクトを永続化するために使用されます。階層に既に永続化されたオブジェクトがあると、エラーが発生します。saveOrUpdateそのような場合は、の代わりに使用する必要がありpersistます。

于 2012-11-13T23:02:34.137 に答える
5

何も解決せず、それでもこの例外が発生する場合は、equals()メソッドを見直して、子コレクションを含めないでください。特に、埋め込みコレクションの構造が深い場合 (たとえば、A には B が含まれ、B には C が含まれるなど)。

の例Account -> Transactions:

  public class Account {

    private Long id;
    private String accountName;
    private Set<Transaction> transactions;

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (!(obj instanceof Account))
        return false;
      Account other = (Account) obj;
      return Objects.equals(this.id, other.id)
          && Objects.equals(this.accountName, other.accountName)
          && Objects.equals(this.transactions, other.transactions); // <--- REMOVE THIS!
    }
  }

equals()上記の例では、小切手からトランザクションを削除します。これは、hibernate が、古いオブジェクトを更新しようとしているのではなく、子コレクションの要素を変更するたびに新しいオブジェクトを渡して永続化することを意味するためです。
もちろん、このソリューションがすべてのアプリケーションに適合するわけではないため、equalsおよびhashCodeメソッドに含めるものを慎重に設計する必要があります。

于 2016-08-04T23:03:43.230 に答える
5

エンティティ定義では、結合された aの@JoinColumnを指定していません。次のようなものが必要です。AccountTransaction

@Entity
public class Transaction {
    @ManyToOne(cascade = {CascadeType.ALL},fetch= FetchType.EAGER)
    @JoinColumn(name = "accountId", referencedColumnName = "id")
    private Account fromAccount;
}

@Table編集:まあ、クラスで注釈を使用している場合に役立つと思います。へー。:)

于 2012-11-13T23:12:12.997 に答える
0

これが私の修正です。

以下は私のエンティティです。ID に @GeneratedValue(strategy = GenerationType.AUTO) の注釈が付けられていることをマークします。これは、ID が Hibernate によって生成されることを意味します。エンティティ オブジェクトの作成時に設定しないでくださいそれはHibernateによって自動生成されるためです。 エンティティ ID フィールドが @GeneratedValue でマークされていない場合は、ID に値を手動で割り当てないことも犯罪であり、IdentifierGenerationException: ids for this class must be manually assigned before calling save() で迎えられることに注意してください。

@Entity
@Data
@NamedQuery(name = "SimpleObject.findAll", query="Select s FROM SimpleObject s")
public class SimpleObject {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column
    private String key;

    @Column
    private String value;

}

そして、これが私のメインクラスです。

public class SimpleObjectMain {

    public static void main(String[] args) {

        System.out.println("Hello Hello From SimpleObjectMain");

        SimpleObject simpleObject = new SimpleObject();
        simpleObject.setId(420L); // Not right, when id is a generated value then no need to set this.
        simpleObject.setKey("Friend");
        simpleObject.setValue("Bani");

        EntityManager entityManager = EntityManagerUtil.getEntityManager();
        entityManager.getTransaction().begin();
        entityManager.persist(simpleObject);
        entityManager.getTransaction().commit();

        List<SimpleObject> simpleObjectList = entityManager.createNamedQuery("SimpleObject.findAll").getResultList();
        for(SimpleObject simple : simpleObjectList){
            System.out.println(simple);
        }

        entityManager.close();
        
    }
}

それを保存しようとしたとき、それはそれを投げていました

PersistentObjectException: detached entity passed to persist.

修正する必要があったのは、メイン メソッドの simpleObject の id 設定行を削除することだけでした。

于 2021-06-14T03:26:38.943 に答える