Hibernate/JPA-未正确调用实体侦听器

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

我试图在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 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
其余的回调没有按预期执行。这是正常的行为吗?我只是误解了吗?这是否与Seam管理事务的方式有关?或者,我只是做得不对吗

如果你能给我任何帮助,我将不胜感激

编辑:根据要求,以下是我正在调用的确切代码和收到的输出:

测试1:

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()