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() 方法对两个对象进行简单检查,然后保存或合并修复它。
我正在尝试使用 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() 方法对两个对象进行简单检查,然后保存或合并修复它。