这里用到了Spring Boot + Mybatis + DynamicDataSource配置动态双数据源,可以动态切换数据源实现数据库的读写分离。

添加依赖

加入Mybatis启动器,这里添加了Druid连接池、Oracle数据库驱动为例。

<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency> <dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
</dependency>

添加启动类

@EnableMybatis
@EnableTransactionManagement
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class Application { public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
} }

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }):

这里用到了双数据源,需要排除数据源的自动配置,如果只有一个数据源用Spring Boot的自动配置就行。

@EnableTransactionManagement:开启事务支持。

@EnableMybatis:开启Mybatis功能

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MybatisConfig.class)
public @interface EnableMybatis { }

Mybatis配置类

@Configuration
@MapperScan(basePackages = DSConfig.BASE_PACKAGES)
public class MybatisConfig implements DSConfig { @Primary
@Bean
public DynamicDataSource dynamicDataSource(@Qualifier(DB_MASTER) DataSource master,
@Qualifier(DB_SLAVE) DataSource slave) {
Map<Object, Object> dsMap = new HashMap<>();
dsMap.put(DB_MASTER, master);
dsMap.put(DB_MASTER, slave); DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setDefaultTargetDataSource(master);
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
} @Bean
public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
} @Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource)
throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dynamicDataSource);
sessionFactory.setMapperLocations(
((ResourcePatternResolver) new PathMatchingResourcePatternResolver())
.getResources(DSConfig.MAPPER_LOCATIONS));
return sessionFactory.getObject();
} }

DSConfig常量类:

public interface DSConfig {

	String DS_PREFIX = "spring.datasource";
String DS_ACTIVE = "active"; String DB_MASTER = "db-master";
String DB_SLAVE = "db-slave"; String DRUID = "druid"; String DRUID_MONITOR_USERNAME = "spring.druid.username";
String DRUID_MONITOR_PASSWORD = "spring.druid.password";
String DRUID_MONITOR_URL = "/druid/*";
String DRUID_FILTER_EXCLUSIONS = "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*";
String DRUID_FILTER_URL = "/*"; String BASE_PACKAGES = "com.example.**.mapper";
String MAPPER_LOCATIONS = "mapper/**/*.xml"; }

连接池配置类

Druid连接池的自动配置类:

