JDBC的使用及Spring + JDBC:
SpringJDBC源码解析 (看一下demo就行,源码不用看)
首先,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();
}
}
spring的事物主要是通过PlatformTransactionManager 事务管理器来实现,事务管理器主要有三个API:
此外,spring定义了事物的传播行为:
掌握:PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED
(跟着2.申明式事务的图走就行,从@EnableTransactionManagement开始看)
spring事务详解(三)源码详解
关于ThreadLocal,详见并发编程笔记中的第九点ThreadLocal部分:并发编程笔记:https://blog.csdn.net/bintoYu/article/details/86527400
事物Transaction有下面两个需求:
spring借助ThreadLocal完美实现了这一机制。代码如下:
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) {
...
}
}
从下面的代码中可以看到,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);
...
}