Spring JPA @ManyToOne保存失败,重复键

时间:2018-04-23 10:09:53

标签: java spring hibernate spring-data-jpa

我有以下实体:

@Entity
@Data
@Table(name = "Parents")
public class Parent {
    @Id
    @GeneratedValue
    Long id;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "child")
    private Child child;
}

@Entity
@Data
@Table(name = "Childs")
public class Child {
    @Id
    String number;
}

对于这两个实体,我都有一个标准的CrudRepository。在启动应用程序时,我尝试使用以下代码填充数据库:

@Component
public class TestFiller implements ApplicationListener<ContextRefreshedEvent> {
    @Autowired
    ParentRepository parentRepository;

    @Autowired
    ChildRepository childRepository;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        for (int i = 0; i < 100; i++) {
            Parent parent = new Parent();

            String childId = "CHILD" + Math.round(Math.random() * 10);
            Optional<Child> childOpt = childRepository.findById(childId);
            if (childOpt.isPresent()) {
                parent.setChild(childOpt.get());
            } else {
                Child child = new Child();
                child.setNumber(childId);
                parent.setChild(child);
            }

            parentRepository.save(parent);
        }
    }
}

这是ChildRepository:

@Repository
public interface ChildRepository extends JpaRepository<Child, String> {
}

但是由于一些莫名其妙的原因,当一个孩子已经出现在数据库中时,这会失败:

Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "childs_pkey"
  Detail: Key (number)=(CHILD6) already exists.
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2182) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1911) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:173) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
    at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:645) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:495) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:441) ~[postgresql-9.4-1206-jdbc42.jar:9.4]

看起来像findById方法返回的实体不受管理。为什么Hibernate / JPA试图保留现有实体?

编辑:根据请求我添加了ChildRepository

3 个答案:

答案 0 :(得分:0)

我认为这是因为你的String ID。 你可以尝试用findOne()方法来帮助你。

String childId = "CHILD" + Math.round(Math.random() * 10);
Child child = new Child();
child.setNumber(childId);
Child childOpt = childRepository.findOne(child);

答案 1 :(得分:0)

根据java documentation

  

这是一个基于价值的课程;使用身份敏感操作   (包括引用相等(==),标识哈希码或   可选的实例可能具有不可预测性   结果,应该避免。

所以首先尝试从结果中获取对象并检查它是否是预期值: -

  if (childOpt.isPresent()) {
           Child checkObj=childOpt.get():
           parent.setChild(checkObj);
  }

答案 2 :(得分:0)

好的,回答我自己的问题:

它是由@ManyToOne(cascade = CascadeType.ALL)的{​​{1}}属性上的CascadeType Child引起的。删除它修复了它。

显然,JPA也会将Parent操作级联到Child,即使它很容易知道Child已经是托管和持久化实体。