Spring Boot 集成 Mybatis 实现双数据源

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

添加依赖

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

1<dependency> 2 <groupId>org.mybatis.spring.boot</groupId> 3 <artifactId>mybatis-spring-boot-starter</artifactId> 4</dependency> 5 6<dependency> 7 <groupId>com.alibaba</groupId> 8 <artifactId>druid</artifactId> 9</dependency> 10 11<dependency> 12 <groupId>com.oracle</groupId> 13 <artifactId>ojdbc6</artifactId> 14</dependency> 15

添加启动类

1@EnableMybatis 2@EnableTransactionManagement 3@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) 4public class Application { 5 6 public static void main(String[] args) { 7 SpringApplication.run(ServiceApplication.class, args); 8 } 9 10}

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }): 这里用到了双数据源,需要排除数据源的自动配置,如果只有一个数据源用Spring Boot的自动配置就行。

@EnableTransactionManagement:开启事务支持。

@EnableMybatis:开启Mybatis功能

1@Target(ElementType.TYPE) 2@Retention(RetentionPolicy.RUNTIME) 3@Documented 4@Import(MybatisConfig.class) 5public @interface EnableMybatis { 6 7}

Mybatis配置类

1@Configuration 2@MapperScan(basePackages = DSConfig.BASE_PACKAGES) 3public class MybatisConfig implements DSConfig { 4 5 @Primary 6 @Bean 7 public DynamicDataSource dynamicDataSource(@Qualifier(DB_MASTER) DataSource master, 8 @Qualifier(DB_SLAVE) DataSource slave) { 9 Map<Object, Object> dsMap = new HashMap<>(); 10 dsMap.put(DB_MASTER, master); 11 dsMap.put(DB_MASTER, slave); 12 13 DynamicDataSource dynamicDataSource = new DynamicDataSource(); 14 dynamicDataSource.setDefaultTargetDataSource(master); 15 dynamicDataSource.setTargetDataSources(dsMap); 16 return dynamicDataSource; 17 } 18 19 @Bean 20 public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource) { 21 return new DataSourceTransactionManager(dynamicDataSource); 22 } 23 24 @Bean 25 public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) 26 throws Exception { 27 SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); 28 sessionFactory.setDataSource(dynamicDataSource); 29 sessionFactory.setMapperLocations( 30 ((ResourcePatternResolver) new PathMatchingResourcePatternResolver()) 31 .getResources(DSConfig.MAPPER_LOCATIONS)); 32 return sessionFactory.getObject(); 33 } 34 35}

DSConfig常量类:

1public interface DSConfig { 2 3 String DS_PREFIX = "spring.datasource"; 4 String DS_ACTIVE = "active"; 5 6 String DB_MASTER = "db-master"; 7 String DB_SLAVE = "db-slave"; 8 9 String DRUID = "druid"; 10 11 String DRUID_MONITOR_USERNAME = "spring.druid.username"; 12 String DRUID_MONITOR_PASSWORD = "spring.druid.password"; 13 String DRUID_MONITOR_URL = "/druid/*"; 14 String DRUID_FILTER_EXCLUSIONS = "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"; 15 String DRUID_FILTER_URL = "/*"; 16 17 String BASE_PACKAGES = "com.example.**.mapper"; 18 String MAPPER_LOCATIONS = "mapper/**/*.xml"; 19 20}

连接池配置类

Druid连接池的自动配置类:

