【发布时间】:2015-02-11 16:15:19
【问题描述】:
我正在尝试使用 Spring JPA/Hibernate 来维护一个父表和一个基于 Id 相关的子表。如果我尝试两次插入同一个对象,则父表会正确“更新”,但即使子表中的条目已经存在,子表也会始终插入。
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;
子表:
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);
});
}
}
如果任一表中的条目不存在,它会很好地保留两个实体并在表之间链接正确的 Id。如果再次插入相同的父对象和子对象,则会出现此问题。 UPDATE 发生在 Parent 表上,但 INSERT 发生在子表上:
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 正确保存这些内容?
【问题讨论】:
-
你有一个奇怪的数据库。是否每个父母只有 1 个孩子,在这种情况下应该是 OneToOne 而不是 ManyToOne。
-
我已将示例分解为尽可能简单,但在实际用例中,子表中的一个条目可能有多个父表条目。
-
发布您创建要持久化实体的代码。
-
你能展示一些你用来插入和更新分贝的代码吗?最好是 JUnit 测试。
-
@AlanHay 确实没有太多代码可用于创建实体。这些对象是使用一些虚假数据创建的。我将更新我的示例以显示它们是如何构建的。我有一个惊人的怀疑,因为子 id 为 0,Spring 尝试查询该表以查找 ID 为 0 的行并且找不到它,从而执行插入。正确的问题可能是,如何正确查询第二个表,以便更新而不是作为新行插入?
标签: java hibernate jpa spring-data-jpa