我有一个问题,我接近解决方案或更好的说我接近如何避免问题EntityExistsException: A different object with the same identifier value
但不明白究竟是什么问题。为了保持代码简短和集中,我做了一个小应用程序来模拟我的项目中的问题。
首先我有一个CrudRepository始终保持不变:
public interface EntityARepository extends CrudRepository<EntityA, Long> {}
首先我需要实体,其中一个与另一个实体有关系:
@Entity
@EqualsAndHashCode(of={"name"})
@ToString(of={"name"})
@XmlRootElement
public class EntityA {
@Id
@GeneratedValue
@Setter
private Long id;
@Setter
@Column(nullable=false, unique=true)
private String name;
@Setter
@ManyToOne(fetch=EAGER, cascade={PERSIST, MERGE})
private EntityB entityB;
}
@ToString(of = { "name" })
@EqualsAndHashCode(of = { "name" })
@Entity
class EntityB {
@Id
// @GeneratedValue => produces issue!
@Setter
private Long id;
@Setter
@XmlAttribute
@Column(nullable=false, unique=true)
private String name;
}
然后我生成数据并尝试保存它们:
@Component
public class DatabaseInitializer implements InitializingBean {
@Autowired EntityARepository repository;
@Override
public void afterPropertiesSet() throws Exception {
final Set<EntityA> aEntities = createAEntities();
repository.save(aEntities);
}
private Set<EntityA> createAEntities() throws Exception {
Set<EntityA> aEntities = new HashSet<>();
aEntities.add(getFirstEntityA());
aEntities.add(getSecondEntityA());
return aEntities;
}
private EntityA getFirstEntityA(){
EntityA a = new EntityA();
// a.setId(1L);
a.setName("a-1");
a.setEntityB(getFirstEntityB());
return a;
}
private EntityA getSecondEntityA(){
EntityA a = new EntityA();
// a.setId(2L);
a.setName("a-2");
a.setEntityB(getFirstEntityB());
return a;
}
private EntityB getFirstEntityB() {
EntityB b = new EntityB();
b.setId(1l);
b.setName("b-1");
return b;
}
}
通过这种安装,我变成了EntityExistsException
:
Caused by: javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.example.EntityB#1]
答案 0 :(得分:0)
当firstEntityA被保存时,它还会保存firstEntityB(因为级联)。因此,该会话之后已经包含了类{1}的类EntityB
的实体。
当secondEntityA被持久化时,它还会调用第一个EntityB上的save,然后抛出异常。
原因是您不能在具有会话中已存在的ID的对象上调用save。
有很多方法可以修复它。例如,您可以调用merge而不是save。
repository.merge(aEntities);
解决此问题的另一种方法是使EntityA的两个实例都具有对同一个EntityB对象的引用:
private Set<EntityA> createAEntities() throws Exception {
Set<EntityA> aEntities = new HashSet<>();
EntityB entityB = getFirstEntityB();
aEntities.add(getFirstEntityA(entityB));
aEntities.add(getSecondEntityA(entityB));
return aEntities;
}
private EntityA getFirstEntityA(EntityB entityB){
EntityA a = new EntityA();
// a.setId(1L);
a.setName("a-1");
a.setEntityB(entityB);
return a;
}
private EntityA getSecondEntityA(EntityB entityB){
EntityA a = new EntityA();
// a.setId(2L);
a.setName("a-2");
a.setEntityB(entityB);
return a;
}
private EntityB getFirstEntityB() {
EntityB b = new EntityB();
b.setId(1l);
b.setName("b-1");
return b;
}