@Configuration
@Import({ PropertiesConfig.class })
@ConditionalOnClass(DruidDataSource.class)
@ConditionalOnProperty(prefix = DSConfig.DS_PREFIX, value = DSConfig.DS_ACTIVE, havingValue = DSConfig.DRUID)
public class DruidAutoConfig implements DSConfig { private Logger logger = LoggerUtils.getLogger(this); @Bean(name = DB_MASTER, initMethod = "init", destroyMethod = "close")
public DataSource dataSourceMaster(DruidMasterProperties masterProperties) throws SQLException {
logger.debug("master properties: {}", masterProperties.toString()); DruidDataSource dds = new DruidDataSource();
dds.setDriverClassName(masterProperties.getDriverClassName());
dds.setUrl(masterProperties.getUrl());
dds.setUsername(masterProperties.getUsername());
dds.setPassword(masterProperties.getPassword());
dds.setInitialSize(masterProperties.getInitialSize());
dds.setMinIdle(masterProperties.getMinIdle());
dds.setMaxActive(masterProperties.getMaxActive());
dds.setMaxWait(masterProperties.getMaxWait());
dds.setTimeBetweenEvictionRunsMillis(masterProperties.getTimeBetweenEvictionRunsMillis());
dds.setMinEvictableIdleTimeMillis(masterProperties.getMinEvictableIdleTimeMillis());
dds.setValidationQuery(masterProperties.getValidationQuery());
dds.setTestOnBorrow(masterProperties.isTestOnBorrow());
dds.setTestWhileIdle(masterProperties.isTestWhileIdle());
dds.setTestOnReturn(masterProperties.isTestOnReturn());
dds.setPoolPreparedStatements(masterProperties.isPoolPreparedStatements());
dds.setMaxPoolPreparedStatementPerConnectionSize(
masterProperties.getMaxPoolPreparedStatementPerConnectionSize());
dds.setFilters(masterProperties.getFilters()); return dds;
} @Bean(name = DB_SLAVE, initMethod = "init", destroyMethod = "close")
public DataSource dataSourceSlave(DruidSlaveProperties slaveProperties) throws SQLException {
logger.debug("slave properties: {}", slaveProperties.toString()); DruidDataSource dds = new DruidDataSource();
dds.setDriverClassName(slaveProperties.getDriverClassName());
dds.setUrl(slaveProperties.getUrl());
dds.setUsername(slaveProperties.getUsername());
dds.setPassword(slaveProperties.getPassword());
dds.setInitialSize(slaveProperties.getInitialSize());
dds.setMinIdle(slaveProperties.getMinIdle());
dds.setMaxActive(slaveProperties.getMaxActive());
dds.setMaxWait(slaveProperties.getMaxWait());
dds.setTimeBetweenEvictionRunsMillis(slaveProperties.getTimeBetweenEvictionRunsMillis());
dds.setMinEvictableIdleTimeMillis(slaveProperties.getMinEvictableIdleTimeMillis());
dds.setValidationQuery(slaveProperties.getValidationQuery());
dds.setTestOnBorrow(slaveProperties.isTestOnBorrow());
dds.setTestWhileIdle(slaveProperties.isTestWhileIdle());
dds.setTestOnReturn(slaveProperties.isTestOnReturn());
dds.setPoolPreparedStatements(slaveProperties.isPoolPreparedStatements());
dds.setMaxPoolPreparedStatementPerConnectionSize(
slaveProperties.getMaxPoolPreparedStatementPerConnectionSize());
dds.setFilters(slaveProperties.getFilters()); return dds;
} @Bean
public ServletRegistrationBean druidServletRegistrationBean(EnvConfig env) {
String username = env.getStringValue(DSConfig.DRUID_MONITOR_USERNAME);
String password = env.getStringValue(DSConfig.DRUID_MONITOR_PASSWORD);
return new ServletRegistrationBean(new DruidStatViewServlet(username, password),
DSConfig.DRUID_MONITOR_URL);
} @Bean
public FilterRegistrationBean druidFilterRegistrationBean() {
WebStatFilter wsf = new WebStatFilter();
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(wsf);
filterRegistrationBean.setUrlPatterns(Arrays.asList(DSConfig.DRUID_FILTER_URL));
filterRegistrationBean.setInitParameters(
Collections.singletonMap("exclusions", DSConfig.DRUID_FILTER_EXCLUSIONS));
return filterRegistrationBean;
} }

根据类路径下有DruidDataSource这个类即有Druid这个jar包和配置文件中spring.datasource.active=druid才开启对Druid连接池的自动配置。

导入的配置文件:

@Configuration
@ComponentScan(basePackages = "com.example.common.config.properties")
public class PropertiesConfig { }

DruidMasterProperties、DruidSlaveProperties属性文件读取的配置省略。

连接池监控配置类:

public class DruidStatViewServlet extends StatViewServlet {

	private static final long serialVersionUID = 1L;

	private String username;
private String password; @Override
public String getInitParameter(String name) {
if ("loginUsername".equals(name)) {
return username;
} if ("loginPassword".equals(name)) {
return password;
} return super.getInitParameter(name);
} public DruidStatViewServlet(String username, String password) {
super();
this.username = username;
this.password = password;
} public String getUsername() {
return username;
} public String getPassword() {
return password;
} }

在META-INF/spring.factories中加入Druid自动配置映射:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.common.config.ds.DruidAutoConfig

切换数据源

切换数据源注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DS {
String value() default DSConfig.DB_MASTER;
}

动态数据源类:

public class DynamicDataSource extends AbstractRoutingDataSource {

	private final Logger logger = LoggerUtils.getLogger(this);

	@Override
protected Object determineCurrentLookupKey() {
logger.debug("当前数据源为{}", DataSourceContextHolder.getDS());
return DataSourceContextHolder.getDS();
} }

动态数据源AOP实现类:

