Spring系列第46篇:Spring如何管理多数据源事务?

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


准备案例代码

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

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值