Spring JPA/Hibernate @ManyToOne 关系总是插入 child 行

Spring JPA/Hibernate @ManyToOne relationship always inserting child row

我正在尝试使用 Spring JPA/Hibernate 维护 Parent table 与 child table 相关ID。如果我尝试插入相同的 object 两次,Parent table 会正确获得 "updated",但 child table 总是插入到偶数尽管 child table 中的条目已经存在。

CREATE TABLE `parent_table` (
  `parent_id` int(11) NOT NULL AUTO_INCREMENT,
  `ex_column_1` int(11) NOT NULL, // there is a unique constraint on this field - I just didn't include it
  `ex_column_2` int(11) NOT NULL,
  `child_id` int(11) NOT NULL,
  PRIMARY KEY (`parent_id`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Child Table:

CREATE TABLE `child_table` (
  `child_id` int(11) NOT NULL AUTO_INCREMENT,
  `child_col_1` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`child_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

POJO:

@Entity
@Table(name = "parent_table")
public final class Parent {
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "parent_id")
    private long id;

    @Id
    @Column(name = "ex_column_1")
    private int exampleField;

    @Column(name = "ex_column_2")
    private int exampleField2;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "child_id", unique = true)
    private Child c;

    public Parent(/*params*/) {}

    // getters ...

    @Entity
    @Table(name = "child_table")
    public static final class Child {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "child_id")
        private long id;

        @Column(name = "child_col_1")
        private exampleChildField;

        public Child(/*params*/) {}

        // getters ...
    }
}

如何构造和保存 POJO 的实际示例:

@RestController
@RequestMapping("/parents")
public final class ParentController {
    private final ParentRepository parentRepository;

    @Inject
    public ParentController(final ParentRepository parentRepository) {
        parentRepository = parentRepository;
    }

    @RequestMapping(value = "", method=RequestMethod.PUT)
    public void updateParents(@RequestBody Parent[] parents) {

        // ignore input for now, test by constructing object

        Parent.Child c = new Parent.Child(0L, "test 1");
        Parent p = new Parent(0L, "unique_column_1", "column_2", c);
        Set<Parent> pSet = new HashSet<>();
        pSet.add(p);

       parentRepository.save(pSet);
    }
}

存储库层:

public interface ParentRepository extends CrudRepository<Parent, Long>, ParentRepositoryCustom {}

public interface ParentRepositoryCustom {
    void save(Set<Parent> parents);    
}

@Repository
final class ParentRepositoryImpl implements ParentRepositoryCustom {
    private final EntityManager entityManager;

    @Inject
    public EmployerRepositoryImpl(final EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Override
    @Transactional
    public void save(Set<Parent> parents) {
        parents.forEach(parent -> {
            Session session = (Session) entityManager.getDelegate();
            session.saveOrUpdate(parent);
        });
    }
}

如果 table 中的条目不存在,它会很好地保留两个实体并在 table 之间链接正确的 ID。如果再次插入相同的 parent 和 child object,就会出现此问题。 UPDATE 发生在 Parent table,但 INSERT 发生在 child:

Hibernate: insert into child_table (child_col_1) values (?)
Hibernate: insert into parent_table (ex_column_1, ex_column_2, child_id) values (?, ?, ?)

在第二次插入时:

Hibernate: insert into child_table (child_col_1) values (?)
Hibernate: update parent_table set ex_column_2=?, child_id=? where ex_column_1=? 

如果条目已经存在,我如何让 EntityManager 正确保留这些?

我想我发现了问题。默认情况下,用于 post new "parent" objects 到 rest 接口的客户端将主键 ids 设置为 0。这适用于父级,因为 ex_column_1 是已知的 ID当时对象是由其他一些机制创建的。

另一方面,Child 对象只有一个主键作为其 Id,通过将其设置为零,Spring 尝试根据 id 零查找相关的子行,并且始终如此如果子行已经存在,则插入而不是更新。使用 EntityManager.find() 方法对两个对象进行简单检查,然后保存或合并修复它。