Springboot+Mybatis+Pagehelper+Aop动态配置Oracle、Mysql数据源

用公司新搭的maven脚手架创建springboot工程,因为脚手架功能未完善,创建出的工程主要就是引了springboot基础包并创建了目录结构,所以需要自己添加框架来搭建工程,也能通过这个过程来更深入了解相关框架,提升自己。
* springboot程序入口:TianlianModelServerApplication.java

package com.tianlian.server;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource; //因为用springboot默认的数据源只能配置一套,而我们需要从多个数据源来查询数据,
//因此用@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})来关闭自动配置功能。
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
//用@PropertySource来加载properties文件
@PropertySource(value={"classpath:dev/properties/dubbo.properties","classpath:dev/properties/mysql.properties","classpath:dev/properties/oracle.properties"})
//加载xml配置文件
@ImportResource(locations={"classpath:configs/dubbo-customers.xml","classpath:configs/dubbo-server.xml"})
@ComponentScan(basePackages = "com.tianlian.server")
//开启AOP代理自动配置
@EnableAspectJAutoProxy
public class TianlianModelServerApplication { public static void main(String[] args) {
SpringApplication.run(TianlianModelServerApplication.class, args);
}
}
  • 读取mysql配置:MySqlDataSourceConfig.java(OracleDataSourceConfig.java类似)
package com.tianlian.server.configs;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component; /**
*用@Value注解来给属性赋值,分别创建oracle数据源的bean、mysql数据源的bean,
*并用@componen把bean交给spring管理,方便后面创建不同数据源时使用。
*/
@Data
@Component
public class MySqlDataSourceConfig { @Value("${mysql.jdbc.driverClassName}")
private String driverClassName; @Value("${mysql.jdbc.url}")
private String url; @Value("${mysql.jdbc.username}")
private String username; @Value("${mysql.jdbc.password}")
private String password; @Value("${mysql.jdbc.initialSize}")
private Integer initialSize; @Value("${mysql.jdbc.maxActive}")
private Integer maxActive; @Value("${mysql.jdbc.minPoolSize}")
private Integer minPoolSize; @Value("${mysql.jdbc.maxWait}")
private Long maxWait; @Value("${mysql.jdbc.minIdle}")
private Integer minIdle; @Value("${mysql.jdbc.timeBetweenEvictionRunsMillis}")
private Long timeBetweenEvictionRunsMillis; @Value("${mysql.jdbc.minEvictableIdleTimeMillis}")
private Long minEvictableIdleTimeMillis; @Value("${mysql.jdbc.validationQuery}")
private String validationQuery; @Value("${mysql.jdbc.testWhileIdle}")
private Boolean testWhileIdle; @Value("${mysql.jdbc.testOnBorrow}")
private Boolean testOnBorrow; @Value("${mysql.jdbc.testOnReturn}")
private Boolean testOnReturn; @Value("${mysql.jdbc.maxOpenPreparedStatements}")
private Integer maxOpenPreparedStatements; @Value("${mysql.jdbc.removeAbandoned}")
private Boolean removeAbandoned; @Value("${mysql.jdbc.removeAbandonedTimeout}")
private Integer removeAbandonedTimeout; @Value("${mysql.jdbc.logAbandoned}")
private Boolean logAbandoned; @Value("${mysql.jdbc.poolPreparedStatements}")
private Boolean poolPreparedStatements; @Value("${mysql.jdbc.filters}")
private String filters;
}
  • 配置mybatis:MyBatisConfigNew.java
package com.tianlian.server.configs;

