【问题标题】:Hibernate afterAfterTransactionCompletion interceptor休眠 afterAfterTransactionCompletion 拦截器
【发布时间】:2013-07-09 09:01:54
【问题描述】:

有没有办法监听事件 *after*AfterTransactionCompletion (2 time after) ?

我正在开发一个用户可以修改实体并保存它的应用程序:

tx.begin();
entity.name = "Archimede";
em.merge(entity);
tx.commit();

我想拦截提交,并“纠正”实体。假设:如果 name.equal("Archimede") 那么拦截器应该更改 surname = "Pitagorico"。

public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[]     previousState, String[] propertyNames, Type[] types) {
    changedEntities.add(entity); //register all changing entities
}

public void afterTransactionCompletion(Transaction tx) {
    for(T entity : changedEntities) {
        if(entity.name.euqual("Archimede")) {
            em.getTransaction().begin();
            entity.surname = "Pitagorico";
            sendNotificationToUser();
            em.getTransaction().commit();
        }
    }
}

问题是不允许在拦截器中使用 entityManager 或事务,这将失败并显示如下内容:

org.hibernate.TransactionException: reuse of Transaction instances not supported

有罪的人在这里(org.hibernate.engine.transaction.spi.AbstractTransactionImpl):

@Override
public void commit() throws HibernateException {
    if ( localStatus != LocalStatus.ACTIVE ) {
        throw new TransactionException( "Transaction not successfully started" );
    }

    LOG.debug( "committing" );

    beforeTransactionCommit();

    try {
        doCommit();
        localStatus = LocalStatus.COMMITTED;
        afterTransactionCompletion( Status.STATUS_COMMITTED ); //<-------
    }
    catch ( Exception e ) {
        localStatus = LocalStatus.FAILED_COMMIT;
        afterTransactionCompletion( Status.STATUS_UNKNOWN );
        throw new TransactionException( "commit failed", e );
    }
    finally {
        invalidate(); //<-------
        afterAfterCompletion(); //<-------
    }
}

拦截器由afterTransactionCompletion(Status.STATUS_COMMITTED);调用,但这是在invalidate();之前,事务仍处于COMMIT状态。 就在下一行它调用 afterAfterCompletion();,此时事务应该是有效的并且准备好.begin()

我想这里有一个拦截器方法,所以前面的代码应该没有问题。

有人知道方法吗?还是我的方法不对? (有更好的吗?)

【问题讨论】:

  • 你为什么不使用@PrePersist注解?
  • 同样在@PrePersist 中我不应该使用EntityManager 或Transaction,并且我只想在事务实际提交时(而不是回滚)“更正”实体。此外,交易不应该依赖于“更正”(如果更正失败,只是更正交易应该失败,而不是第一个)。它们应该是两个独立的事务。
  • 为什么我有一种看到并发代码不安全的感觉?将实体作为实例字段会使您变得糟糕。你怎么看?
  • 为什么不在 onFlushDirty 期间更正实体?

标签: java hibernate jpa interceptor


【解决方案1】:

我会使用一些 AOP 并会使用 @Transactions

例如:

@Transactional(propagation = Propagation.REQUIRES_NEW) 
public MyEntity doSaveService(MyEntity myentity){
  em.merge(myentity); // not required but easier to read
  em.flush(); //not sure if you need it
} 

在此合并方法上添加一些 AOP(根据需要添加一些注释或 XML 声明)

@Transactional(propagation = Propagation.REQUIRES_NEW)
protected Object postMergeProcess(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
 MyEntity myEntityBeforeMerge = (MyEntity ) proceedingJoinPoint.getArgs()[0];
 MyEntity myEntityAfterMerge = (MyEntity ) proceedingJoinPoint.proceed();
 myEntityAfterMerge.setWhatever("xxx");
 em.merge();
}

XML:

<aop:config>
  <aop:aspect ref="">
    <aop:pointcut id="mergePointCut" expression="execution(* x.y.z.EntityService.merge(..))" />
            <aop:around method="postMergeProcess" pointcut-ref="mergePointCut" />
 </aop:aspect>
</aop:config>

【讨论】:

  • 谢谢,这可能有效。但是我没有获得合并方法,我不能只绑定关于 EntityManager#merge() 方法的建议吗?我必须对 tx.commit() 做出反应,但原理是一样的。我正在使用 google guice,我希望它的 AOP 就足够了。我现在唯一能想到的问题是托管实体更新,如果我没有明确调用 merge(),有没有办法强迫我这样做?
  • 编辑:我用一个明确的名字改变了合并方法。 AOP 拦截应该在您的服务层或类似的东西上完成。您也可以尝试使用 Entity.merge。我不确定这种情况下的行为。
  • 我已经包装了 EntityManager,所以我可以拦截persist、merge、remove 和 trasaction.commit 但是,我刚刚意识到,拦截合并而不是 onFlushDirty 我会丢失 currentState、previousState 和 propertyNames,所以现在我不知道哪个属性发生了变化。 (合并甚至并不意味着实体完全改变了)。有什么想法吗?
  • 在我的示例中,您有一个 myEntityBeforeMerge 和 myEntityAfterMerge。因此,您可以检索已更改的属性。否则我不知道没有真正的源代码。
  • 如果我没看错的话,myEntityBeforeMerge 是合并前的实体(但要保存修改),而 myEntityAfterMerge 是托管实体,所以内容是一样的。在任何用户修改(数据库上的状态)之前,我需要原始实体状态。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-25
  • 1970-01-01
  • 1970-01-01
  • 2014-02-02
  • 1970-01-01
  • 2013-07-03
相关资源
最近更新 更多