文档章节

Spring Boot 集成 Mybatis 实现双数据源

Java技术栈
 Java技术栈
发布于 2018/06/08 09:24
字数 924
阅读 156
收藏 16

这里用到了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 最强技术教程

扫描关注我们的微信公众号,干货每天更新。

image

© 著作权归作者所有

Java技术栈
粉丝 187
博文 180
码字总数 160142
作品 0
深圳
架构师
私信 提问
Spring Boot整合MyBatis学习总结

公司的很多项目都陆陆续续引入了Spring Boot,通过对Spring Boot的接触了解发现其真的是大大地简化了开发、简化了依赖配置,很多功能注解一下就可以实现,真的是太方便了。下面记录了一个Spr...

zhuwensheng
2018/06/29
0
0
mybatisplus-spring-boot-starter 1.0.2 发布,代号:清风

mybatisplus-boot-starter 为 mybaits-plus 快速集成 spring-boot 简化配置而生,让您不在为对比 myBatis 和 hibernate 而烦恼 mybatis-plus 作为 mybatis 的好拍档补充了它的短板,让 myba...

青苗
2017/06/26
1K
8
springboot集成mybatis

springboot集成mybatis application.yml 从哪里找到这些配置项: springboot 自己的配置项 https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties......

黄威
2018/07/09
169
0
苞米豆多数据源启动器 2.0.1 发布,Bug 修复版本

苞米豆多数据源启动器 2.0.1 发布了。强烈建议升级!更新内容: 修复一个方法缓存的bug,会引起同名方法的注解失效。 底层代码的重命名和部分格式的调整。 源码地址: https://gitee.com/ba...

小锅盖
2018/08/09
744
4
Spring Boot学习笔记

多模块开发 [SpringBoot学习]-IDEA创建Gradle多Module结构的SpringBoot项目 RabbitMQ RabbitMQ 安装 linux安装RabbitMQ详细教程 Ubuntu 16.04 RabbitMq 安装与运行(安装篇) ubantu安装...

OSC_fly
2018/07/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

AliOS Things 3.0 应用开发指南

目录 应用开发框架介绍 使用条件 快速开始 第一步:下载AliOS Things 3.0源码 第二步:添加AOS_SDK_PATH环境变量 第三步:AliOS Studio中创建应用工程 编译、烧录、调试 其他说明 参考文档 ...

阿里云官方博客
38分钟前
3
0
日期和月份的计算

需求一 根据 【首次任务开始时间】和【任务间隔时间】和【每个任务持续时间】和【任务次数】计算出每个任务的时间 // 数据计算方法 async dateCalculation() { const firstD...

沉迷代码我爱学习
43分钟前
2
0
Spring Cloud Gateway 之请求坑位[微服务IP不同请求会失败]

问题产生背景 在使用Spring Cloud Gateway过程中,希望配置多Routes映射不同的微服务,因为Gateway 和Zuul的访问路径不同(zuul 会带有服务service Id),造成错误。 现象表现 问题定位 认为是...

IsaacZhang
54分钟前
5
0
Vue nodejs商城项目-搭建express框架环境

本文转载于:专业的前端网站➯Vue nodejs商城项目-搭建express框架环境 1.express-project 搭建express框架环境 安装express generator生成器 通过生成器自动创建项目 配置分析 安装 cnpm i -...

前端老手
今天
3
0
maven项目A引入maven项目B的jar包

首先打开 项目B 的 pom 文件,加入如下配置 <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin<......

嘴角轻扬30
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部