import com.alibaba.druid.pool.DruidDataSource;
import com.github.pagehelper.PageHelper;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
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.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager; @Configuration
@MapperScan(basePackages = "com.tianlian.server.dao.mapper")
public class MyBatisConfigNew { @Autowired
private MySqlDataSourceConfig mysqlJdbcMapper; @Autowired
private OracleDataSourceConfig oracleJdbcMapper; /**
*根据oracle和mysql的配置属性bean分别创建oracle数据源bean、mysql数据源bean
*/
@Bean
public DataSource mysqlDataSource() throws Exception {
DruidDataSource datasource = new DruidDataSource(); datasource.setUrl(mysqlJdbcMapper.getUrl());
datasource.setUsername(mysqlJdbcMapper.getUsername());
datasource.setPassword(mysqlJdbcMapper.getPassword());
datasource.setDriverClassName(mysqlJdbcMapper.getDriverClassName());
datasource.setInitialSize(mysqlJdbcMapper.getInitialSize());
datasource.setMinIdle(mysqlJdbcMapper.getMinIdle());
datasource.setMaxActive(mysqlJdbcMapper.getMaxActive());
datasource.setMaxWait(mysqlJdbcMapper.getMaxWait());
datasource.setTimeBetweenEvictionRunsMillis(mysqlJdbcMapper.getTimeBetweenEvictionRunsMillis());
datasource.setMinEvictableIdleTimeMillis(mysqlJdbcMapper.getMinEvictableIdleTimeMillis());
datasource.setValidationQuery(mysqlJdbcMapper.getValidationQuery());
datasource.setTestWhileIdle(mysqlJdbcMapper.getTestWhileIdle());
datasource.setTestOnBorrow(mysqlJdbcMapper.getTestOnBorrow());
datasource.setTestOnReturn(mysqlJdbcMapper.getTestOnReturn());
datasource.setPoolPreparedStatements(mysqlJdbcMapper.getPoolPreparedStatements());
return datasource;
} @Bean
public DataSource oracleDataSource() throws Exception {
DruidDataSource datasource = new DruidDataSource(); datasource.setUrl(oracleJdbcMapper.getUrl());
datasource.setUsername(oracleJdbcMapper.getUsername());
datasource.setPassword(oracleJdbcMapper.getPassword());
datasource.setDriverClassName(oracleJdbcMapper.getDriverClassName());
datasource.setInitialSize(oracleJdbcMapper.getInitialSize());
datasource.setMinIdle(oracleJdbcMapper.getMinIdle());
datasource.setMaxActive(oracleJdbcMapper.getMaxActive());
datasource.setMaxWait(oracleJdbcMapper.getMaxWait());
datasource
.setTimeBetweenEvictionRunsMillis(oracleJdbcMapper.getTimeBetweenEvictionRunsMillis());
datasource.setMinEvictableIdleTimeMillis(oracleJdbcMapper.getMinEvictableIdleTimeMillis());
datasource.setValidationQuery(oracleJdbcMapper.getValidationQuery());
datasource.setTestWhileIdle(oracleJdbcMapper.getTestWhileIdle());
datasource.setTestOnBorrow(oracleJdbcMapper.getTestOnBorrow());
datasource.setTestOnReturn(oracleJdbcMapper.getTestOnReturn());
datasource.setPoolPreparedStatements(oracleJdbcMapper.getPoolPreparedStatements());
return datasource;
} /**
* 创建动态数据源,将上面创建的数据源交给动态数据源管理(即放到抽象类AbstractRoutingDataSource中的targetDataSources管理)
*/
@Bean
@Primary
public DynamicDataSource dataSource(
@Qualifier("mysqlDataSource") DataSource mysqlDataSource,
@Qualifier("oracleDataSource") DataSource oracleDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.mysqlDb, mysqlDataSource);
targetDataSources.put(DatabaseType.oracleDb, oracleDataSource);
DynamicDataSource dataSource = new DynamicDataSource();
// 该方法是AbstractRoutingDataSource的方法
dataSource.setTargetDataSources(targetDataSources);
// 默认的datasource设置为myTestDbDataSource
dataSource.setDefaultTargetDataSource(mysqlDataSource);
return dataSource;
} /**
*将动态数据源交给mybatis的SqlSessionFactoryBean,并添加PageHelper分页插件。
*因为要切换数据源,必须要把PageHelper的autoRuntimeDialect属性设置为true才能在不同类新的数据源切换时,
*使用不同数据源的分页方式。
*/
@Bean
public SqlSessionFactory sqlSessionFactory(
@Qualifier("mysqlDataSource") DataSource mysqlDataSource,
@Qualifier("oracleDataSource") DataSource oracleDataSource) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(this.dataSource(mysqlDataSource, oracleDataSource));
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
//分页插件
PageInterceptor pageHelper = new
PageInterceptor();
Properties properties = new Properties();
properties.setProperty("reasonable", "true");
properties.setProperty("supportMethodsArguments", "true");
properties.setProperty("returnPageInfo", "check");
properties.setProperty("params", "count=countSql");
properties.setProperty("autoRuntimeDialect", "true");
pageHelper.setProperties(properties);
//添加插件
fb.setPlugins(new Interceptor[]{pageHelper});
//添加XML目录
try {
fb.setMapperLocations(resolver.getResources("classpath:mapper/**/*.xml"));
return fb.getObject();
} catch (Exception e)
{
e.printStackTrace(); throw new RuntimeException(e); }
}
@Bean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory)
{ return new SqlSessionTemplate(sqlSessionFactory); } /** * 配置事务管理器 */
@Bean public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource)
throws Exception { return new DataSourceTransactionManager(dataSource); } }
  • 用ThreadLocal为每个线程保存各自选择的数据源:DataSourceContextHolder.java
