交易开始时的监听器
Listener on transaction start
我正在寻找一个干净的解决方案来为事务启动提供一个侦听器。这意味着我希望侦听器是 spring 上下文中的一个 bean(组件),它会在新事务开始时从 TransactionPlatformManager 或 Hibernate Session 或类似的东西接收事务开始的事件。
一些事情:
@Component
class TransactionListener implements ?? {
@Autowired
private Something x;
public void onTransactionBegin(...) {
x.doSomething()
}
}
具体来说,我正在缓解一个系统范围的问题,我需要在事务开始时设置一个本地线程,这样我就可以在处理休眠实体时进一步访问该本地线程以检索信息。
我查看了资源,没有找到可以实现此类侦听器的痕迹。我找到的唯一解决方案是子类化 HibernateTransactionManager 及其 doBegin() 方法,我觉得这不是特别好。
Spring 在其 TransactionSynchronization 中有一些交易回调,但是正如您正确地注意到的那样,交易开始时没有回调,我的错误。
据我所知,Spring 不会让您知道事务何时开始,尽管这可能因不同的实现而异 PlatformTransactionManager
。如果你想挂入Spring事务,相信你还剩下
- 子类化事务管理器并调用一些回调
- 使用 spring-aop 为
@Transactional
创建建议(显然,这只有在使用注释时才有效)
如果您使用的是 Hibernate,您可能会在 https://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Interceptor.html#afterTransactionBegin(org.hibernate.Transaction)
中使用 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.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);
}
}
我遇到了类似的问题,我想在事务开始后立即记录 Oracle 会话 ID,以便调查我们遇到的一些问题。
最后我想,由于Spring使用PlatformTransactionManager
,您可以通过自定义访问所有信息。
首先要做的是确定您使用的是哪种实现。在我们的例子中,它是在 @Configuration
class 中声明的简单 JpaTransactionManager
,所以它相当容易。
完成此操作后,请注意您可以在此 class 上启用调试或跟踪日志记录,如果您的目标是调试问题,它已经提供了大量事务状态信息。
如果这还不够,很容易将其子class 并替换上一个。然后只需覆盖您想要拦截的方法,如 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
,我认为它仍然更清晰,可以收到 commit/rollback 事件通知.这个方法在 doBegin()
之后立即被调用。
我正在寻找一个干净的解决方案来为事务启动提供一个侦听器。这意味着我希望侦听器是 spring 上下文中的一个 bean(组件),它会在新事务开始时从 TransactionPlatformManager 或 Hibernate Session 或类似的东西接收事务开始的事件。
一些事情:
@Component
class TransactionListener implements ?? {
@Autowired
private Something x;
public void onTransactionBegin(...) {
x.doSomething()
}
}
具体来说,我正在缓解一个系统范围的问题,我需要在事务开始时设置一个本地线程,这样我就可以在处理休眠实体时进一步访问该本地线程以检索信息。
我查看了资源,没有找到可以实现此类侦听器的痕迹。我找到的唯一解决方案是子类化 HibernateTransactionManager 及其 doBegin() 方法,我觉得这不是特别好。
Spring 在其 TransactionSynchronization 中有一些交易回调,但是正如您正确地注意到的那样,交易开始时没有回调,我的错误。
据我所知,Spring 不会让您知道事务何时开始,尽管这可能因不同的实现而异 PlatformTransactionManager
。如果你想挂入Spring事务,相信你还剩下
- 子类化事务管理器并调用一些回调
- 使用 spring-aop 为
@Transactional
创建建议(显然,这只有在使用注释时才有效)
如果您使用的是 Hibernate,您可能会在 https://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Interceptor.html#afterTransactionBegin(org.hibernate.Transaction)
中使用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.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);
}
}
我遇到了类似的问题,我想在事务开始后立即记录 Oracle 会话 ID,以便调查我们遇到的一些问题。
最后我想,由于Spring使用PlatformTransactionManager
,您可以通过自定义访问所有信息。
首先要做的是确定您使用的是哪种实现。在我们的例子中,它是在 @Configuration
class 中声明的简单 JpaTransactionManager
,所以它相当容易。
完成此操作后,请注意您可以在此 class 上启用调试或跟踪日志记录,如果您的目标是调试问题,它已经提供了大量事务状态信息。
如果这还不够,很容易将其子class 并替换上一个。然后只需覆盖您想要拦截的方法,如 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
,我认为它仍然更清晰,可以收到 commit/rollback 事件通知.这个方法在 doBegin()
之后立即被调用。