Map map = resources.get();
//通过map拿到事务管理器开启的连接
Connection conn = map.get(dataSource1);
//通过conn提交事务
conn.commit();
//管理连接
conn.close();
16、清理ThreadLocal中的连接:通过map.remove(dataSource1)将连接从resource ThreadLocal中移除
17、清理事务
从上面代码中可以看出:整个过程中有2个地方需要用到数据库连接Connection对象,第1个地方是:spring事务拦截器启动事务的时候会从datasource中获取一个连接,通过这个连接开启事务手动提交,第2个地方是:最终执行sql操作的时候,也需要用到一个连接。那么必须确保这两个连接必须是同一个连接的时候,执行sql的操作才会受spring事务控制,那么如何确保这2个是同一个连接呢?从代码中可以看出必须让事务管理器中的datasource和JdbcTemplate中的datasource必须是同一个,那么最终2个连接就是同一个对象。
这里顺便回答一下群友问的一个问题:什么是事务挂起操作?
这里以事务传播行为REQUIRED_NEW为例说明一下,REQUIRED_NEW表示不管当前事务管理器中是否有事务,都会重新开启一个事务,如果当前事务管理器中有事务,会把当前事务挂起。
所谓挂起,你可以这么理解:对当前存在事务的现场生成一个快照,然后将事务现场清理干净,然后重新开启一个新事务,新事务执行完毕之后,将事务现场清理干净,然后再根据前面的快照恢复旧事务。
下面我们再回到本文的内容,多数据源事务管理。
简化版的过程如下:
Map map=resource的ThreadLocal.get();
DataSource datasource = transactionManager.getDataSource();
Connection conn = map.get(datasource);
//如果conn不为空,就表示当前有事务
if(conn!=null){
}
从这段代码可以看出:判断是否存在事务,主要和datasource有关,和事务管理器无关,即使是不同的事务管理器,只要事务管理器的datasource是一样的,那么就可以发现当前存在的事务。
事务管理器的运行过程和如何判断是否有事务,这2点大家一定要理解,这个理解了,后面的案例理解起来会容易很多。
下面上案例。
git地址:
https://gitee.com/javacode2018/spring-series
本文案例对应源码:
案例1:spring-series\lesson-002-tx\src\main\java\com\javacode2018\tx\demo7
案例2:spring-series\lesson-002-tx\src\main\java\com\javacode2018\tx\demo8
准备案例代码
1、准备db
2个数据库:ds1、ds2
每个库中2个表:user1、user2
DROP DATABASE IF EXISTS ds1;
CREATE DATABASE if NOT EXISTS ds1;
USE ds1;
DROP TABLE IF EXISTS user1;
CREATE TABLE user1(
id int PRIMARY KEY AUTO_INCREMENT,
name varchar(64) NOT NULL DEFAULT ‘’ COMMENT ‘姓名’
);
DROP TABLE IF EXISTS user2;
CREATE TABLE user2(
id int PRIMARY KEY AUTO_INCREMENT,
name varchar(64) NOT NULL DEFAULT ‘’ COMMENT ‘姓名’
);
DROP DATABASE IF EXISTS ds2;
CREATE DATABASE if NOT EXISTS ds2;
USE ds2;
DROP TABLE IF EXISTS user1;
CREATE TABLE user1(
id int PRIMARY KEY AUTO_INCREMENT,
name varchar(64) NOT NULL DEFAULT ‘’ COMMENT ‘姓名’
);
DROP TABLE IF EXISTS user2;
CREATE TABLE user2(
id int PRIMARY KEY AUTO_INCREMENT,
name varchar(64) NOT NULL DEFAULT ‘’ COMMENT ‘姓名’
);
2、spring配置类
定义2个数据源:dataSource1、dataSource2,分别用来连接数据库ds1和ds2
定义2个JdbcTemplate:jdbcTemplate1、jdbcTemplate2,分别关联dataSource1和dataSource2
2个数据源对应2个事务管理器:transactionManager1、transactionManager2,分别用来管理2个数据源的事务
6个bean的名称
| 数据源 | JdbcTemplate | 事务管理器 |
| — | — | — |
| dataSource1 | jdbcTemplate1 | transactionManager1 |
| dataSource2 | jdbcTemplate2 | transactionManager2 |
源码如下:
package com.javacode2018.tx.demo7;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@EnableTransactionManagement //开启spring事务管理功能
@Configuration //指定当前类是一个spring配置类
@ComponentScan //开启bean扫描注册
public class MainConfig7 {
//定义数据源1,连接数据库:ds1
@Bean
public DataSource dataSource1() {
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
dataSource.setDriverClassName(“com.mysql.jdbc.Driver”);
dataSource.setUrl(“jdbc:mysql://localhost:3306/ds1?characterEncoding=UTF-8”);
dataSource.setUsername(“root”);
dataSource.setPassword(“root123”);
dataSource.setInitialSize(5);
return dataSource;
}
//定义一个JdbcTemplate,对应数据源dataSource1,用来操作数据库:ds1
@Bean