1. ホーム
  2. java

[解決済み] 子データ保存時にpersistに渡されるdetachedエンティティについて

2022-03-13 21:21:27

質問

フォームを送信するときに、このエラーが発生します。

<ブロッククオート

org.hibernate.PersistentObjectException: detached entity passed to persist: com.project.pmet.model.Account;ネストされた例外はjavax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.project.pmet.model.Account

以下は私のエンティティです。

アカウント

@Entity
@DynamicInsert
@DynamicUpdate
public class Account {

    @Id
    @GeneratedValue
    private Integer id;

    @Column(nullable = false)
    private String login;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private String email;

    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner")
    private List<Team> ownedTeams;

    ...

チームです。

@Entity
@DynamicInsert
@DynamicUpdate
public class Team {

    @Id
    @GeneratedValue
    private Integer id;

    @Column(nullable = false)
    private String name;

    @ManyToOne
    @JoinColumn(name = "owner_id", nullable = false)
    private Account owner;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "team")
    private List<Account> members;

    ...

これはControllerの一部です。

    @ModelAttribute("team")
    public Team createTeamObject() {
        return new Team();
    }

    @RequestMapping(value = "/teams/create-team", method = RequestMethod.GET)
    public String getCreateTeam(@ModelAttribute("team") Team team, Principal principal) {
        logger.info("Welcome to the create team page!");

        Account owner = accountService.findOneByLogin(principal.getName());
        team.setOwner(owner);
        team.setMembers(new AutoPopulatingList<Account>(Account.class));

        return "teams";
    }

    @RequestMapping(value = "/teams/create-team", method = RequestMethod.POST)
    public String postCreateTeam(@ModelAttribute("team") Team team) {
        logger.info("Team created!");

        teamService.save(team);

        return "redirect:/teams.html";
    }

そしてフォーム。

<form:form commandName="team" id="teamForm">
      <div class="form-group">
          <label>Name</label>
          <form:input path="name" cssClass="form-control" />
      </div>
      <div class="form-group" id="row-template">
          <label>Members</label>
          <form:select path="members[0].id" cssClass="form-control" data-live-search="true" >
             <form:options items="${accounts}" itemValue="id" />
          </form:select>
          ...
      </div>
   <form:hidden path="owner.id" />
</form:form>

何が間違っているのでしょうか?

どうすればいいですか?

teamService.save(team);

Saveメソッドは、transientオブジェクトのみを受け付けます。あなたが見つけることができる一時的なオブジェクトは何ですか ここで

Transient - an object is transient if it has just been instantiated using the new operator, and it is not associated with a Hibernate Session. It has no persistent representation in the database and no identifier value has been assigned. Transient instances will be destroyed by the garbage collector if the application does not hold a reference anymore. Use the Hibernate Session to make an object persistent (and let Hibernate take care of the SQL statements that need to be executed for this transition).

Teamオブジェクトを取得し、それをDBに永続化しようとしていますが、そのオブジェクトにはAccountオブジェクトが含まれており、そのAccountオブジェクトは切り離されています(そのオブジェクトのインスタンスはDBに保存されていますが、そのオブジェクトはセッションにないことを意味します)。Hibernateは、あなたが指定したようにそれを保存しようとしています。

@OneToMany(cascade = CascadeType.ALL, ....

そこで、この問題を解決するためのいくつかの方法があります。

1) CascadeType.ALL構成を使用しない。Accountオブジェクトは複数のチームに対して使用することができ(少なくともドメイン構造では可能)、更新操作によってすべてのチームのAccountが更新される可能性があるため、この操作はチームの更新とともに開始されるべきではありません。 もし本当にMERGE/DELETE設定が必要なら、私はそこからカスケード・パラメータを削除します(デフォルト値はカスケード操作なし)。しかし、本当に永続化する必要があるのであれば、オプション#2を参照してください。

2) 'save()' の代わりに 'saveOrUpdate()' メソッドを使用する。saveOrUpdate()' メソッドは、トランジェントなオブジェクトやデタッチドなオブジェクトを受け付けます。 しかし、この方法の問題は設計にあります。チームオブジェクトを保存するときに、本当にアカウントを挿入/更新する必要があるのでしょうか?私なら、この操作を2つに分けて、TeamからAccountを更新できないようにします。

お役に立てれば幸いです。