package com.tianlian.server.configs;

public class DataSourceContextHolder {

  /**
* 默认数据源
*/
public static final DatabaseType DEFAULT_DS = DatabaseType.mysqlDb;
//public static final DatabaseType DEFAULT_DS = DatabaseType.oracleDb; private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>(); public static void setDatabaseType(DatabaseType type) {
contextHolder.set(type);
} public static DatabaseType getDatabaseType() {
return contextHolder.get();
} public static void clearDatabaseType() {
contextHolder.remove();
}
}
  • 继承抽象类AbstractRoutingDataSource实现抽象方法——选择切换到哪个数据源的方法:DynamicDataSource.java
package com.tianlian.server.configs;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

  @Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDatabaseType();
} }
  • 定义一个注解DS,后面用它来标识用哪个数据源来查询:DS.java
package com.tianlian.server.configs;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD
})
public @interface DS { DatabaseType value() default DatabaseType.mysqlDb; }
  • 利用spring的aop来实现根据注解来动态切换数据源的动作:DynamicDataSourceAspect.java
package com.tianlian.server.configs;

import java.lang.reflect.Method;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; @Aspect
@Component
public class DynamicDataSourceAspect { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class); //首先要定义一个切点——mapper接口下的所有查询方法
@Pointcut(value = "execution(* com.tianlian.server.dao.mapper.*.*(..))")
private void pointcut() {
} @Around("pointcut()")
public void around(ProceedingJoinPoint point) throws Throwable {
DataSourceContextHolder.clearDatabaseType();
Object target = point.getTarget();
String method = point.getSignature().getName();
Class<?>[] classz = target.getClass().getInterfaces();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
//设置默认数据源
DatabaseType dataSource = DataSourceContextHolder.DEFAULT_DS;
String methodName = "";
try {
Method m = classz[0].getMethod(method, parameterTypes);
//根据方法上的DS注解的值来设置数据源
if (m != null && m.isAnnotationPresent(DS.class)) {
DS annotation = m
.getAnnotation(DS.class);
dataSource = annotation.value();
methodName = m.getName();
}
} catch (Exception e) {
logger.error("DataSource switch error:{}", e.getMessage(), e);
} finally {
logger.info("{} | method {} | datasource {} | begin",
((MethodSignature) point.getSignature()).getMethod().getDeclaringClass(), methodName,
dataSource);
}
DataSourceContextHolder.setDatabaseType(dataSource);
point.proceed();
DataSourceContextHolder.clearDatabaseType();
logger.info("{} | method {} | datasource {} | end",
((MethodSignature) point.getSignature()).getMethod().getDeclaringClass(), methodName,
dataSource);
} }
  • 接下来就可以写测试用的查询接口来尝试一下数据切换功能了。

如果有写的不好的地方欢迎拍砖

源码地址:https://github.com/weijiayou/Springboot-DynamicDataSource-Demo

