Hibernate/JPA-未正确调用实体侦听器
我试图在Seam/Hibernate/JPA应用程序中利用EntityListener对象和回调方法。我在JBoss 5.1上使用Seam 2.2管理的持久性上下文,后端使用PostgreSQL 9.1。我已声明以下实体:Hibernate/JPA-未正确调用实体侦听器,hibernate,jpa,callback,seam,entitylisteners,Hibernate,Jpa,Callback,Seam,Entitylisteners,我试图在Seam/Hibernate/JPA应用程序中利用EntityListener对象和回调方法。我在JBoss 5.1上使用Seam 2.2管理的持久性上下文,后端使用PostgreSQL 9.1。我已声明以下实体: @Entity(name = "TestEntity") @EntityListeners(TestCallback.class) @Table(name = "tbl_test") public class TestEntity implements Serializabl
@Entity(name = "TestEntity")
@EntityListeners(TestCallback.class)
@Table(name = "tbl_test")
public class TestEntity implements Serializable {
private static final long serialVersionUID = 2016897066783042092L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "xxx")
@SequenceGenerator(name = "xxx", sequenceName = "xxx")
@Index(name = "xxx")
@DocumentId
private Long id = null;
@Column
private String test = null;
...
}
以及以下EntityListener回调类:
public class TestCallback {
/**
* Logger for this class
*/
private Log logger = null;
public TestCallback() {
logger = Logging.getLog(TestCallback.class);
}
@PrePersist
public void prePersist(TestEntity e) {
logger.debug("prePersist(TestEntity) - start"); //$NON-NLS-1$
logger.debug("prePersist(TestEntity) - end"); //$NON-NLS-1$
}
@PostPersist
public void postPersist(TestEntity e) {
logger.debug("postPersist(TestEntity) - start"); //$NON-NLS-1$
logger.debug("postPersist(TestEntity) - end"); //$NON-NLS-1$
}
@PostLoad
public void postLoad(TestEntity e) {
logger.debug("postLoad(TestEntity) - start"); //$NON-NLS-1$
logger.debug("postLoad(TestEntity) - end"); //$NON-NLS-1$
}
@PreUpdate
public void preUpdate(TestEntity e) {
logger.debug("preUpdate(TestEntity) - start"); //$NON-NLS-1$
logger.debug("preUpdate(TestEntity) - end"); //$NON-NLS-1$
}
@PostUpdate
public void postUpdate(TestEntity e) {
logger.debug("postUpdate(TestEntity) - start"); //$NON-NLS-1$
logger.debug("postUpdate(TestEntity) - end"); //$NON-NLS-1$
}
@PreRemove
public void preRemove(TestEntity e) {
logger.debug("preRemove(TestEntity) - start"); //$NON-NLS-1$
logger.debug("preRemove(TestEntity) - end"); //$NON-NLS-1$
}
@PostRemove
public void postRemove(TestEntity e) {
logger.debug("postRemove(TestEntity) - start"); //$NON-NLS-1$
logger.debug("postRemove(TestEntity) - end"); //$NON-NLS-1$
}
}
然而,当我运行我的测试时,我并没有看到所有回调方法都像我预期的那样被调用。我已经运行了以下场景的测试:
- 保留新项目
- 更新现有项目
- 加载项目
- 删除项目
@PrePersist
@PreRemove
@PostLoad
@PreUpdate
public void runTest() {
logger.debug("runTest() - start"); //$NON-NLS-1$
TestEntity e = new TestEntity();
e.setTest("XXX");
this.entityManager.persist(e);
this.entityManager.flush();
this.entityManager.clear();
logger.debug("runTest() - end"); //$NON-NLS-1$
}
产出1:
12:27:56,307 INFO [STDOUT] 29735 DEBUG myapp.test.web.actions.test.TestAction - - runTest() - start
12:27:56,312 INFO [STDOUT] 29740 DEBUG myapp.test.entities.TestCallback - - prePersist(TestEntity) - start
12:27:56,312 INFO [STDOUT] 29740 DEBUG myapp.test.entities.TestCallback - - prePersist(TestEntity) - end
12:27:56,347 INFO [STDOUT] 29775 DEBUG myapp.test.web.actions.test.TestAction - - runTest() - end
测试2:
public void runTest2() {
logger.debug("runTest2() - start"); //$NON-NLS-1$
String sql = "SELECT DISTINCT t FROM TestEntity t";
Query q = this.entityManager.createQuery(sql);
List<TestEntity> l = q.getResultList();
for (int i = 0; i < l.size(); i++) {
String x = l.get(i).getTest();
logger.debug("runTest2() - String x=" + x); //$NON-NLS-1$
}
logger.debug("runTest2() - end"); //$NON-NLS-1$
}
测试3:
public void runTest3() {
logger.debug("runTest3() - start"); //$NON-NLS-1$
String sql = "SELECT DISTINCT t FROM TestEntity t";
Query q = this.entityManager.createQuery(sql);
List<TestEntity> l = q.getResultList();
for (int i = 0; i < l.size(); i++) {
l.get(i).setTest("YYY" + System.currentTimeMillis());
this.entityManager.persist(l.get(i));
}
this.entityManager.flush();
this.entityManager.clear();
Random rand = new SecureRandom();
q = this.entityManager.createQuery(sql);
l = q.getResultList();
for (int i = 0; i < l.size(); i++) {
this.entityManager.remove(l.get(i));
}
this.entityManager.flush();
this.entityManager.clear();
logger.debug("runTest3() - end"); //$NON-NLS-1$
}
如果没有您运行的实际测试、运行它们的方式以及(从侦听器)获得的输出,很难说有什么聪明的地方,但是使用MySQL处理下表和数据
创建表“测试”(
`id`int(11)
`数据`字符(1),
主键(`id`)
);
+----+------+
|id |数据|
+----+------+
|1 | a|
+----+------+
我做过这个测试
EntityManagerFactory emf=。。。
EntityManager em=emf.createEntityManager();
EntityTransaction tx=em.getTransaction();
//保留新项目
测试b=新测试();
b、 id=2;
b、 数据='b';
tx.begin();
em.b;
tx.commit();
//更新现有项目
b、 数据='a';
tx.begin();
em.merge(b);
tx.commit();
//现有荷载
测试a=em.find(测试1级);
//删除现有的
tx.begin();
em.remove(b);
tx.commit();
em.close();
emf.close();
并得到以下输出
prePersist(2:b)
后波斯特(2:b)
预更新(2:a)
假设更新(2:a)
后加载(1:a)
预拆除(2:a)
移除后(2:a)
(我在Test
中实现了toString()
,因此它发出id+:“+data
,并且我将侦听器类中的日志语句减少为System.out.format(“preXxx(%s)\n),e)
,以便更好地读取。)
对于初学者,请查看这是否适用于您的环境,以便我们缩小问题范围,但至少发布一些输入/输出
我试着为自己启动一个简单的JBoss/Seam测试应用程序来运行一个测试,但是我一点也不了解Seam。不管怎样,我看了一下你的例子,下面是我认为你的问题所在 测试1/输出1 我假设您的
runTest()
方法启动托管持久性会话或类似的东西,因此在调用该方法之前,Seam会调用entityManager.getTransaction().begin()
,当该方法存在时,会调用entityManager.getTransaction().commit()。以下是我对您的场景中可能发生的情况的看法
// transaction begin
// persist - @PrePersist called
this.entityManager.persist(e);
// flush - @PostPersist should be called
this.entityManager.flush();
// clear - the context is cleared
this.entityManager.clear();
// transaction end
我已经测试过这个场景,但是使用MySQL和手动持久性管理@PostPersist
被调用
我尝试将entityManager.setFlushMode(…)
设置为FlushModeType.AUTO
和FlushModeType.COMMIT
,但没有任何更改。然而,在Seam文档中有第三个枚举,它不在JPA规范中,并且是特定于Seam的。它应该被提供给注释(这个注释可能表示注释的方法启动了手头的事务)
我很确定,在您的情况下,flush()
在事务结束(方法用完)之前不会向数据库提交任何内容。如果删除对clear()
方法的调用,则应调用@PostPersist
测试2/输出2
我觉得这很好
测试3/输出3
我假设您的问题是,@PrePersist
和@postersist
根本没有被调用,但是您调用了persist()
。因为您使用了find()
,所以返回的实例将被管理。对它们调用persist()
,没有任何作用。但是,改变它们的状态并调用flush()
会触发@PreUpdate
@PostUpdate
未被调用的原因与第一次测试中未被调用的原因完全相同:@PostPersist
在任何内容传播到数据库之前清除持久性上下文
结论
您的问题归结为这样一个事实:在将任何内容提交到数据库之前,您必须清除持久性上下文
我建议您进行一些非常简单的测试,并尝试使用我提到的FlushModeType
设置。更新你的Hibernate版本。我使用3.x.x系列的最新版本(3.8.6.Final)。顺便说一句,你并没有和我们分享你的Hibernate版本,但这可能很重要 这不是正常的行为。我在一个使用seam 2.2.2和seam托管持久性上下文、jpa/hibernate、jboss 5.1和mysql的项目中工作,所有回调都被正确调用。
可能您的测试用例中有一个bug。如果我给出了错误的答案,很抱歉。。。我不知道
但在你的主题中,你说的是“Hibernate/JPA”,这是不清楚的。
是否使用SessionFactory中的会话或EntityManager工厂中的EntityManager持久化实体
这是一个很大的区别,因为如果将Seam与SessionFactory一起使用,最大的区别在于默认情况下,JPA侦听器(触发带注释的回调)在使用
12:30:00,404 INFO [STDOUT] 153832 DEBUG myapp.test.web.actions.test.TestAction - - runTest3() - start
12:30:00,407 INFO [STDOUT] 153835 DEBUG myapp.test.entities.TestCallback - - postLoad(TestEntity) - start
12:30:00,407 INFO [STDOUT] 153835 DEBUG myapp.test.entities.TestCallback - - postLoad(TestEntity) - end
12:30:00,408 INFO [STDOUT] 153836 DEBUG myapp.test.entities.TestCallback - - preUpdate(TestEntity) - start
12:30:00,408 INFO [STDOUT] 153836 DEBUG myapp.test.entities.TestCallback - - preUpdate(TestEntity) - end
12:30:00,410 INFO [STDOUT] 153838 DEBUG myapp.test.entities.TestCallback - - postLoad(TestEntity) - start
12:30:00,411 INFO [STDOUT] 153839 DEBUG myapp.test.entities.TestCallback - - postLoad(TestEntity) - end
12:30:00,414 INFO [STDOUT] 153842 DEBUG myapp.test.entities.TestCallback - - preRemove(TestEntity) - start
12:30:00,414 INFO [STDOUT] 153842 DEBUG myapp.test.entities.TestCallback - - preRemove(TestEntity) - end
12:30:00,453 INFO [STDOUT] 153881 DEBUG myapp.test.web.actions.test.TestAction - - runTest3() - end
// transaction begin
// persist - @PrePersist called
this.entityManager.persist(e);
// flush - @PostPersist should be called
this.entityManager.flush();
// clear - the context is cleared
this.entityManager.clear();
// transaction end
EntityManager em = ...
Session session = (Session)em.getDelegage()
SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl)session.getSessionFactory();
EventListeners el = sessionFactoryImpl.getEventListeners()
el.getPreLoadEventListeners()
el.getPreDeleteEventListeners()