首页 > 解决方案 > Spring data - 具有相同标识符的不同对象,即使对象相等

问题描述

我有一个使用 spring boot 2.2.6 构建的项目,我正在尝试在数据库中插入/更新一个条目(Parent)并对其子级进行级联 de 操作,如果子级不存在,则应该插入它,否则应该是更新。我有以下课程:

@Entity
@Data
public class Parent {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    List<Child> children1;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    List<Child> children2;
}
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(of = {"id"})
public class Child {
    @Id
    private Long id;

    private String name;
}
public interface ParentRepository extends JpaRepository<Parent, Long> {
}

@Service
public class TestService {

    private ParentRepository parentRepository;

    public TestService(ParentRepository parentRepository) {
        this.parentRepository = parentRepository;
    }

    @Transactional
    public void save() {
        Parent parent = new Parent();
        Child c1 = new Child(1L, "c1");
        Child c2 = new Child(1L, "c1");
        List<Child> children1 = new ArrayList<>();
        children1.add(c1);
        List<Child> children2 = new ArrayList<>();
        children1.add(c2);

        parent.setChildren1(children1);
        parent.setChildren2(children2);

        parentRepository.save(parent);
    }
}

我没有任何其他配置,因此我收到以下错误:

A different object with the same identifier value was already associated with the session : [Child#1]

我不明白为什么我会收到此错误,因为 c1.equals(c2) 被评估为真。

如果我将 children1 和 children2 的类型更改为 Set,我会收到以下错误:

Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.CHILD(ID) [1, 'c1']"

现在我不明白为什么会收到此错误,因为我期望合并级联类型来处理此问题,进行更新而不是插入。

你能帮我理解我错过了什么吗?你能给我一些关于在这种情况下插入/更新对象的最佳实践的提示吗?

标签: javaspring-bootjpaspring-data-jpaspring-data

解决方案


尽管 Java 中的对象相等,但这并不意味着它们在持久化上下文中是同一个实体。一旦你这样做:

Child c1 = new Child(1L, "c1");
Child c2 = new Child(1L, "c1");

...并使用PERSIST级联将它们持久化,这两个对象都将被持久化,并且由于它们的 ID 相等,因此违反了唯一 ID 约束。解决方案是使用相同的对象,因此实体只会被创建一次:

Child c = new Child(1L, "c1");

List<Child> children = new ArrayList<>();
children.add(c);

parent.setChildren1(children);
parent.setChildren2(children);

推荐阅读