我正在尝试在我的Seam / Hibernate / JPA应用程序中利用EntityListener对象和回调方法。我在后端使用带有PostgreSQL 9.1的JBoss 5.1上的Seam 2.2管理持久化上下文。我声明了以下实体:
@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$
}
输出2:
12:28:36,964 INFO [STDOUT] 70392 DEBUG myapp.test.web.actions.test.TestAction - - runTest2() - start
12:28:36,982 INFO [STDOUT] 70410 DEBUG myapp.test.entities.TestCallback - - postLoad(TestEntity) - start
12:28:36,982 INFO [STDOUT] 70410 DEBUG myapp.test.entities.TestCallback - - postLoad(TestEntity) - end
12:28:36,982 INFO [STDOUT] 70410 DEBUG myapp.test.web.actions.test.TestAction - - runTest2() - String x=XXX
12:28:36,983 INFO [STDOUT] 70411 DEBUG myapp.test.web.actions.test.TestAction - - runTest2() - end
测试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$
}
输出3:
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
答案 0 :(得分:8)
对不起,如果我给出了错误答案......我不知道Seam。
但在您的主题中,您说“Hibernate / JPA”尚不清楚。 您是使用SessionFactory中的Session或EntityManagerFactory中的EntityManager来持久保存实体吗?
有一个很大的区别,因为如果你将Seam与SessionFactory一起使用,那么最大的区别在于默认情况下JPA监听器(触发带注释的回调)在默认情况下不会被注册,而它们与EntityManagerFactory一起使用。 因此,您可能正在使用SessionFactory,并且您的项目中配置了该会话工厂的其他人只注册了所有JPA回调侦听器的子集。
请参阅: http://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/configuration.html#d0e865
编辑: 好的抱歉,您使用的是EntityManager ...
但也许最好尝试将SessionFactory放在EntityManagerFactory后面,并查看注册了哪些事件监听器。那个申请你一个人吗?如果有人试图注册自定义/遗留事件监视器或其他东西,他可能已经覆盖了JPA事件监视器。
这可以通过以下方式实现:
EntityManager em = ...
Session session = (Session)em.getDelegage()
SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl)session.getSessionFactory();
EventListeners el = sessionFactoryImpl.getEventListeners()
然后你可以看看里面的东西,例如,根据你的问题,你可以比较:
el.getPreLoadEventListeners()
el.getPreDeleteEventListeners()
请记住“默认行为”是: http://docs.jboss.org/hibernate/stable/entitymanager/reference/en/html_single/#d0e865
似乎可以轻松覆盖JPA默认侦听器,请参阅: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/listeners.html
如果你向我们展示你的persistence.xml
,那就太好了答案 1 :(得分:3)
如果没有你运行的实际测试,你运行它们的方式和你得到的输出(来自你的听众)很难说聪明,但是使用带有下表和数据的MySQL
CREATE TABLE `test` (
`id` int(11)
`data` char(1),
PRIMARY KEY (`id`)
);
+----+------+
| id | data |
+----+------+
| 1 | a |
+----+------+
我已经运行了这个测试
EntityManagerFactory emf = ...
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
// persisting new item
Test b = new Test();
b.id = 2;
b.data = 'b';
tx.begin();
em.persist(b);
tx.commit();
// update existing item
b.data = 'a';
tx.begin();
em.merge(b);
tx.commit();
// load existing
Test a = em.find(Test.class, 1);
// remove existing
tx.begin();
em.remove(b);
tx.commit();
em.close();
emf.close();
并获得以下输出
prePersist(2: b)
postPersist(2: b)
preUpdate(2: a)
postUpdate(2: a)
postLoad(1: a)
preRemove(2: a)
postRemove(2: a)
(我在toString()
中实现了Test
,因此它会发出id + ": " + data
并且我已将我的侦听器类中的日志记录语句缩减为System.out.format("preXxx(%s)\n", e)
,以便它会被更好地阅读。)
请查看此是否适合您的环境中的初学者,以便我们可以缩小问题范围,但至少会发布一些输入/输出。
我尝试为自己启动一个简单的JBoss / Seam测试应用程序来运行测试,但是我根本没有得到Seam ......无论如何,我看了你的例子,这就是我认为你的问题。
我假设您的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
我已经测试了这种情况,但是使用带有 manual 持久性管理@PostPersist
的MySQL会被调用。
我已尝试将entityManager.setFlushMode(...)
设置为FlushModeType.AUTO
和FlushModeType.COMMIT
,但它没有任何变化。但是在Seam文档中有第三个枚举FlushModeType.MANUAL
,它不在JPA规范中,并且是Seam特有的。它应该 fed 到@Begin
注释(这个注释可能表示带注释的方法启动手头的事务)。
我很确定在你的情况下flush()
在事务结束之前不会向数据库提交任何内容(方法运行 out )。如果您删除了对clear()
方法的调用,则应调用@PostPersist
。
这对我来说似乎没问题!
我认为您的问题是@PrePersist
和@PostPersist
根本没有被调用,但是您调用了persist()
。因为您使用find()
,所以已经管理了返回的实例。在他们上面调用persist()
什么都不做。但是,改变他们的状态并调用flush()
触发@PreUpdate
。 @PostUpdate
没有被调用完全相同的原因@PostPersist
在第一次测试中未被调用:在任何东西传播到数据库之前清除持久化上下文。
您的问题归结为这样一个事实:在将任何内容提交到数据库之前,您clear()
了您的持久化上下文。
我建议您进行一些真正的简单测试,然后尝试使用我提到的FlushModeType
设置。更新您的Hibernate版本。我使用3.x.x系列(3.8.6.Final)的最新版本。顺便说一句,你并没有完全与我们分享你的Hibernate版本,但它可能很重要!
答案 2 :(得分:2)
这不是正常行为。我正在研究一个使用seam 2.2.2与seam管理持久化上下文,jpa / hibernate,jboss 5.1和mysql的项目,并正确调用所有回调。 也许您的测试用例存在错误。