【spring boot】SpringBoot初学(7)– 多数据源及其事务
前言
github: https://github.com/vergilyn/SpringBootDemo
代码位置:
参考:
Spring Boot Reference Guide , §77.2 Configure Two DataSources
springboot + mybatis + 多数据源 (AOP实现)
一、准备
因为配置的是oracle、mysql、JdbcTemplate,所以需要各自的驱动jar和JdbcTemplate所需要的jar。
<!-- spring jdbc支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency> <!-- mysql驱动支持 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency> <!-- oracle驱动支持。注:此驱动maven不一定下载得到。-->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.1.0.7.0</version>
</dependency>
# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.continue-on-error=false # Do not stop if an error occurs while initializing the database.
spring.datasource.data= # Data (DML) script resource references.
spring.datasource.data-username= # User of the database to execute DML scripts (if different).
spring.datasource.data-password= # Password of the database to execute DML scripts (if different).
spring.datasource.dbcp2.*= # Commons DBCP2 specific settings
spring.datasource.driver-class-name= # Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
spring.datasource.generate-unique-name=false # Generate a random datasource name.
spring.datasource.hikari.*= # Hikari specific settings
spring.datasource.initialize=true # Populate the database using 'data.sql'.
spring.datasource.jmx-enabled=false # Enable JMX support (if provided by the underlying pool).
spring.datasource.jndi-name= # JNDI location of the datasource. Class, url, username & password are ignored when set.
spring.datasource.name=testdb # Name of the datasource.
spring.datasource.password= # Login password of the database.
spring.datasource.platform=all # Platform to use in the schema resource (schema-${platform}.sql).
spring.datasource.schema= # Schema (DDL) script resource references.
spring.datasource.schema-username= # User of the database to execute DDL scripts (if different).
spring.datasource.schema-password= # Password of the database to execute DDL scripts (if different).
spring.datasource.separator=; # Statement separator in SQL initialization scripts.
spring.datasource.sql-script-encoding= # SQL scripts encoding.
spring.datasource.tomcat.*= # Tomcat datasource specific settings
spring.datasource.type= # Fully qualified name of the connection pool implementation to use. By default, it is auto-detected from the classpath.
spring.datasource.url= # JDBC url of the database.
spring.datasource.username=
oralce、mysql中的表结构是一模一样的。表名:MYSQL_PARENT、ORACLE_PARENT
字段:主键PARENT_ID,INT类型。非空字段PARENT_NAME,VARCHAR类型。
二、完整demo
代码结构说明:
1. mysql、oracle的包下,放各自DataSource的service、dao。
2. 包config放置多数据源的配置,其中包括DataSource、DataSourceTransactionManager等。
2.1 多数据源的DataSource、TransactionManager、JdbcTemplate配置
@Configuration
@PropertySource("classpath:config/dbMulti/db_multi.properties")
public class DBmultiConfig {
/* 此处 @Bean + @Qualifier("oracleDB") 等价于 @Bean("oracleDB").
* 如果写成@Bean("oracleDB"),在idea中,之后的@Qualifier("oracleDB")会有error提示.但不影响代码的正确性.
*/
@Bean
@Qualifier("oracleDB")
@Primary
@ConfigurationProperties("oracle.datasource")
public DataSource oracleDataSource() {
return DataSourceBuilder.create().build();
} @Bean(name = "oracleJT")
public JdbcTemplate oracleJdbcTemplate(@Qualifier("oracleDB")DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean("oracleTS")
public DataSourceTransactionManager oracleTransactionManager(@Qualifier("oracleDB")DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
// 多数据源mybatis的sqlSession注入
// @Bean("oracleSS")
public SqlSessionFactory oracleSqlSession(@Qualifier("oracleDB")DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
//bean.setXX(...) 其余mybatis的设置
/* 例如:以下是mybatis基于*.xml文件配置,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定). 则无需set.
* factoryBean.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage")); // 指定基包
* factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));
*/
return factoryBean.getObject();
} @Bean
@Qualifier("mysqlDB")
@ConfigurationProperties("mysql.datasource")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
} @Bean(name = "mysqlJT")
public JdbcTemplate mysqlJdbcTemplate(@Qualifier("mysqlDB") DataSource dataSource){
return new JdbcTemplate(dataSource);
} @Bean("mysqlTS")
public DataSourceTransactionManager mysqlTransactionManager(@Qualifier("mysqlDB")DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}

#### spring boot配置多数据源及事务 #### mysql数据源配置
mysql.datasource.maximum-pool-size=30
mysql.datasource.url=jdbc:mysql://localhost/VERGILYN
mysql.datasource.username=root
mysql.datasource.password=409839163
mysql.datasource.max-total=30 #### oracle数据源配置
oracle.datasource.maximum-pool-size=30
oracle.datasource.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
oracle.datasource.username=vergilyn
oracle.datasource.password=409839163
oracle.datasource.max-total=30
db_multi.properties
@Primary:标明此DataSource是一个主数据源。(其实@Primary的作用并不是用来标明主数据源的,参考@Primary用法:在spring中常被忽视的注解 @Primary)
oracleDataSource()、mysqlDataSource():分别用来配置注入mysql、oracle的DataSource。
oracleTransactionManager()、mysqlTransactionManager():分别用来配置注入mysql、oracle的DataSource的事务管理,都交由spring统一管理。
db_multi.properties:和datasource配置是一样的,只要保证最后的(.url .username)正确,前面的mysql.datasource、oracle.datasource可以随意命名的。
(基于mybatis的SqlSession并没有测试过,但应该思路没错。)
2.2 oracle、mysql的service&dao的实现
2.2.1 service
@Service
public class OracleService {
@Autowired
private MysqlService mysqlService;
@Autowired
private OracleDao oracleDao; @Transactional(transactionManager = "oracleTS",propagation = Propagation.REQUIRED)
public Parent getById(int parentId){
return oracleDao.getById(parentId);
} @Transactional(transactionManager = "oracleTS",rollbackFor = Exception.class)
public void insert(Parent p) throws Exception{
oracleDao.insert(p);
} @Transactional(transactionManager = "oracleTS",rollbackFor = Exception.class)
public void insertDBmulti(Parent parent,boolean isSameTransaction) throws Exception {
oracleDao.insert(parent);
if(isSameTransaction){
mysqlService.insert(parent);
}else{
try {
mysqlService.insert(parent);
}catch (Exception e){
e.printStackTrace();;
}
} }
@Transactional(transactionManager = "oracleTS",propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void insertREQUIRES_NEW(Parent parent) throws Exception {
this.insert(parent);
}
}

*/
@Service
public class MysqlService {
@Autowired
private MysqlDao mysqlDao;
@Autowired
private OracleService oracleService; @Transactional(transactionManager = "mysqlTS",propagation = Propagation.REQUIRED)
public Parent getById(int parentId){
return mysqlDao.getById(parentId);
} @Transactional(transactionManager = "mysqlTS",rollbackFor = Exception.class)
public void insert(Parent p) throws Exception{
mysqlDao.insert(p);
} @Transactional(transactionManager = "mysqlTS",propagation = Propagation.REQUIRED)
public void insertREQUIRES_NEW(Parent parent) throws Exception {
oracleService.insertREQUIRES_NEW(parent);
this.insert(parent);
}
}
MysqlService.java
2.2.2 dao
@Repository
public class OracleDao {
@Resource(name = "oracleJT")
private JdbcTemplate jdbcTemplate; public Parent getById(int parentId) {
String sql = "select * from oracle_parent where id = ?"; return jdbcTemplate.queryForObject(sql,new Object[]{parentId}
,new BeanPropertyRowMapper<Parent>(Parent.class));
} public void insert(Parent p) {
String sql = "insert into oracle_parent(parent_id,parent_name) values(?,?)"; jdbcTemplate.update(sql,new Object[]{p.getParentId(),p.getParentName()});
}
}

@Repository("mysqlDao")
public class MysqlDao { // @Resource(name = "mysqlJT") 等价于 @Qualifier("mysqlJT") + @Autowired
// Resource是j2ee提供的,而Autowired、Qualifier是由spring提供的.为了降低与spring的耦合度,建议用Resource.
@Qualifier("mysqlJT")
@Autowired
private JdbcTemplate jdbcTemplate; public Parent getById(int parentId) {
String sql = "select * from mysql_parent where id = ?"; return jdbcTemplate.queryForObject(sql,new Object[]{parentId}
,new BeanPropertyRowMapper<Parent>(Parent.class));
} public void insert(Parent p) {
String sql = "insert into mysql_parent(parent_id,parent_name) values(?,?)"; jdbcTemplate.update(sql,new Object[]{p.getParentId(),p.getParentName()});
}
}
MysqlDao.java
说明:
1、@Resource与@Qualifier+@Autowired的区别
@Resource是javax.annotation.Resource,即由j2ee提供。
而@Qualifier+@Autowired是由spring提供。
(网上的资料说,为了降低与spring的耦合度,建议使用@Resource。不理解为什么要降低与spring的耦合度...)
2、以上dao的demo每个类中都要注入各自DataSource的JdbcTemplate(mybatis则要注入SqlSession)
可借由extends各自继承一个父类的BaseDao,来简化代码。(也可用aop来实现)
3、service也存在2的问题。要频繁的写@Transactional配置,指定各自的TrancactionManager。
虽然,也可以借由aop来统一管理。但,个人并不建议。可能个人习惯事务要明确写明。
2.2.3 SpringBootApplication、Junit测试、其他

public class Parent implements Serializable{
private int parentId;
private String parentName;
public Parent() {
} public Parent(int parentId, String parentName) {
this.parentId = parentId;
this.parentName = parentName;
} public int getParentId() {
return parentId;
} public void setParentId(int parentId) {
this.parentId = parentId;
} public String getParentName() {
return parentName;
} public void setParentName(String parentName) {
this.parentName = parentName;
}
}
Parent.java
@SpringBootApplication
public class DBmultiApplication{
public static void main(String[] args) {
SpringApplication app = new SpringApplication(DBmultiApplication.class);
app.run(args);
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DBmultiApplication.class)
public class DBmultiApplicationTest {
@Autowired
private MysqlService mysqlService;
@Autowired
private OracleService oracleService;
private final Parent parent = new Parent(100,"Vergilyn"); @Test
public void mysqlInsert() throws Exception {
mysqlService.insert(parent);
System.out.println("mysql insert end.");
} @Test
public void oracleInsert() throws Exception {
oracleService.insert(parent);
System.out.println("oracle insert end.");
} @Test
public void sameTransaction() throws Exception {
oracleService.insertDBmulti(parent, true);
System.out.println("sameTransaction() end."); } @Test
public void diffTransaction() throws Exception {
oracleService.insertDBmulti(parent, false);
System.out.println("diffTransaction() end.");
} /**
* 在mysql中,先调用oracle,此oracle的事务是:REQUIRES_NEW。
* 所以,即使在mysql方法中最后被回滚,oracle也被正确insert一行数据。
*/
@Test
public void insertREQUIRES_NEW() throws Exception {
mysqlService.insertREQUIRES_NEW(parent);
System.out.println("insertREQUIRES_NEW() end.");
}
}
测试说明:
1. mysqlInsert()、oracleInsert() 测试各自的DataSource、TransactionManager、JdbcTemplate是否配置正确。
2. sameTransaction当insert的时候,oracle、mysql要么都成功,要么同时回滚。
如demo,先在oracle插入一行数据,再在mysql中插入一行。如果,mysql中存在id=100的数据,导致mysql插入失败。那么产生RuntimeException,所以事务回滚。即oracle中不会被插入一行数据,此操作被回滚了。
而如diffTransaction(),在oracle中把mysql抛出的异常吃掉了。所以,oracle不会被回滚。
3. 如果insert之间互不影响,可有2种解决方式(自己知道的)
i. 把异常吃掉,那么就不会回滚了。如oracleService.insertDBmulti(...)
ii. 调用的其他事务另起一个新事务,如mysqlService.insertREQUIRES_NEW(...),其中oracleService.insertREQUIRES_NEW(...)的事务并声明为propagation = Propagation.REQUIRES_NEW。
【spring boot】SpringBoot初学(7)– 多数据源及其事务的更多相关文章
- Spring boot配置多个Redis数据源操作实例
原文:https://www.jianshu.com/p/c79b65b253fa Spring boot配置多个Redis数据源操作实例 在SpringBoot是项目中整合了两个Redis的操作实例 ...
- Spring Boot 2.x Redis多数据源配置(jedis,lettuce)
Spring Boot 2.x Redis多数据源配置(jedis,lettuce) 96 不敢预言的预言家 0.1 2018.11.13 14:22* 字数 65 阅读 727评论 0喜欢 2 多数 ...
- spring boot + druid + mybatis + atomikos 多数据源配置 并支持分布式事务
文章目录 一.综述 1.1 项目说明 1.2 项目结构 二.配置多数据源并支持分布式事务 2.1 导入基本依赖 2.2 在yml中配置多数据源信息 2.3 进行多数据源的配置 三.整合结果测试 3.1 ...
- Spring Boot HikariCP 一 ——集成多数据源
其实这里介绍的东西主要是参考的另外一篇文章,数据库读写分离的. 参考文章就把链接贴出来,里面有那位的代码,简单明了https://gitee.com/comven/dynamic-datasource ...
- 14、Spring Boot 2.x 集成 Druid 数据源
14.Spring Boot 2.x 集成 Druid 数据源 完整源码: Spring-Boot-Demos
- Spring Boot数据访问之动态数据源切换之使用注解式AOP优化
在Spring Boot数据访问之多数据源配置及数据源动态切换 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中详述了如何配置多数据源及多数据源之间的动态切换.但是需要读数据库的地方,就 ...
- 【Rocket MQ】RocketMQ4.2.0 和 spring boot的结合使用,实现分布式事务
RocketMQ4.2.0 和 spring boot的结合使用,实现分布式事务 参考地址:https://www.jianshu.com/p/f57de40621a0
- spring boot 中 Mybatis plus 多数据源的配置方法
最近在学习spring boot,发现在jar包依赖方面做很少的工作量就可以了,对于数据库操作,我用的比较多的是mybatis plus,在中央仓库已经有mybatis-plus的插件了,对于单数据源 ...
- (转)Spring Boot(七):Mybatis 多数据源最简解决方案
http://www.ityouknow.com/springboot/2016/11/25/spring-boot-multi-mybatis.html 说起多数据源,一般都来解决那些问题呢,主从模 ...
- Spring Boot + MyBatis + Pagehelper 配置多数据源
前言: 本文为springboot结合mybatis配置多数据源,在项目当中很多情况是使用主从数据源来读写分离,还有就是操作多库,本文介绍如何一个项目同时使用2个数据源. 也希望大家带着思考去学习!博 ...
随机推荐
- React+wangeditor+node富文本处理带图片上传
最近有个需求出现在我的视野中,因为我的另外的博客需要上传文章,但是我不想每次都在我的数据库中慢慢的修改格式,所以我另做了一个后台去编辑文本后发送给服务器,那么这里就涉及到两点,一个是富文本,一个是需要 ...
- DevOps:运维体系建设
简介 运维体系的建设的目的在于方便运维工作,通过自动化.规范化.流程化的操作方法提高运维效率,打造一个安全.可靠.高效.可追踪.可回溯的运维环境,实现一个高可用.高并发.具备高容错.自我修复.故障能快 ...
- [Python]random生成随机6位验证码
#!/usr/bin/env pyhton # coding:utf-8 # @Time : 2020-02-16 10:07 # @Author : LeoShi # @Site : # @File ...
- MySQL索引那些事
原文链接 大家有没有遇到过慢查询的情况,执行一条SQL需要几秒,甚至十几.几十秒的时间,这时候DBA就会建议你去把查询的 SQL 优化一下,怎么优化?你能想到的就是加索引吧? 为什么加索引就查的快了? ...
- JS生成全局唯一标识符(GUID,UUID)的方法
全局唯一标识符(GUID,Globally Unique Identifier)也称作 UUID(Universally Unique IDentifier) . GUID是一种由算法生成的二进制长度 ...
- css中伪类和伪元素
伪类和伪元素时对那些我们不能通过class.id等选择元素的补充 伪类的操作对象是文档树中已有的元素(可以给已有元素加了一个类替代),而伪元素则创建了一个文档数外的元素(可以添加一个新元素替代) CS ...
- Spark SQL 之自定义删除外部表
前言 Spark SQL 在删除外部表时,本不能删除外部表的数据的.本篇文章主要介绍如何修改Spark SQL 源码实现在删除外部表的时候,可以带额外选项来删除外部表的数据. 本文的环境是我一直使用的 ...
- leaflet结合geoserver利用WFS服务实现图层新增功能(附源码下载)
前言 leaflet 入门开发系列环境知识点了解: leaflet api文档介绍,详细介绍 leaflet 每个类的函数以及属性等等 leaflet 在线例子 leaflet 插件,leaflet ...
- 使用Ajax时[object%20object] 报错的解决方案
踩坑经过 最近初学Ajax,当我想把Ajax应用到自己项目中的时候,没有达到理想的效果,还报了如下错误: 点击图中报错,产生报错页面如下: 当时写的Ajax如下: // 提交修改密码表单 $(&quo ...
- 聊聊spring之贯穿全局的重要对象BeanDefinition
BeanDefinition 在 spring 中贯穿始终,spring 要根据 BeanDefinition 对象来实 例化 bean,只要把解析的标签,扫描的注解类封装成 BeanDefiniti ...