我正在寻找一个干净的解决方案来拥有一个用于事务启动的监听器。这意味着我希望监听器成为spring上下文中的bean(组件),它将在事务启动时从TransactionPlatformManager或Hibernate Session或类似的东西接收一个事件,从而开始新的事务。
一些事情:
@Component
class TransactionListener implements ?? {
@Autowired
private Something x;
public void onTransactionBegin(...) {
x.doSomething()
}
}
具体来说,我正在缓解一个系统范围的问题,我需要在事务启动时设置一个本地线程,所以我可以在处理hibernate实体时进一步访问该线程以检索信息。
我查看了消息来源,发现没有迹象表明这样的听众是可以实现的。我找到的唯一解决方案是继承HibernateTransactionManager及其doBegin()方法,我觉得这个方法并不特别好。
答案 0 :(得分:1)
Spring在TransactionSynchronization中有一些事务回调,但正如你所注意到的那样,没有回调事务启动,我的错误。
据我所知,Spring在交易开始时不会让你知道,虽然这可能因不同的实施PlatformTransactionManager
而异。如果你想挂钩Spring事务,我相信你留下了
@Transactional
创建建议(这显然只有在使用注释时才有效)如果您正在使用Hibernate,那么https://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Interceptor.html#afterTransactionBegin(org.hibernate.Transaction)
中的afterTransactionBegin
可能会有运气
答案 1 :(得分:1)
我有一个类似的问题,我想在事务开始时立即记录Oracle会话ID,以便调查我们遇到的一些问题。
最后我发现,由于Spring使用PlatformTransactionManager
,因此您可以通过自定义访问所有信息。
要做的第一件事是确定您正在使用哪个实现。在我们的例子中,它是在JpaTransactionManager
类中声明的简单@Configuration
,所以很简单。
完成此操作后,请注意,您可以在此类上启用调试或跟踪日志记录,如果您的目标是调试问题,则该类已经提供了许多事务状态信息。
如果这还不够,可以很容易地将其子类化并替换上一个。然后只需覆盖您要拦截的方法,例如doBegin()
或prepareSynchronization()
。
例如查看我的实现:
@Slf4j
public class LoggingJpaTransactionManager extends JpaTransactionManager {
@Autowired
private EntityManager entityManager;
LoggingJpaTransactionManager(EntityManagerFactory emf) {
super(emf);
}
@Override
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
super.prepareSynchronization(status, definition);
if (status.isNewTransaction() && log.isInfoEnabled()) {
Query query = entityManager.createNativeQuery("select sys_context('USERENV','SID') from dual");
Object sessionId = query.getSingleResult();
log.info("Started a new transaction on session id {}", sessionId);
TransactionSynchronizationManager.registerSynchronization(…);
}
}
}
注意:我选择覆盖prepareSynchronization()
而不是doBegin()
,因为它允许使用TransactionSynchronizationManager
,我认为它可以更干净地通知提交/回退事件。无论如何,在doBegin()
之后立即调用此方法。
答案 2 :(得分:0)
到目前为止,这对我有用。
@Aspect
@Component
public class StartTransactionInterceptor {
@Pointcut("target(org.springframework.transaction.PlatformTransactionManager)")
public void isPlatformTransactionManager() {
}
@Pointcut("execution(org.springframework.transaction.TransactionStatus getTransaction("
+ "org.springframework.transaction.TransactionDefinition)))")
public void getsTransaction() {
}
@Around("isPlatformTransactionManager() && getsTransaction()")
public Object registerSynchronization(ProceedingJoinPoint joinPoint) throws Throwable {
TransactionStatus value = (TransactionStatus)joinPoint.proceed();
if (value.isNewTransaction()) {
// send some application event to others who are interested
}
return value;
}
}
或者,您可以使用Spring的SimpleTransactionScope
并将bean作为事务范围。当其他人在事务中调用DealWithStuffPerTx.addMoreStuff(Object)
时,bean会被懒惰地实例化。
@Configuration
public class TransactionScopeConfig implements BeanFactoryPostProcessor {
public static final String NAME = "tx";
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerScope(NAME, new SimpleTransactionScope());
}
}
@Component
@Scope(value = TransactionScopeConfig.NAME, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class DealWithStuffPerTx extends TransactionSynchronizationAdapter {
public void addMoreStuff(Object stuff) {
}
@Override
public void afterCommit() {
// deal with stuff
}
@PostConstruct
public void init() {
TransactionSynchronizationManager.registerSynchronization(this);
}
}