前言

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. 一台电脑上配置多个git的ssh key

    前几天公司的代码库全部迁移到了阿里云上,在配置git的ssh key的时候遇到了一个问题,那就是自己的密钥在添加时提示已经存在,原来是自己的个人账号上已经添加过这个密钥了,公司分配的账号就不能再添加这 ...

  2. 5G为人工智能与工业互联网赋能 高清79页PPT

    人工智能和5G是时下最热门的科技领域之一,两者都是能改变社会,推进下一代工业革命的颠覆性技术. 工业互联网是利用基础科学.工业.信息技术.互联网等领域的综合优势,从大数据应用等软服务切入,注重软件.网 ...

  3. Go语言实现:【剑指offer】翻转单词顺序列

    该题目来源于牛客网<剑指offer>专题. 例如,"student. a am I",正确的句子应该是"I am a student." Go语言实 ...

  4. 对MYSQL注入相关内容及部分Trick的归类小结

    前言 最近在给学校的社团成员进行web安全方面的培训,由于在mysql注入这一块知识点挺杂的,入门容易,精通较难,网上相对比较全的资料也比较少,大多都是一个比较散的知识点,所以我打算将我在学习过程中遇 ...

  5. 基于原生的 html css js php ajax做的一个 web登录和注册系统

    完整代码下载: 百度网盘地址 https://pan.baidu.com/s/1D1gqHSyjgfoOtYCZm7ofJg 提取码 :nf0b 永久有效 注意: 1 如果要正常运行此示例, 本地需要 ...

  6. 【大白话系统】MySQL 学习总结 之 缓冲池(Buffer Pool) 如何支撑高并发和动态调整

    如果大家对我的 [大白话系列]MySQL 学习总结系列 感兴趣的话,可以点击关注一波. 一.上节回顾 在上节< 缓冲池(Buffer Pool) 的设计原理和管理机制>中,介绍了缓冲池整体 ...

  7. Enum, Generic and Templates

    文 Akisann@CNblogs / zhaihj@Github 本篇文章同时发布在Github上:https://zhaihj.github.io/enum-generic-and-templat ...

  8. ospfv3 lsa database

    https://www.networkfuntimes.com/ospfv3-the-new-lsa-types-in-ipv6-ospf/ WHY DID THEY CHANGE THE LSAs ...

  9. ubuntu 14.04 下安装 selenium 2.0

    文章参考出处:http://blog.sina.com.cn/s/blog_5042ea610102we4y.html 1.安装 python-pip sudo apt-get install pyt ...

  10. [Linux-CentOS7]安装Telnet

    # yum install telnet Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile Resolv ...