web-dev-qa-db-ja.com

デタッチされたオブジェクトをJPApersistに渡す方法はありますか? (永続化するために渡された切り離されたエンティティ)

AccountAccountRoleの2つのエンティティがあります。

public class Account {
   private AccountRole accountRole;

   @ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
   public AccountRole getAccountRole() {
      return accountRole;
   }

public class AccountRole {
    private Collection<Account> accounts = new ArrayList<Account>();

    @OneToMany(mappedBy = "accountRole", fetch = FetchType.EAGER)
    public Collection<Account> getAccounts() {
         return accounts;
    }

データベースからaccountRoleを取得し、Accountを永続化しようとすると問題が発生します。 この時点で、アカウントを作成したばかりで、役割はすでにデータベースに存在します。

AccountRole role = accountService.getRoleFromDatabase(AccountRoles.ROLE_USER);
account.setAccountRole(role);

//setting both ways, as suggested
public void setAccountRole(AccountRole accountRole) {
    accountRole.addAccount(this);
    this.accountRole = accountRole;
}

entityManager.persist(account); // finally in my DAO

私はこれを読みました: JPA/Hibernate:永続化するために渡された切り離されたエンティティそして私が理解したことは、私がしていることをするために、両方向からエンティティ値を設定する必要があります私のセッターで。

まだエラーが発生しています。

 org.hibernate.PersistentObjectException: detached entity passed to persist: foo.bar.pojo.AccountRole
10
Jaanus

交換するだけです

entityManager.persist(account);

と:

entityManager.merge(account);

そして、マージカスケードを許可します。

@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.EAGER)
public AccountRole getAccountRole() {
    return accountRole;
}

マージはこれを行うため:

エンティティが新しい場合は、persist()と同じです。ただし、エンティティがすでに存在する場合は、エンティティが更新されます。

16
Jaanus

処理中にトランザクションを終了したように見えるため、accountRoleが切り離されるか、他の理由ですでに切り離されています。

entityManager.merge(accountRole)を呼び出す前にentityManager.persist(account)を呼び出すと、修正されるはずです。

編集:残念ながら、accountRoleがDBにすでに存在するかどうかわからない場合は、クエリを実行して確認する必要があります。存在する場合は-マージし、存在しない場合は-持続します。それは確かに面倒ですが、私はまだより良い回避策を見ていません。

EDIT2:mergeメソッドに渡すエンティティは切り離されたままになります-管理対象エンティティはmergeによって返されるため、最初にマージしてから、accountの参照をmergeの戻り値に設定する必要があります。

3
kostja

データ化されたエンティティを渡して永続化することはできません。方法はありません。しかし、そうする必要はありません。

Account(すでに永続化されている)とは独立してAccountRoleを永続化する必要があります。これを実現するには、単純に子エンティティの@ManyToOneからカスケードを削除(この場合はAccount):

public class Account {
    private AccountRole accountRole;

    @ManyToOne // no cascading here!
    public AccountRole getAccountRole() {
        return accountRole;
    }

ここで私の説明を参照してください、理由: https://stackoverflow.com/a/54271569/522578

0
Eugen Labun