保存子数据时传递到持久化的分离实体

问题描述 投票:0回答:7

我在提交表单时收到此错误:

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>

我做错了什么?

java spring hibernate cascade persistent
7个回答
61
投票
teamService.save(team);

Save 方法仅接受瞬态对象。您可以在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).

您正在获取 Team 对象,并尝试将其持久保存到数据库中,但该对象中包含 Account 对象,并且该 Account 对象已分离(意味着该对象的实例已保存到数据库中,但该对象不在数据库中)会议)。 Hibernate 正在尝试保存它,因为您已指定:

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

因此,有几种方法可以解决它:

1) 不要使用CascadeType.ALL配置。帐户对象可用于多个团队(至少域结构允许),并且更新操作可能会更新所有团队的帐户 - 这意味着不应通过团队更新启动此操作。 如果您确实需要使用合并/删除配置,我将从那里删除级联参数(默认值是无级联操作)。但如果你真的需要坚持下去,那么请参阅选项#2

2)使用“saveOrUpdate()”方法而不是“save()”。 “saveOrUpdate()”方法接受瞬态和分离的对象。 但这种方法的问题在于设计:保存团队对象时是否真的需要插入/更新帐户?我会将其分为两个操作,并防止从团队更新帐户。

希望这有帮助。


25
投票

发生错误是因为设置了id。 Hibernate 区分瞬态对象和分离对象,并且持久化仅适用于瞬态对象。

isteamService.save(team);

在此操作中无法加载id,因为它是@GenerateValue


6
投票

请将

@OneToMany(cascade = CascadeType.ALL,..)
更改为
@OneToMany(cascade = CascadeType.REMOVE,...)
或除
CascadeType.PERSIST
之外的其他内容,问题已解决


5
投票

由于您的 id 是自动生成的值,因此不要从客户端发送它。我有同样的问题。确保您没有为自动生成的属性提供值。


1
投票

当我尝试保存子实体然后将新保存的实体作为参数传递给新的父对象时,发生了此错误。

例如:

ChildA a = childAService.save(childAObject);

Parent parent = new Parent()

parent.setChildA(a) // <=== Culprit

parentService.save(parent);

相反,请执行以下操作:

ChildA a = new ChildA();

parent.setChildA(a)

parentService.save(parent)

Hibernate 会为你处理

a
的持久化,你不必自己做。


1
投票

请注意 Lombok .toBuilder() 方法 - 它正在创建对象的新实例,当您尝试更新子对象的部分时,这可能会产生误导。

示例:

public class User {

 @OneToOne(...)
 Field x;

}
@Builder(toBuilder = true)
public class Field {
String a;
String b;
}
@Transactional
public class UserService {

public updateUserField(User user) {
...
user.setX(user.getX().toBuilder().a("New value").build());
}

}

这将抛出 PersistentObjectException,而无需显式调用 userRepo.save 方法。

你需要做:

var x = user.getX();
x.setA("New Value");

0
投票

出现主要问题是因为我在 URL 中包含了 ID,如下所示。这给 Hibernate 带来了混乱,因为在我的模型中,我用 @GenerateValue 注释了 ID,表明 Hibernate 应自动生成 ID。因此,不需要显式传递 ID 值:

@Controller
@RequestMapping("/course/{id}/section")
public class SectionController {
    //....SOME CODE
}

将 {id} 替换为特定内容:

@Controller
@RequestMapping("/course/{courseId}/section")
public class SectionController {
    //....SOME CODE
}
© www.soinside.com 2019 - 2024. All rights reserved.