Java 事务启动时的侦听器
我正在寻找一个干净的解决方案,为事务启动提供一个侦听器。这意味着我希望侦听器是spring上下文中的一个bean(组件),它将在新事务启动时从TransactionPlatformManager或Hibernate会话或类似的地方接收事务启动事件 一些事情:Java 事务启动时的侦听器,java,spring,hibernate,spring-transactions,hibernate-session,Java,Spring,Hibernate,Spring Transactions,Hibernate Session,我正在寻找一个干净的解决方案,为事务启动提供一个侦听器。这意味着我希望侦听器是spring上下文中的一个bean(组件),它将在新事务启动时从TransactionPlatformManager或Hibernate会话或类似的地方接收事务启动事件 一些事情: @Component class TransactionListener implements ?? { @Autowired private Something x; public void onTransac
@Component
class TransactionListener implements ?? {
@Autowired
private Something x;
public void onTransactionBegin(...) {
x.doSomething()
}
}
具体来说,我正在缓解一个系统范围的问题,我需要在事务启动时设置一个本地线程,以便在处理hibernate实体以检索信息时进一步访问该本地线程
我调查了资料来源,没有发现这样的聆听者是可以做到的。我找到的唯一解决方案是将HibernateTransactionManager及其doBegin()方法子类化,我觉得这不是特别好。Spring在其中有一些事务回调,但是正如您正确地注意到的,事务启动没有回调,这是我的错误 据我所知,Spring不会让您知道事务何时开始,尽管这可能因不同的实现而有所不同
PlatformTransactionManager
。如果你想参与Spring交易,我相信你只剩下
@Transactional
创建一个建议(显然,这仅在使用注释时有效)如果您使用的是Hibernate,您可能会幸运地使用中的
afterTransactionBegin
,到目前为止,这对我很有效
@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.addMoreTuff(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);
}
}
我遇到了一个类似的问题,我希望在事务启动时立即记录Oracle会话id,以便调查我们遇到的一些问题 最后我发现,由于Spring使用了
PlatformTransactionManager
,您可以通过定制来访问所有信息
首先要做的是确定您正在使用的实现。在我们的例子中,它是在@Configuration
类中声明的一个简单的JpaTransactionManager
,因此非常简单
完成此操作后,请注意,您可以在此类上启用调试或跟踪日志记录,如果您的目标是调试问题,则该类已经提供了大量事务状态信息
如果这还不够,可以很容易地将其子类化并替换上一个。然后只需覆盖您想要拦截的方法,如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()
之后会立即调用此方法。将通知除事务开始之外的所有事件。最简单的方法可能是为PlatformTransactionManager
创建一个包装器,并在3个方法调用中激发这些事件。但是你为什么要注册这个?你想做什么?你提到解决问题,但这样做似乎有点奇怪。我面临的问题与此无关,它是由我们的大型多模块体系结构引起的。如果我可以选择对架构做出决策,我会用不同的方式来做。但是在这个遗留体系结构中,如果没有沉浸式重写,这是不可能的,这就是我需要它的原因。方面解决方案看起来不错,但不幸的是,它只适用于JDK代理,而不适用于CGLIB类代理,因为AbstractPlatformTransactionManager
中有许多final
方法,在您尝试启动事务时立即导致NPE。据我所知,第二种方法无法全局拦截事务,它只适用于调用addMoreStuff()
的代码。