【发布时间】:2012-10-08 20:15:20
【问题描述】:
我在 Hibernate 版本中使用 JPA 2。 4.1.7.Final 作为 JPA 实现。我也在使用 Spring framework v. 3.1.2.RELEASE 来明确。这是我的问题。
我已经编写了一个方法来添加/更新我的用户实体。
@Override
@Transactional
public void saveUser(UserForm userForm) {
User user;
if (userForm.getId() == null) { // new user
user = new User();
user.setCreationDate(new Date());
entityManager.persist(user); // !!!
} else {
user = entityManager.find(User.class, userForm.getId());
}
user.setFirstName(userForm.getFirstName());
user.setLastName(userForm.getLastName());
user.setMiddleName(userForm.getMiddleName());
user.setEmail(userForm.getEmail());
user.setRole(entityManager.find(Role.class, 1));//TODO
user.setLogin(userForm.getLogin());
user.setPassword(userForm.getPassword1());
entityManager.flush();
}
我正在测试添加用户 (userForm.getId() == null)。而且上面的代码不起作用,报错:
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Column 'first_name' cannot be null
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1306)
at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:989)
...
但是。如果我在 flush() 之前将调用 persist() 移到最后,一切正常:
@Override
@Transactional
public void saveUser(UserForm userForm) {
User user;
if (userForm.getId() == null) { // new user
user = new User();
user.setCreationDate(new Date());
} else {
user = entityManager.find(User.class, userForm.getId());
}
user.setFirstName(userForm.getFirstName());
user.setLastName(userForm.getLastName());
user.setMiddleName(userForm.getMiddleName());
user.setEmail(userForm.getEmail());
user.setRole(entityManager.find(Role.class, 1));//TODO
user.setLogin(userForm.getLogin());
user.setPassword(userForm.getPassword1());
entityManager.persist(user);// !!!
entityManager.flush();
}
我认为发生的情况是在第一个(问题)情况下,它尝试存储在持久调用时存在的用户对象数据,尽管在 flush() 期间执行的实际保存(upd. 错误。它试图在persist() 调用时准确地发出sql insert)。让我产生这种想法的原因是,当我尝试调试时,我发现它仍然尝试在内部某处插入正确的 creation_date,但其他值是空值。
但我发誓,在我几年前从事的其他项目中,这工作得很好,尽管后来我使用了 Oracle 而不是 MySQL(我不认为这是原因)和旧版本的框架。
这可能是什么问题?也许 Hibernate 有一些配置选项会影响这个?
或者这是正确的行为和我对 JPA API 的误解?
UPD。我正在使用
@Id
@GeneratedValue
@Column(columnDefinition="INT")
private int id;
用于用户条目中的 id 字段。我相信这意味着生成策略= AUTO,适合mysql auto_increment key。
【问题讨论】:
-
听起来这可能是使用 IDENTITY 生成 PK 的问题。这就是这里使用的东西吗?
-
我同意如果 id 生成是 Identity 则需要立即插入。因此,如果可能,请切换到 Sequence 或 TableGenerator。如果不能,那么修改后的代码是正确的,但我会为 persist() 调用添加空检查以避免级联检查。
-
取决于您是否将
hibernate.id.new_generator_mappings设置为true(为了向后兼容,默认为false)。无论如何,如果@Transactional真的在做它的事情,那将是 Hibernate 中的一个错误。传统上,基于 IDENTITY 的 id 生成需要立即执行 INSERT 才能知道 id。但是,在这种情况下,Hibernate 应该尝试将 IDENTITY 的插入延迟到刷新(事务性 JPA 使用)。如果您想继续使用 IDENTITY 生成 id,您必须弄清楚原因;或启用hibernate.id.new_generator_mappings -
实际上,我收回了这一点,我们只在使用扩展持久性上下文的情况下延迟插入持久化。因此,根据您的解释,这可能不是您在这里所做的。所以插入应该发生在持久 with IDENTITY 上。
-
这引起了关于 JPA 规范中措辞的争论。它说持久化使实体“持久化”(如果您更喜欢该术语,则可以管理)。在其他地方,它部分地描述了一个实体是“持久的”,因为它引用了它的持久性标识符。在 IDENTITY 生成的情况下,我们作为持久性提供者(JPA 实现者)知道其 ID 的唯一方法是执行插入。恕我直言,这说明了从不将 IDENTITY 与更喜欢 Hibernate 和 JPA 的“事务性写入”的 O/RM 库一起使用的几个原因之一。
标签: java hibernate jpa jpa-2.0