今天同事遇到一个问题:在使用Spring的事务管理时,开启了一个事务,接着在内部开启了两个传播模式为嵌入(PROPAGATION_NESTED)的事务,想看下这三个事务的id是否一致,但是查了半天也没查到Spring怎么获取事务id。
这里提供两个思路,不知到对不对。
1 使用MySQL的事务id
既然Spring的事务最终还是要看MySQL,那我们不如直接去MySQL里查询事务id:
SELECT TRX_ID
FROM INFORMATION_SCHEMA.INNODB_TRX
WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID();
写到mapper.xml里
<select id="getCurrentTransactionId" resultType="string" >
SELECT TRX_ID
FROM INFORMATION_SCHEMA.INNODB_TRX
WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID();
</select>
在主事务方法上添加注解:@Transactional
在子事务方法上添加 :@Transactional(propagation = Propagation.NESTED)
在方法中调用获取事务id的方法并打印出来:
############## main transaction id is: 283244068130304.
############## sub nested transaction id is: 283244068130304.
查询数据库:
这里可能存在的问题:因为这个查询使用connection_id作为条件,还没搞明白这个连接ID怎么来的,原理还有点含糊,不建议用这种方法。
2 TransactionSynchronizationManager
使用:TransactionSynchronizationManager.getCurrentTransactionName()
获取事务名,名称是加了事务注解的方法的“包名+方法名”的全路径名称。
目前来看这种方法更靠谱。
String txName = TransactionSynchronizationManager.getCurrentTransactionName();
LogConstant.BUS.info("\n ############## main transaction name is: {}.", txName);
查看源码,是保存了一个ThreadLocal变量:
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<String>("Current transaction name");
spring的事务管理会在开启事务时往里塞值。
测试一下:
定义事务注解:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor = {Exception.class})
@Documented
public @interface RunInTransactional {
Propagation propagation() default Propagation.REQUIRED;
}
开启外层事务:
/**
* @Description 测试事务id
* @Author lilong
* @Date 2019-03-11 16:19
*/
@Service
public class TestTransactional {
@Autowired
private TestTransactionalPropagation testTransactionalPropagation;
@RunInTransactional
public void testTransactionalName() {
String txName = TransactionSynchronizationManager.getCurrentTransactionName();
LogConstant.BUS.info("\n ############ main transaction name is:{}.", txName);
testTransactionalPropagation.testNested();
testTransactionalPropagation.testRequired();
testTransactionalPropagation.testRequiresNew();
}
}
在上面方法的内部开启事务(注意在新的类文件里写下面两个方法,不要跟上面的方法在同一个类里,否则事务注解会失效):
/**
* @Description 测试事务传播机制
* @Author lilong
* @Date 2019-03-11 10:13
*/
@Service
public class TestTransactionalPropagation {
/**
* 嵌入式事务
*/
@RunInTransactional(propagation = Propagation.NESTED)
public void testNested() {
String txName = TransactionSynchronizationManager.getCurrentTransactionName();
LogConstant.BUS.info("\n ############## sub testNested transaction name is: {}.", txName);
}
/**
* REQUIRED
*/
@RunInTransactional(propagation = Propagation.REQUIRED)
public void testRequired() {
String txName = TransactionSynchronizationManager.getCurrentTransactionName();
LogConstant.BUS.info("\n ############## sub testRequired transaction name is: {}.", txName);
}
/**
* REQUIRES_NEW
*/
@RunInTransactional(propagation = Propagation.REQUIRES_NEW)
public void testRequiresNew() {
String txName = TransactionSynchronizationManager.getCurrentTransactionName();
LogConstant.BUS.info("\n ############## sub testRequiresNew transaction name is: {}.", txName);
}
}
打印结果:
############ main transaction name is:service.impl.TestTransactional.testTransactionalName.
############## sub testNested transaction name is: service.impl.TestTransactional.testTransactionalName.
############## sub testRequired transaction name is: service.impl.TestTransactional.testTransactionalName.
############## sub testRequiresNew transaction name is: service.impl.TestTransactionalPropagation.testRequiresNew.
这个方法我这边是好的,同事那边取到的事务名是null,暂时还没搞懂原因,估摸着是spring的事务框架在启动事务时往这个ThreadLocal变量里塞值了,我这边是用注解声明的方式使用的事务,同事那边用的是编程的方式,可能还是有差别,有时间再好好研究下。