21

I'm getting this error when submitting the form:

org.hibernate.PersistentObjectException: detached entity passed to persist: com.project.pmet.model.Account; nested exception is javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.project.pmet.model.Account

Here are my entities:

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;

    ...

Team:

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

    ...

Here's a part of the 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";
    }

And the form:

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

What am I doing wrong?

Sana
  • 362
  • 3
  • 13
keysersoze
  • 2,402
  • 4
  • 18
  • 22

4 Answers4

46
teamService.save(team);

Save method accepts only transient objects. What is the transient object you can find here

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).

You are getting the Team object and you are trying to persist it to the DB but that object has Account object in it and that Account object is detached (means that instance of that object has saved into the DB but that object is not in the session). Hibernate is trying to save it because of you have specified:

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

So, there are few ways how you can fix it:

1) do not use CascadeType.ALL configuration. Account object can be used for number of Teams (at least domain structure allows it) and update operation might update Account for ALL Teams -- it means that this operation should not be initiated with Team update. I would remove cascade parameter from there (default value is no cascade operations), of if you really need use MERGE/DELETE configuration. But if you really need to persist it then see option #2

2) use 'saveOrUpdate()' method instead of 'save()'. 'saveOrUpdate()' method accepts transient and detached objects. But the problem with this approach is in design: do you really need to insert/update account when you are saving Team object? I would split it in two operations and prevent updating Account from the Team.

Hope this helps.

Ilya Ovesnov
  • 3,501
  • 1
  • 22
  • 29
19

The error occurs because the id is set. Hibernate distinguishes between transient and detached objects and persist works only with transient objects.

isteamService.save(team);

in this operation can not be loaded id because is @GeneratedValue

borchvm
  • 2,729
  • 7
  • 34
  • 36
  • 1
    Thats the perfext answer, make sure id has no value. – Michael Hegner Sep 17 '16 at 21:03
  • 1
    Oh, so objects with an id that has a `@GeneratedValue` can't even be saved if that id is already set? I thought that annotation would just be some kind of fallback. Good to know it's not! – Froxx Sep 30 '17 at 20:17
  • 1
    Make sure your id is not set to 0 if your datatype is `Integer` and it is okay if id datatype is `int`. – Ram Oct 21 '17 at 06:32
4

Since your id is auto generated value, don't send it from client side. I had a same issue. Make sure that you does't provide a value for auto generated attribute.

Pubudu
  • 83
  • 3
3

Please, change @OneToMany(cascade = CascadeType.ALL,..) to @OneToMany(cascade = CascadeType.REMOVE,...) or another except CascadeType.PERSIST and the problem has been solved