前言

github: https://github.com/vergilyn/SpringBootDemo

代码位置:

参考:

Spring Boot Reference Guide , §77.2 Configure Two DataSources

springboot + mybatis + 多数据源

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)– 多数据源及其事务的更多相关文章

  1. Spring boot配置多个Redis数据源操作实例

    原文:https://www.jianshu.com/p/c79b65b253fa Spring boot配置多个Redis数据源操作实例 在SpringBoot是项目中整合了两个Redis的操作实例 ...

  2. 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 多数 ...

  3. spring boot + druid + mybatis + atomikos 多数据源配置 并支持分布式事务

    文章目录 一.综述 1.1 项目说明 1.2 项目结构 二.配置多数据源并支持分布式事务 2.1 导入基本依赖 2.2 在yml中配置多数据源信息 2.3 进行多数据源的配置 三.整合结果测试 3.1 ...

  4. Spring Boot HikariCP 一 ——集成多数据源

    其实这里介绍的东西主要是参考的另外一篇文章,数据库读写分离的. 参考文章就把链接贴出来,里面有那位的代码,简单明了https://gitee.com/comven/dynamic-datasource ...

  5. 14、Spring Boot 2.x 集成 Druid 数据源

    14.Spring Boot 2.x 集成 Druid 数据源 完整源码: Spring-Boot-Demos

  6. Spring Boot数据访问之动态数据源切换之使用注解式AOP优化

    在Spring Boot数据访问之多数据源配置及数据源动态切换 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中详述了如何配置多数据源及多数据源之间的动态切换.但是需要读数据库的地方,就 ...

  7. 【Rocket MQ】RocketMQ4.2.0 和 spring boot的结合使用,实现分布式事务

    RocketMQ4.2.0 和 spring boot的结合使用,实现分布式事务 参考地址:https://www.jianshu.com/p/f57de40621a0

  8. spring boot 中 Mybatis plus 多数据源的配置方法

    最近在学习spring boot,发现在jar包依赖方面做很少的工作量就可以了,对于数据库操作,我用的比较多的是mybatis plus,在中央仓库已经有mybatis-plus的插件了,对于单数据源 ...

  9. (转)Spring Boot(七):Mybatis 多数据源最简解决方案

    http://www.ityouknow.com/springboot/2016/11/25/spring-boot-multi-mybatis.html 说起多数据源,一般都来解决那些问题呢,主从模 ...

  10. Spring Boot + MyBatis + Pagehelper 配置多数据源

    前言: 本文为springboot结合mybatis配置多数据源,在项目当中很多情况是使用主从数据源来读写分离,还有就是操作多库,本文介绍如何一个项目同时使用2个数据源. 也希望大家带着思考去学习!博 ...

随机推荐

  1. Codeforces_714_B

    http://codeforces.com/problemset/problem/714/B 当不同大小整数有1.2个时,肯定成立,3个时,需要判断,大于等于4个,则肯定不成立. #include & ...

  2. Springboot笔记(二)整合

    1.整合Freemarker 一种模板引擎,前端渲染模板的,类似于EL,jsp,不过比前两个好用 导入很简单   pom.xml <dependency> <groupId>o ...

  3. How to collect TLOG usage status automatically ?

    Yes , in SQLSERVER, we use "DBCC sqlperf(logspace)" to check transaction logfile status.Bu ...

  4. 全网最详细的Linux命令系列-Screen远程会话命令

    screen 管理你的远程会话 你是不是经常需要 SSH 或者 telent 远程登录到 Linux 服务器?你是不是经常为一些长时间运行的任务而头疼,比如系统备份.ftp 传输等等.通常情况下我们都 ...

  5. Go语言实现:【剑指offer】链表中环的入口结点

    ​该题目来源于牛客网<剑指offer>专题. 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null. Go语言实现: /** * Definition for sing ...

  6. Go语言实现:【剑指offer】把数组排成最小的数

    该题目来源于牛客网<剑指offer>专题. 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3,32,321},则打印出这三个数字 ...

  7. error C2662

    原因:关于const的问题 具体错误:函数的参数列表中参数签名为const,但是却调用了该参数的非const的成员函数 例子: 即使我们知道NoConst()并不会改变类的data成员,编译器依旧会报 ...

  8. gcc和g++的区别:安装、版本、编译(转)

    用以下命令: yum install gcc 安装的只有gcc,而不会安装g++.gcc是编译器合集,而gcc-g++或简称g++则是C++编译器.gcc成为了编译器的选择器.gcc通过识别被编译的源 ...

  9. 关于ThinkPHP在Nginx服务器下因PATH_INFO出错的解决方法

    参考:https://www.linuxidc.com/Linux/2011-11/46871.htm 这是一个ningx设置的问题,和TP无关.TP默认使用PATH_INFO来做CURD,而ngin ...

  10. [Pyhton]连接MSSQL实例并执行SQL语句

    运行环境: 服务器端: MSSQL 2014 Server 2012 R2 程序端: Python 3.7.4 MacOS 10.14.6 CentOS Linux release 7.7.1908 ...