代码
我的实体定义如下:
@Entity(name="setting")
@NamedQueries({
@NamedQuery(name=JpaSetting.FIND_BY_NAME,
query="SELECT s FROM setting s WHERE s.name = :name"),
})
public class JpaSetting {
// CONSTANTS \\
protected static final String FIND_BY_NAME = "JpaSetting.findByName";
protected static final String NAME_ATTR = "name";
// FIELDS \\
@Id @GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="id")
private int m_id;
@Column(name="name", nullable=false, unique=true)
private String m_name;
@Column(name="value")
private String m_value;
/* Classic getters and setters here... */
}
服务是:
public class SettingsService {
@PersistenceContext(type=PersistenceContextType.EXTENDED)
private EntityManager m_em;
private JpaSetting retrieveSetting(String p_name)
throws NoResultException {
return m_em.createNamedQuery(JpaSetting.FIND_BY_NAME, JpaSetting.class)
.setParameter(JpaSetting.NAME_ATTR, p_name)
.getSingleResult();
}
@Transactional
public void save(String p_name, String p_value) {
try {
m_em.merge(
retrieveSetting(p_name).setValue(p_value) );
}
catch (NoResultException e) {
m_em.persist(
new JpaSetting().setName(p_name).setValue(p_value) );
}
}
}
问题:在save()
方法中,m_em.persist()
不会在数据库中创建实体。生成实体的id(通过调试确认),我可以在Hibernate日志中看到select nextval('sequence')
请求,但是没有创建请求。
表setting
存在(使用生成的ddl创建),现在为空。我可以使用psql客户端插入和删除设置,并使用与应用程序使用的帐户相同的帐户。
没有抛出任何异常,一切似乎都正常工作。 除了实体没有坚持。
我在同一个应用程序中使用了另一个实体/服务(使用相同的持久性单元)persist()
有效,但我找不到明显的区别。
修改
我只是试图在数据库中创建记录以检查merge
情况,但它也不起作用。所以,这是一个更普遍的问题,但我不知道它在哪里。
答案 0 :(得分:1)
我找到了。
事实证明,save()
方法是由同一个类的另一个方法调用的, 没有用@Transactional
注释。
当我使用默认拦截模式proxy
时,所述代理无法拦截从非事务方法到@Transactional
的调用。因此,没有使用任何事务,并且提交从未发生过。
我通过将@Transactional
添加到类本身来解决它,因为它的所有公共方法都应该是事务性的。
答案 1 :(得分:0)
我不知道持久性是如何起作用的,try{ m_em.merge
在JPA过程中会产生异常。该异常很可能EJBTransactionRollback
与当前JPA堆栈关联的任何查询。
由于JPA不立即提交事务(它们是在最佳时间由提供者确定的稍后时间提交的),这就是为什么你看到它在调试模式下生成一个id的原因(虽然它永远不会被提交。)没有抛出异常的原因是因为你在JPA关闭事务时超出范围然后抛出EJBTransactionRollback
;在flush()
之后添加persist()
将导致事务正确提交。您应该在此时看到回滚发生并抛出异常。
如果我是你,使用save()
方法,我会使用您的retrieveSetting()
查询,如果没有结果,则persist()
。我认为问题在于将persist()
放在catch
块中,您不能期望回滚一个事务查询,并且提交同一事务中的另一个查询。