第八章 springboot + mybatis + 多数据源(转载)
本篇博客转发自:http://www.cnblogs.com/java-zhao/p/5413845.html
在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源。
代码结构:

简要原理:
1)DatabaseType列出所有的数据源的key---key
2)DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法
3)DynamicDataSource继承AbstractRoutingDataSource并重写其中的方法determineCurrentLookupKey(),在该方法中使用DatabaseContextHolder获取当前线程的DatabaseType
4)MyBatisConfig中生成2个数据源DataSource的bean---value
5)MyBatisConfig中将1)和4)组成的key-value对写入到DynamicDataSource动态数据源的targetDataSources属性(当然,同时也会设置2个数据源其中的一个为DynamicDataSource的defaultTargetDataSource属性中)
6)将DynamicDataSource作为primary数据源注入到SqlSessionFactory的dataSource属性中去,并且该dataSource作为transactionManager的入参来构造DataSourceTransactionManager
7)使用的时候,在dao层或service层先使用DatabaseContextHolder设置将要使用的数据源key,然后再调用mapper层进行相应的操作,建议放在dao层去做(当然也可以使用spring aop+自定注解去做)
注意:在mapper层进行操作的时候,会先调用determineCurrentLookupKey()方法获取一个数据源(获取数据源:先根据设置去targetDataSources中去找,若没有,则选择defaultTargetDataSource),之后在进行数据库操作。
1、假设有两个数据库,配置如下
application.properties
#the first datasource
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://xxx:3306/mytestdb?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
jdbc.username = root
jdbc.password = 123 #the second datasource
jdbc2.driverClassName = com.mysql.jdbc.Driver
jdbc2.url = jdbc:mysql://xxx:3306/mytestdb2?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
jdbc2.username = root
jdbc2.password = 123
说明:在之前的配置的基础上,只增加了上述的第二个数据源。
2、DatabaseType
package com.xxx.firstboot.common.datasource; /**
* 列出所有的数据源key(常用数据库名称来命名)
* 注意:
* 1)这里数据源与数据库是一对一的
* 2)DatabaseType中的变量名称就是数据库的名称
*/
public enum DatabaseType {
mytestdb,mytestdb2
}
作用:列举数据源的key。
3、DatabaseContextHolder
package com.xxx.firstboot.common.datasource; /**
* 作用:
* 1、保存一个线程安全的DatabaseType容器
*/
public class DatabaseContextHolder {
private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>(); public static void setDatabaseType(DatabaseType type){
contextHolder.set(type);
} public static DatabaseType getDatabaseType(){
return contextHolder.get();
}
}
作用:构建一个DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法
4、DynamicDataSource
package com.xxx.firstboot.common.datasource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /**
* 动态数据源(需要继承AbstractRoutingDataSource)
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
作用:使用DatabaseContextHolder获取当前线程的DatabaseType
5、MyBatisConfig
package com.xxx.firstboot.common; import java.util.HashMap;
import java.util.Map;
import java.util.Properties; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager; import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.xxx.firstboot.common.datasource.DatabaseType;
import com.xxx.firstboot.common.datasource.DynamicDataSource; /**
* springboot集成mybatis的基本入口 1)创建数据源(如果采用的是默认的tomcat-jdbc数据源,则不需要)
* 2)创建SqlSessionFactory 3)配置事务管理器,除非需要使用事务,否则不用配置
*/
@Configuration // 该注解类似于spring配置文件
@MapperScan(basePackages = "com.xxx.firstboot.mapper")
public class MyBatisConfig { @Autowired
private Environment env; /**
* 创建数据源(数据源的名称:方法名可以取为XXXDataSource(),XXX为数据库名称,该名称也就是数据源的名称)
*/
@Bean
public DataSource myTestDbDataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("jdbc.driverClassName"));
props.put("url", env.getProperty("jdbc.url"));
props.put("username", env.getProperty("jdbc.username"));
props.put("password", env.getProperty("jdbc.password"));
return DruidDataSourceFactory.createDataSource(props);
} @Bean
public DataSource myTestDb2DataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("jdbc2.driverClassName"));
props.put("url", env.getProperty("jdbc2.url"));
props.put("username", env.getProperty("jdbc2.username"));
props.put("password", env.getProperty("jdbc2.password"));
return DruidDataSourceFactory.createDataSource(props);
} /**
* @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
* @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
*/
@Bean
@Primary
public DynamicDataSource dataSource(@Qualifier("myTestDbDataSource") DataSource myTestDbDataSource,
@Qualifier("myTestDb2DataSource") DataSource myTestDb2DataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.mytestdb, myTestDbDataSource);
targetDataSources.put(DatabaseType.mytestdb2, myTestDb2DataSource); DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
dataSource.setDefaultTargetDataSource(myTestDbDataSource);// 默认的datasource设置为myTestDbDataSource return dataSource;
} /**
* 根据数据源创建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(ds);// 指定数据源(这个必须有,否则报错)
// 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
fb.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));// 指定基包
fb.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));// return fb.getObject();
} /**
* 配置事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
} }
作用:
- 通过读取application.properties文件生成两个数据源(myTestDbDataSource、myTestDb2DataSource)
- 使用以上生成的两个数据源构造动态数据源dataSource
- @Primary:指定在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@Autowire注解报错(一般用于多数据源的情况下)
- @Qualifier:指定名称的注入,当一个接口有多个实现类的时候使用(在本例中,有两个DataSource类型的实例,需要指定名称注入)
- @Bean:生成的bean实例的名称是方法名(例如上边的@Qualifier注解中使用的名称是前边两个数据源的方法名,而这两个数据源也是使用@Bean注解进行注入的)
- 通过动态数据源构造SqlSessionFactory和事务管理器(如果不需要事务,后者可以去掉)
6、使用
ShopMapper:
package com.xxx.firstboot.mapper; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select; import com.xxx.firstboot.domain.Shop; public interface ShopMapper { @Select("SELECT * FROM t_shop WHERE id = #{id}")
@Results(value = { @Result(id = true, column = "id", property = "id"),
@Result(column = "shop_name", property = "shopName") })
public Shop getShop(@Param("id") int id); }
ShopDao:
package com.xxx.firstboot.dao; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository; import com.xxx.firstboot.common.datasource.DatabaseContextHolder;
import com.xxx.firstboot.common.datasource.DatabaseType;
import com.xxx.firstboot.domain.Shop;
import com.xxx.firstboot.mapper.ShopMapper; @Repository
public class ShopDao {
@Autowired
private ShopMapper mapper; /**
* 获取shop
*/
public Shop getShop(int id) {
DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb2);
return mapper.getShop(id);
}
}
注意:首先设置了数据源的key,然后调用mapper(在mapper中会首先根据该key从动态数据源中查询出相应的数据源,之后取出连接进行数据库操作)
ShopService:
package com.xxx.firstboot.service; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import com.xxx.firstboot.dao.ShopDao;
import com.xxx.firstboot.domain.Shop; @Service
public class ShopService { @Autowired
private ShopDao dao; public Shop getShop(int id) {
return dao.getShop(id);
}
}
ShopController:
package com.xxx.firstboot.web; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import com.xxx.firstboot.domain.Shop;
import com.xxx.firstboot.service.ShopService; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; @RestController
@RequestMapping("/shop")
@Api("shopController相关api")
public class ShopController { @Autowired
private ShopService service; @ApiOperation("获取shop信息,测试多数据源")
@RequestMapping(value = "/getShop", method = RequestMethod.GET)
public Shop getShop(@RequestParam("id") int id) {
return service.getShop(id);
} }
补:其实DatabaseContextHolder和DynamicDataSource完全可以合为一个类
参考:
http://www.cnblogs.com/lzrabbit/p/3750803.html
遗留:在实际开发中,一个dao类只会用到一个数据源,如果dao类中的方法很多的话,每一个方法前边都要添加一个设置数据源的一句话,代码有些冗余,可以使用AOP切面。
第八章 springboot + mybatis + 多数据源(转载)的更多相关文章
- 第八章 springboot + mybatis + 多数据源2(解决循环引用)
解决了循环引用 1.application.properties #the first datasource jdbc.names:1,2 jdbc1.driverClassName = com.my ...
- 第八章 springboot + mybatis + 多数据源3(使用切面AOP)
引入 aop包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...
- 第八章 springboot + mybatis + 多数据源
http://www.cnblogs.com/java-zhao/p/5413845.html
- 第九章 springboot + mybatis + 多数据源 (AOP实现)
在第八章 springboot + mybatis + 多数据源代码的基础上,做两点修改 1.ShopDao package com.xxx.firstboot.dao; import org.spr ...
- 【第九章】 springboot + mybatis + 多数据源 (AOP实现)
在第八章 springboot + mybatis + 多数据源代码的基础上,做两点修改 1.ShopDao package com.xxx.firstboot.dao; import org.spr ...
- spring-boot (四) springboot+mybatis多数据源最简解决方案
学习文章来自:http://www.ityouknow.com/spring-boot.html 配置文件 pom包就不贴了比较简单该依赖的就依赖,主要是数据库这边的配置: mybatis.confi ...
- springboot + mybatis + 多数据源
此文已由作者赵计刚薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验 在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构: 简要原理: 1) ...
- springboot mybatis 多数据源配置
首先导入mybatis等包,这里就不多说. 下面是配置多数据源和mybatis,每个数据源对应一套mybatis模板 数据源1: package com.aaaaaaa.config.datasour ...
- 【第八章】 springboot + mybatis + 多数据源
在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构: 简要原理: 1)DatabaseType列出所有的数据源的key---key 2)DatabaseConte ...
随机推荐
- C++ 随机数
#include <iostream> #include <stdlib.h> #include <time.h> #define random(a,b) (ran ...
- C语言PIC18 serial bootloader和C#语言bootloader PC端串口通信程序
了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 新PIC18 Boot ...
- Java开发环境配置
JDK下载地址: http://www.oracle.com/technetwork/java/javase/downloads/index.html 安装:双击exe文件一直下一步就Ok了. 环境变 ...
- PHPCMS V9 分页类的修改教程
首先,打开 phpcms\libs\functions\global.func.php 这个文件,找到文件第622行的分页函数,复制一下,粘贴到默认分页函数的下面,重新命名后保存.(笔者在此命名为:p ...
- flume log4j配置
版权声明:本文为博主原创文章,未经博主允许不得转载. 1.依赖jar包导入,通过maven管理,pom文件增加如下配置: <!-- log4j输出日志到flume的appender依赖包 --& ...
- Java线程问题分析定位
Java线程问题分析定位 分析步骤: 1.使用top命令查看系统资源占用情况,发现Java进程占用大量CPU资源,PID为11572: 2.显示进程详细列表命令:ps -mp 11572 -o THR ...
- 网络同步带来的bug
说一下之前遇到的bug,首先贴点代码 public class TcpSession : ITcpSession { private Socket _appSession; private AutoR ...
- 使用WebClient 或者 HttpWebRequest均报:"The Remote name can't be solved"
错误原因: 未配置代理服务器设置的问题, 需要在配置节做如下操作. ============================================ 文章编号: 318140 - 查看本文应用 ...
- curd 里url传输汉字验证错误问题解决方法
在url汉字转换的部分用base64_encode转化 base64_encode 将字符串以 BASE64 编码. 语法: string base64_encode(string data); 返回 ...
- iOS调试
iOS高效调试 写代码难免出现bug.储备些调试技能绝对能够提高你的工作效率,让bug无所遁形.下面就和大家分享一些我在工作中常用的iOS调试小技能. 1. 打印 最简单,基础的调试方法就是打印日志了 ...