1@Configuration 2@Import({ PropertiesConfig.class }) 3@ConditionalOnClass(DruidDataSource.class) 4@ConditionalOnProperty(prefix = DSConfig.DS_PREFIX, value = DSConfig.DS_ACTIVE, havingValue = DSConfig.DRUID) 5public class DruidAutoConfig implements DSConfig { 6 7 private Logger logger = LoggerUtils.getLogger(this); 8 9 @Bean(name = DB_MASTER, initMethod = "init", destroyMethod = "close") 10 public DataSource dataSourceMaster(DruidMasterProperties masterProperties) throws SQLException { 11 logger.debug("master properties: {}", masterProperties.toString()); 12 13 DruidDataSource dds = new DruidDataSource(); 14 dds.setDriverClassName(masterProperties.getDriverClassName()); 15 dds.setUrl(masterProperties.getUrl()); 16 dds.setUsername(masterProperties.getUsername()); 17 dds.setPassword(masterProperties.getPassword()); 18 dds.setInitialSize(masterProperties.getInitialSize()); 19 dds.setMinIdle(masterProperties.getMinIdle()); 20 dds.setMaxActive(masterProperties.getMaxActive()); 21 dds.setMaxWait(masterProperties.getMaxWait()); 22 dds.setTimeBetweenEvictionRunsMillis(masterProperties.getTimeBetweenEvictionRunsMillis()); 23 dds.setMinEvictableIdleTimeMillis(masterProperties.getMinEvictableIdleTimeMillis()); 24 dds.setValidationQuery(masterProperties.getValidationQuery()); 25 dds.setTestOnBorrow(masterProperties.isTestOnBorrow()); 26 dds.setTestWhileIdle(masterProperties.isTestWhileIdle()); 27 dds.setTestOnReturn(masterProperties.isTestOnReturn()); 28 dds.setPoolPreparedStatements(masterProperties.isPoolPreparedStatements()); 29 dds.setMaxPoolPreparedStatementPerConnectionSize( 30 masterProperties.getMaxPoolPreparedStatementPerConnectionSize()); 31 dds.setFilters(masterProperties.getFilters()); 32 33 return dds; 34 } 35 36 @Bean(name = DB_SLAVE, initMethod = "init", destroyMethod = "close") 37 public DataSource dataSourceSlave(DruidSlaveProperties slaveProperties) throws SQLException { 38 logger.debug("slave properties: {}", slaveProperties.toString()); 39 40 DruidDataSource dds = new DruidDataSource(); 41 dds.setDriverClassName(slaveProperties.getDriverClassName()); 42 dds.setUrl(slaveProperties.getUrl()); 43 dds.setUsername(slaveProperties.getUsername()); 44 dds.setPassword(slaveProperties.getPassword()); 45 dds.setInitialSize(slaveProperties.getInitialSize()); 46 dds.setMinIdle(slaveProperties.getMinIdle()); 47 dds.setMaxActive(slaveProperties.getMaxActive()); 48 dds.setMaxWait(slaveProperties.getMaxWait()); 49 dds.setTimeBetweenEvictionRunsMillis(slaveProperties.getTimeBetweenEvictionRunsMillis()); 50 dds.setMinEvictableIdleTimeMillis(slaveProperties.getMinEvictableIdleTimeMillis()); 51 dds.setValidationQuery(slaveProperties.getValidationQuery()); 52 dds.setTestOnBorrow(slaveProperties.isTestOnBorrow()); 53 dds.setTestWhileIdle(slaveProperties.isTestWhileIdle()); 54 dds.setTestOnReturn(slaveProperties.isTestOnReturn()); 55 dds.setPoolPreparedStatements(slaveProperties.isPoolPreparedStatements()); 56 dds.setMaxPoolPreparedStatementPerConnectionSize( 57 slaveProperties.getMaxPoolPreparedStatementPerConnectionSize()); 58 dds.setFilters(slaveProperties.getFilters()); 59 60 return dds; 61 } 62 63 @Bean 64 public ServletRegistrationBean druidServletRegistrationBean(EnvConfig env) { 65 String username = env.getStringValue(DSConfig.DRUID_MONITOR_USERNAME); 66 String password = env.getStringValue(DSConfig.DRUID_MONITOR_PASSWORD); 67 return new ServletRegistrationBean(new DruidStatViewServlet(username, password), 68 DSConfig.DRUID_MONITOR_URL); 69 } 70 71 @Bean 72 public FilterRegistrationBean druidFilterRegistrationBean() { 73 WebStatFilter wsf = new WebStatFilter(); 74 FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); 75 filterRegistrationBean.setFilter(wsf); 76 filterRegistrationBean.setUrlPatterns(Arrays.asList(DSConfig.DRUID_FILTER_URL)); 77 filterRegistrationBean.setInitParameters( 78 Collections.singletonMap("exclusions", DSConfig.DRUID_FILTER_EXCLUSIONS)); 79 return filterRegistrationBean; 80 } 81 82}

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

导入的配置文件:

1@Configuration 2@ComponentScan(basePackages = "com.example.common.config.properties") 3public class PropertiesConfig { 4 5}

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

连接池监控配置类:

1public class DruidStatViewServlet extends StatViewServlet { 2 3 private static final long serialVersionUID = 1L; 4 5 private String username; 6 private String password; 7 8 @Override 9 public String getInitParameter(String name) { 10 if ("loginUsername".equals(name)) { 11 return username; 12 } 13 14 if ("loginPassword".equals(name)) { 15 return password; 16 } 17 18 return super.getInitParameter(name); 19 } 20 21 public DruidStatViewServlet(String username, String password) { 22 super(); 23 this.username = username; 24 this.password = password; 25 } 26 27 public String getUsername() { 28 return username; 29 } 30 31 public String getPassword() { 32 return password; 33 } 34 35}

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

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

切换数据源

切换数据源注解:

1@Target(ElementType.METHOD) 2@Retention(RetentionPolicy.RUNTIME) 3@Documented 4public @interface DS { 5 String value() default DSConfig.DB_MASTER; 6}

动态数据源类:

1public class DynamicDataSource extends AbstractRoutingDataSource { 2 3 private final Logger logger = LoggerUtils.getLogger(this); 4 5 @Override 6 protected Object determineCurrentLookupKey() { 7 logger.debug("当前数据源为{}", DataSourceContextHolder.getDS()); 8 return DataSourceContextHolder.getDS(); 9 } 10 11}

动态数据源AOP实现类:

1@Aspect 2@Component 3public class DynamicDataSourceAspect { 4 5 @Before("@annotation(DS)") 6 public void beforeSwitchDS(JoinPoint point) { 7 Class<?> className = point.getTarget().getClass(); 8 String methodName = point.getSignature().getName(); 9 Class<?>[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes(); 10 String dataSource = DataSourceContextHolder.DEFAULT_DS; 11 12 try { 13 Method method = className.getMethod(methodName, argClass); 14 if (method.isAnnotationPresent(DS.class)) { 15 DS annotation = method.getAnnotation(DS.class); 16 dataSource = annotation.value(); 17 } 18 } catch (Exception e) { 19 e.printStackTrace(); 20 } 21 DataSourceContextHolder.setDS(dataSource); 22 } 23 24 @After("@annotation(DS)") 25 public void afterSwitchDS(JoinPoint point) { 26 DataSourceContextHolder.clearDS(); 27 } 28 29}

绑定当前线程数据源类:

1public class DataSourceContextHolder { 2 3 public static final String DEFAULT_DS = DSConfig.DB_MASTER; 4 5 private static final ThreadLocal<String> DS_HOLDER = new ThreadLocal<>(); 6 7 public static void setDS(String dbType) { 8 DS_HOLDER.set(dbType); 9 } 10 11 public static String getDS() { 12 return (DS_HOLDER.get()); 13 } 14 15 public static void clearDS() { 16 DS_HOLDER.remove(); 17 } 18}

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

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

image

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )