1 预备知识
JDBC的使用及Spring + JDBC:
SpringJDBC源码解析 (看一下demo就行,源码不用看)
2 事物示例:
首先,config类上需要添加注解@EnableTransactionManagement:
@EnableTransactionManagement
@Configuration
public class JDBCConfig {
接着,类中需要配置事物管理器作为一个Bean:
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(DruidDataSource druidDataSource) {
return new DataSourceTransactionManager(druidDataSource);
}
测试用例:
@Transactional
public class JDBCServiceImpl implements JDBCService{
private JdbcTemplate jdbcTemplate;
public JDBCServiceImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void testTransactional() {
jdbcTemplate.update("update user set name='王五' where id=1", new Object[]{});
throw new RuntimeException("异常");
}
public class JDBCEnter {
public static void main (String args[]){
ApplicationContext context = new AnnotationConfigApplicationContext("com.test.jdbc");
JDBCService jdbcService= context.getBean(JDBCService.class);
jdbcService.testTransactional();
}
}
3 spring 事物机制
spring的事物主要是通过PlatformTransactionManager 事务管理器来实现,事务管理器主要有三个API:
- getTransaction(TransactionDefinition definition) ,获得事物。
- commit(TransactionStatus status) 根据状态提交
- rollback(TransactionStatus status) 根据状态回滚
此外,spring定义了事物的传播行为:
- PROPAGATION_REQUIRED , required , 必须 【默认值】
支持当前事务,A如果有事务,B将使用该事务。
如果A没有事务,B将创建一个新的事务。 - PROPAGATION_SUPPORTS ,supports ,支持
支持当前事务,A如果有事务,B将使用该事务。
如果A没有事务,B将以非事务执行。 - PROPAGATION_MANDATORY,mandatory ,强制
支持当前事务,A如果有事务,B将使用该事务。
如果A没有事务,B将抛异常。 - PROPAGATION_REQUIRES_NEW , requires_new ,必须新的
如果A有事务,将A的事务挂起,B创建一个新的事务
如果A没有事务,B创建一个新的事务 - PROPAGATION_NOT_SUPPORTED ,not_supported ,不支持
如果A有事务,将A的事务挂起,B将以非事务执行
如果A没有事务,B将以非事务执行 - PROPAGATION_NEVER ,never,从不
如果A有事务,B将抛异常
如果A没有事务,B将以非事务执行 - PROPAGATION_NESTED ,nested ,嵌套
A和B底层采用保存点机制,形成嵌套事务。
掌握:PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED
4 源码部分:
(跟着2.申明式事务的图走就行,从@EnableTransactionManagement开始看)
spring事务详解(三)源码详解
5 spring Transaction 使用ThreadLocal管理Connection
关于ThreadLocal,详见并发编程笔记中的第九点ThreadLocal部分:并发编程笔记:https://blog.csdn.net/bintoYu/article/details/86527400
事物Transaction有下面两个需求:
- 要求在一个方法内的所有方法都执行或都回滚,因此要确保某个线程在执行事物方法时,connection始终不变。
- 为了保证效率,可以让不同线程使用不同的connection。
spring借助ThreadLocal完美实现了这一机制。代码如下:
debegin()
PlatformTransactionManager 的doBegin()会获取数据库链接Connection,如果connection是新创建的,则将其绑定到ThreadLocal中。
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;
Connection con = null;
try {
//1. 当前connection为空时,从dataSource里取出一个。
if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = this.dataSource.getConnection();
...
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
//2.对connection进行一些预处理,代码略
...
//3. 如果是新的connection,则调用bindResouce()保存链接
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(this.getDataSource(), txObject.getConnectionHolder());
}
} catch (Throwable var7) {
...
}
}
bindResource()
从下面的代码中可以看到,resources是一个ThreadLocal,存放的是一个Map结构,map中的key是dataSource,value是connection。这样就将connection存放到线程的ThreadLocal中了。
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<Map<Object, Object>>("Transactional resources");
public static void bindResource(Object key, Object value) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Assert.notNull(value, "Value must not be null");
//1. 从ThreadLocal中取出map
Map<Object, Object> map = resources.get();
//2. map为空的话,new一个并设置到ThreadLocal中
if (map == null) {
map = new HashMap<Object, Object>();
resources.set(map);
}
//3. 将connection和dataSource放到map中
Object oldValue = map.put(actualKey, value);
...
}