Отдельная сущность передана для сохранения при сохранении дочерних данных

Я получаю эту ошибку при отправке формы:

org.hibernate.PersistentObjectException: отдельная сущность передана в постоянное хранилище: com.project.pmet.model.Account; Вложенное исключение: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: отдельная сущность передана в постоянное хранилище: 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;

    ...

Здесь часть контроллера:

    @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>

Что я делаю неправильно?

Ответ 1

teamService.save(team);

Метод сохранения принимает только переходные объекты. Что такое переходный объект, вы можете найти здесь

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, и вы пытаетесь сохранить его в БД, но у этого объекта есть объект Account в нем и этот объект учетной записи отсоединен (означает, что экземпляр этого объекта был сохранен в БД, но этот объект не является в сессии). Hibernate пытается сохранить его из-за того, что вы указали:

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

Итак, есть несколько способов, как вы можете это исправить:

1) не используйте конфигурацию CascadeType.ALL. Объект учетной записи может использоваться для количества команд (по крайней мере, это позволяет доменная структура), и операция обновления может обновлять учетную запись для ВСЕХ команд - это означает, что эту операцию не следует инициировать с обновлением Team. Я бы отключил параметр каскада (значение по умолчанию не является каскадным), если вам действительно нужна конфигурация MERGE/DELETE. Но если вам действительно нужно это сохранить, см. Параметр № 2

2) используйте метод saveOrUpdate() вместо "save()". Метод saveOrUpdate() принимает временные и отдельные объекты. Но проблема с этим подходом в дизайне: действительно ли вам нужно вставлять/обновлять учетную запись при сохранении объекта Team? Я бы разделил его на две операции и предотвратил обновление учетной записи из команды.

Надеюсь, что это поможет.

Ответ 2

Ошибка возникает из-за установки id. Спящий режим различает переходные и отдельные объекты и сохраняет работу только с переходными объектами.

isteamService.save(team);

в этой операции нельзя загрузить id, потому что это @GeneratedValue

Ответ 3

Поскольку ваш идентификатор является автоматически сгенерированным значением, не отправляйте его со стороны клиента. У меня была такая же проблема. Убедитесь, что вы не указали значение для автоматически генерируемого атрибута.