这里用到了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();
}
}

推荐阅读

干货:2TB架构师四阶段视频教程

面经:史上最全Java多线程面试题及答案

面经:史上最全阿里高级Java面试题

面经:史上最全Spring面试题

教程:最全Spring Boot全套视频教程

书籍:进阶Java架构师必看的15本书

工具:推荐一款在线创作流程图、思维导图软件

分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。

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

  1. Spring boot 与mybatis 多数据源问题

    https://www.cnblogs.com/ityouknow/p/6102399.html Spring Boot 集成Mybatis实现多数据源 https://blog.csdn.net/m ...

  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集成mybatis(1)

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

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

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

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

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

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

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

  9. spring boot集成mybatis只剩两个sql 并提示 Cannot obtain primary key information from the database, generated objects may be incomplete

    前言 spring boot集成mybatis时只生成两个sql, 搞了一个早上,终于找到原因了 找了很多办法都没有解决, 最后注意到生成sql的时候打印了一句话: Cannot obtain pri ...

随机推荐

  1. 关于曲线 规划 算法 线性 S曲线 贝塞尔曲线

    工控领域经常会涉及速度加减速的算法:线性加减速,S曲线加减速(sin函数,拓展其他三角函数曲线), 贝塞尔曲线,等等. 线性加减速:    设定起始速度V0,目标速度V1,加速时间Ta(s,或加速度) ...

  2. Java实验报告(四)&第六周学习总结

    班级 计科二班 学号 20188425 姓名 IM 完成时间2019/10/07 评分等级 一.实验目的 (1)掌握类的继承 (2)变量的继承和覆盖,方法的继承,重载和覆盖的实现: 二.实验的内容 ( ...

  3. c# .netframwork 4.0 调用 2.0时报错 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集。

    “System.IO.FileLoadException”类型的未经处理的异常在 XXX.dll 中发生 其他信息: 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的 ...

  4. thinkphp中的exp查询

    今天遇到一个问题,就是在vendor表中查询出vendor_id = vendor_f_id的数据,其实使用原生的sql语句是非常简单的: select * from vendor where ven ...

  5. PHP 算式验证码

    这里不多说,直接上代码! /** * 改造的加减法验证类 * 使用示例 VerifyCode::get('xxx', 20); * 验证示例 VerifyCode::check('1', 'xxx') ...

  6. 后端数据推送-EventSource

    服务器发送事件(以下简称SSE)是HTML 5规范的一个组成部分,可以实现服务器到客户端的单向数据通信.通过SSE,客户端可以自动获取数据更新,而不用重复发送HTTP请求.一旦连接建立,“事件”便会自 ...

  7. SpringBoot集成Swagger,Postman,newman,jenkins自动化测试.

    环境:Spring Boot,Swagger,gradle,Postman,newman,jenkins SpringBoot环境搭建. Swagger简介 Swagger 是一款RESTFUL接口的 ...

  8. PHP字符串操作函数练习20191025

    <?php$arr=["tom","peter","mary"];$str=implode("+",$arr);/ ...

  9. Android中Parcelable的原理和使用方法

    Parcelable的简单介绍 介绍Parcelable不得不先提一下Serializable接口,Serializable是Java为我们提供的一个标准化的序列化接口,那什么是序列化呢? 进行And ...

  10. 转:动态库路径配置- /etc/ld.so.conf文件

    Linux 共享库 Linux 系统上有两类根本不同的 Linux 可执行程序.第一类是静态链接的可执行程序.静态可执行程序包含执行所需的所有函数 — 换句话说,它们是“完整的”.因为这一原因,静态可 ...