为什么 Hibernate 在一对多双向更新操作中给出同一实体的多个表示?
Why Hibernate giving Multiple representations of the same entity in One to Many Bidirectional update actions?
我尝试了很多解决方案但都失败了。试图实现的是更新操作。
我有两个实体,比如人与人的交流。
@Entity
@Table(name = "person")
@SequenceGenerator(name = "person_id_seq", sequenceName = "person_id_seq", allocationSize = 1)
@Inheritance(strategy = InheritanceType.JOINED)
@Getter
@Setter
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "person_id_seq")
protected int id;
@OrderBy(value = "communicationAddress ASC")
@OneToMany(targetEntity = PersonCommunication.class,cascade=CascadeType.ALL, mappedBy = "person")
protected List<PersonCommunication> communications;
}
@Getter
@Setter
@Entity
@Table(name = "person_communication")
@SequenceGenerator(name = "person_communication_id_seq", sequenceName = "person_communication_id_seq", allocationSize = 1)
public class PersonCommunication {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "person_communication_id_seq")
private int id;
@ManyToOne(cascade = CascadeType.ALL)
private Person person;
}
当我尝试添加和更新时,在我的服务 class 中,假设我想添加一些通讯,通过 id 找到一个人,如果存在则更新通讯而不是另存为新人像这样。
Optional<StaffMember> staffMemberOptional = staffRepository.findById(id);
StaffMember incoming = modelMapper.map(staffMemberDTO, StaffMember.class);
if(staffMemberOptional.isPresent()){
staffMemberOptional.get().setCommunications(incoming.getCommunications());
staffMemberOptional.get().setFirstName(incoming.getFirstName());
staffRepository.save(staffMemberOptional.get());
}else{
staffRepository.save(incoming);
}
当我尝试更新时出现异常。
***"Multiple representations of the same entity [com.impactivo.smartpcmh.server.model.StaffMember#2050] are being merged. Detached: [com.impactivo.smartpcmh.server.model.Person@1ad3d552]; Managed: [com.impactivo.smartpcmh.server.model.StaffMember@12f382f3]; nested exception is java.lang.IllegalStateException: Multiple representations of the same entity [com.impactivo.smartpcmh.server.model.StaffMember#2050] are being merged. Detached: [com.impactivo.smartpcmh.server.model.Person@1ad3d552]; Managed: [com.impactivo.smartpcmh.server.model.StaffMember@12f382f3]",***
为什么会这样以及如何解决这个问题?
谢谢。
发生这种情况是因为您在收集了同一实体的托管版本后试图合并一个分离的实体。
在事务中获取实体时,您会得到一个托管实体,在同一事务中再次获取它会在 return 中为您提供相同的对象。另一方面,如果您跨越事务边界,实体将变得分离,如果您开始一个新的事务并再次获取相同的实体,您实际上会得到一个不同的对象。然后,如果您尝试重新引入分离的实体(使用合并),Hibernate 会抱怨,因为它将有两个对象用于同一个实体,并且它不知道其中哪个包含正确的状态以写入数据库。
整个分离-合并模式在我看来是一种反模式,应该避免。您应该在事务中获取实体、进行更改并提交事务(应配置为刷新所有更改,无需 运行 合并)。
// (1)this will load a managed version of the entity
Optional<StaffMember> staffMemberOptional = staffRepository.findById(id);
// (2) this will create a detached version of the entity
StaffMember incoming = modelMapper.map(staffMemberDTO, StaffMember.class);
if(staffMemberOptional.isPresent()){
staffMemberOptional.get().setCommunications(incoming.getCommunications());
staffMemberOptional.get().setFirstName(incoming.getFirstName());
staffRepository.save(staffMemberOptional.get());
}else{
//this will try to reintroduce the detached entity, but since it was already
//loaded in (1), the entity manager will end up with 2 versions of the same entity
staffRepository.save(incoming);
}
我尝试了很多解决方案但都失败了。试图实现的是更新操作。 我有两个实体,比如人与人的交流。
@Entity
@Table(name = "person")
@SequenceGenerator(name = "person_id_seq", sequenceName = "person_id_seq", allocationSize = 1)
@Inheritance(strategy = InheritanceType.JOINED)
@Getter
@Setter
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "person_id_seq")
protected int id;
@OrderBy(value = "communicationAddress ASC")
@OneToMany(targetEntity = PersonCommunication.class,cascade=CascadeType.ALL, mappedBy = "person")
protected List<PersonCommunication> communications;
}
@Getter
@Setter
@Entity
@Table(name = "person_communication")
@SequenceGenerator(name = "person_communication_id_seq", sequenceName = "person_communication_id_seq", allocationSize = 1)
public class PersonCommunication {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "person_communication_id_seq")
private int id;
@ManyToOne(cascade = CascadeType.ALL)
private Person person;
}
当我尝试添加和更新时,在我的服务 class 中,假设我想添加一些通讯,通过 id 找到一个人,如果存在则更新通讯而不是另存为新人像这样。
Optional<StaffMember> staffMemberOptional = staffRepository.findById(id);
StaffMember incoming = modelMapper.map(staffMemberDTO, StaffMember.class);
if(staffMemberOptional.isPresent()){
staffMemberOptional.get().setCommunications(incoming.getCommunications());
staffMemberOptional.get().setFirstName(incoming.getFirstName());
staffRepository.save(staffMemberOptional.get());
}else{
staffRepository.save(incoming);
}
当我尝试更新时出现异常。
***"Multiple representations of the same entity [com.impactivo.smartpcmh.server.model.StaffMember#2050] are being merged. Detached: [com.impactivo.smartpcmh.server.model.Person@1ad3d552]; Managed: [com.impactivo.smartpcmh.server.model.StaffMember@12f382f3]; nested exception is java.lang.IllegalStateException: Multiple representations of the same entity [com.impactivo.smartpcmh.server.model.StaffMember#2050] are being merged. Detached: [com.impactivo.smartpcmh.server.model.Person@1ad3d552]; Managed: [com.impactivo.smartpcmh.server.model.StaffMember@12f382f3]",***
为什么会这样以及如何解决这个问题?
谢谢。
发生这种情况是因为您在收集了同一实体的托管版本后试图合并一个分离的实体。
在事务中获取实体时,您会得到一个托管实体,在同一事务中再次获取它会在 return 中为您提供相同的对象。另一方面,如果您跨越事务边界,实体将变得分离,如果您开始一个新的事务并再次获取相同的实体,您实际上会得到一个不同的对象。然后,如果您尝试重新引入分离的实体(使用合并),Hibernate 会抱怨,因为它将有两个对象用于同一个实体,并且它不知道其中哪个包含正确的状态以写入数据库。
整个分离-合并模式在我看来是一种反模式,应该避免。您应该在事务中获取实体、进行更改并提交事务(应配置为刷新所有更改,无需 运行 合并)。
// (1)this will load a managed version of the entity
Optional<StaffMember> staffMemberOptional = staffRepository.findById(id);
// (2) this will create a detached version of the entity
StaffMember incoming = modelMapper.map(staffMemberDTO, StaffMember.class);
if(staffMemberOptional.isPresent()){
staffMemberOptional.get().setCommunications(incoming.getCommunications());
staffMemberOptional.get().setFirstName(incoming.getFirstName());
staffRepository.save(staffMemberOptional.get());
}else{
//this will try to reintroduce the detached entity, but since it was already
//loaded in (1), the entity manager will end up with 2 versions of the same entity
staffRepository.save(incoming);
}