@Aspect
@Component
public class DynamicDataSourceAspect { @Before("@annotation(DS)")
public void beforeSwitchDS(JoinPoint point) {
Class<?> className = point.getTarget().getClass();
String methodName = point.getSignature().getName();
Class<?>[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DEFAULT_DS; try {
Method method = className.getMethod(methodName, argClass);
if (method.isAnnotationPresent(DS.class)) {
DS annotation = method.getAnnotation(DS.class);
dataSource = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
DataSourceContextHolder.setDS(dataSource);
} @After("@annotation(DS)")
public void afterSwitchDS(JoinPoint point) {
DataSourceContextHolder.clearDS();
} }

绑定当前线程数据源类:

public class DataSourceContextHolder {

	public static final String DEFAULT_DS = DSConfig.DB_MASTER;

	private static final ThreadLocal<String> DS_HOLDER = new ThreadLocal<>();

	public static void setDS(String dbType) {
DS_HOLDER.set(dbType);
} public static String getDS() {
return (DS_HOLDER.get());
} public static void clearDS() {
DS_HOLDER.remove();
}
}

推荐:Spring Boot & Cloud 最强技术教程

Spring Boot 集成 Mybatis 实现双数据源的更多相关文章

  1. Spring Boot 集成Mybatis实现多数据源

    静态的方式 我们以两套配置方式为例,在项目中有两套配置文件,两套mapper,两套SqlSessionFactory,各自处理各自的业务,这个两套mapper都可以进行增删改查的操作,在这两个主MYS ...

  2. Spring Boot集成MyBatis开发Web项目

    1.Maven构建Spring Boot 创建Maven Web工程,引入spring-boot-starter-parent依赖 <project xmlns="http://mav ...

  3. 详解Spring Boot集成MyBatis的开发流程

    MyBatis是支持定制化SQL.存储过程以及高级映射的优秀的持久层框架,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集. spring Boot是能支持快速创建Spring应用的Java框 ...

  4. 【spring boot】14.spring boot集成mybatis,注解方式OR映射文件方式AND pagehelper分页插件【Mybatis】pagehelper分页插件分页查询无效解决方法

    spring boot集成mybatis,集成使用mybatis拖沓了好久,今天终于可以补起来了. 本篇源码中,同时使用了Spring data JPA 和 Mybatis两种方式. 在使用的过程中一 ...

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

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

  6. spring boot集成mybatis(1)

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  7. spring boot集成mybatis(2) - 使用pagehelper实现分页

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  8. spring boot集成mybatis(3) - mybatis generator 配置

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  9. spring boot集成MyBatis 通用Mapper 使用总结

    spring boot集成MyBatis 通用Mapper 使用总结 2019年 参考资料: Spring boot集成 MyBatis 通用Mapper SpringBoot框架之通用mapper插 ...

随机推荐

  1. js----作用域链

    作用域链是javascript的一个难点,要了解它就要了解作用域.变量.执行环境.生命周期等. 下面是找的资料加总结,加深理解. 作用域 变量的作用域可分为 A:全局作用域----最外层函数定义的变量 ...

  2. IP路由实验之---Telnet远程登陆

    实验设备:一台华三路由器,一台PC 骤一,为路由器端口配置 IP 地址 <H3C>system-view #进入系统视图 [H3C] / #进入0/0端口 [H3C-Ethernet-/] ...

  3. discuz代码转为html代码

    下面附件是来自discuz的一个函数文件(原来是在source/function/function_discuzcode.php位置),已稍微修改: https://files.cnblogs.com ...

  4. Dart 基础重点截取 Dart 2 20180417

    官网教程 https://www.dartlang.org/guides/language/language-tour dart是一个单线程的语言,没有多线程 Final and const If y ...

  5. Linux驱动之输入子系统简析

    输入子系统由驱动层.输入子系统核心.事件处理层三部分组成.一个输入事件,如鼠标移动.键盘按下等通过Driver->Inputcore->Event handler->userspac ...

  6. 更改angular的默认端口

    一.现象 当本地同时运行了多个angular项目时,端口占用问题 Port 4200 is already in use. Use '--port' to specify a different po ...

  7. 通过Solrj实现对索引库中数据的局部更新操作

    for (UpdateIndexDTO updateIndexDTO : data) { // 局部更新 SolrInputDocument doc = new SolrInputDocument() ...

  8. MySQL优化(四) 慢查询的定位及优化

    一.SQL语句优化的一般步骤: (1)通过 show status 命令了解各种 SQL 的执行效率: (2)定位执行效率较低的 SQL 语句(重点是 Select): (3)通过 explain 分 ...

  9. Python之路(第二十九篇) 面向对象进阶:内置方法补充、异常处理

    一.__new__方法 __init__()是初始化方法,__new__()方法是构造方法,创建一个新的对象 实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法 __ ...

  10. leveldb 学习记录(二) Slice

    基本每个KV库都有一个简洁的字符串管理类 比如redis的sds  比如leveldb的slice 管理一个字符串指针和数据长度 通过对字符串指针 长度的管理实现一般的创建 判断是否为空 获取第N个位 ...