Springboot+Mybatis+Pagehelper+Aop动态配置Oracle、Mysql数据源的更多相关文章

  1. 基于springboot通过注解AOP动态切换druid多数据源--mybatis

    控制于接口之上: 开始:demo地址  在lsr-core-base中 自定义注解: /** * @Description: 数据源切换注解 * @Package: lsr-microservice ...

  2. SpringBoot+Mybatis+PageHelper实现分页

    SpringBoot+Mybatis+PageHelper实现分页 mybatis自己没有分页功能,我们可以通过PageHelper工具来实现分页,非常简单方便 第一步:添加依赖 <depend ...

  3. springboot+mybatis+pagehelper

    springboot+mybatis+pagehelper整合 springboot   版本2.1.2.RELEASE mybatis  版本3.5 pagehelper 版本5.18 支持在map ...

  4. 【优雅写代码系统】springboot+mybatis+pagehelper+mybatisplus+druid教你如何优雅写代码

    目录 spring基本搭建 整合mybatis pom配置 mybatis配置 设置数据源 设置sqlsessionfactory 设置扫描 设置开启事务 资源放行 测试 结果 思考&& ...

  5. spring-boot + mybatis +pagehelper 使用分页

    转自:https://segmentfault.com/a/1190000015668715?utm_medium=referral&utm_source=tuicool 最近自己搭建一个sp ...

  6. springboot整合Quartz实现动态配置定时任务

    前言 在我们日常的开发中,很多时候,定时任务都不是写死的,而是写到数据库中,从而实现定时任务的动态配置,下面就通过一个简单的示例,来实现这个功能. 一.新建一个springboot工程,并添加依赖 & ...

  7. SpringBoot+Mybatis+PageHelper简化分页实现

    前言 经过一段时间的测试和修改PageHelper插件逐渐走到了让我觉得靠谱的时候,它功能的就是简化分页的实现,让分页不需要麻烦的多写很多重复的代码. 已经加入我的github模版中:https:// ...

  8. springboot+mybatis+dubbo+aop日志第二篇

    本篇主要介绍dubbo-demo-api接口层和dubbo-demo-service层,以及如何通过dubbo把服务发布出去,介绍代码前,咱们先来回顾一下整个demo工程的结构,如下图所示: 1.du ...

  9. springboot+mybatis+dubbo+aop日志第一篇

    本篇文章主要讲述项目搭建过程,不会涉及过多的基础知识,本项目是作者对前段时间学习的一个总结,主要使用到技术有:maven父子工程.springboot.mybatis.dubbo.zookeeper. ...

随机推荐

  1. SpringBoot quartz定时器

    <!-- 案例1 --> <!-- 定时器 --> <bean name="CodeTest" class="com.aaa.bbb.con ...

  2. Ingress-Nginx

    注意:  本文只用的ingress-nginx版本为0.24.1,可在官方网站自行下载 https://github.com/kubernetes/ingress-nginx 一.Ingress-Ng ...

  3. JavaScript/JQuery自执行函数

    JavaScript中任何库与框架设计的第一个要点就是解决命名空间与变量污染的问题.jQuery就是利用了JavaScript函数作用域的特性,采用自执行函数包裹了自身的方法来解决这个问题.从jQue ...

  4. iOS开发xib控件删不掉,修改xib运行不发生改变,修改xib不管用

    修改xib控件tag值,颜色,大小,甚至删除发现编译.运行之后效果没改变,用代码修改内容发现管用, 其实只需要clean一下!^_^ 快捷键:shift + command + k

  5. selenium 加载出新的窗口

    加载出新的窗口的时候 在点击某一个按钮的时候 有些时候会加载出新的页面 此时直接定位是定位不到的 就比如一开始在 1窗口定位 后来跳转到了2窗口,需要在2窗口上定位元素,此时就要先切换到2窗口 这里引 ...

  6. 修改/etc/docker/daemon.json中的log-opts配置发现无效 docker 限制日志大小

    https://colobu.com/2018/10/22/no-space-left-on-device-for-docker/ 在/etc/docker/daemon.json中修改或添加log- ...

  7. Mybatis集成ehcache

    Mybatis集成ehcache 1.为什么需要缓存 拉高程序的性能 2. 什么样的数据需要缓存 很少被修改或根本不改的数据 业务场景比如:耗时较高的统计分析sql.电话账单查询sql等 3. ehc ...

  8. ESA2GJK1DH1K基础篇: 阿里云物联网平台: 测试MQTT客户端接收云平台的数据

    前言 有时候想想可能直接连接现成的感觉比较方便吧! 这种东西考验的是你底子是否够好,是否有很强的学习能力 因为咱就是看文档,理解文档.用文档. 测这节会感觉:这是啥呀...下一节更精彩,但是必须看这节 ...

  9. Django 1.11 bootstrap样式文件无法加载问题解决

    先吐槽一波,多看官方教程,多找对应版本解决方法,多思考!... 在调试模式下面,打开页面无法加载bootstrap.min.css样式,解决思路如下: 查看settings文件INSTALL_APP配 ...

  10. 基于 SpringBoot2.0+优雅整合 SpringBoot+Mybatis

    SpringBoot 整合 Mybatis 有两种常用的方式,一种就是我们常见的 xml 的方式 ,还有一种是全注解的方式.我觉得这两者没有谁比谁好,在 SQL 语句不太长的情况下,我觉得全注解的方式 ...