Spring boot+Mybatis-plus+多源数据库动态切换+druid 使用AOP动态切换

**

spring boot 2.x +mybats-plus实现动态数据库切换

**
配置文件

#tomcat配置
server.port=8888
server.servlet.session.timeout=1800m
server.tomcat.uri-encoding=UTF-8

#s数据压缩
server.compression.enabled=true
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain
server.compression.min-response-size=100

#mybatis插件配置
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.mapper-locations=classpath:mapper/*Mapper.xml
mybatis-plus.typeAliasesPackage=com.dfdk.datamanage.entity

#数据库连接池配置
#drive数据库
spring.datasource.druid.drive.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.drive.url=jdbc:mysql://192.168.43\
.117:3306/drive?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT
spring.datasource.druid.drive.username=root
spring.datasource.druid.drive.password=root
#scada数据库
#spring.datasource.scada.driver-class-name=oracle.jdbc.OracleDriver
#spring.datasource.url=jdbc:oracle:thin:@//172.20.51.66:1521/orcl
spring.datasource.druid.scada.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.druid.scada.url=jdbc:oracle:thin:@//127.0.0.1:1521/orcl
spring.datasource.druid.scada.username=system
spring.datasource.druid.scada.password=123456
spring.datasource.druid.initialSize=5
spring.datasource.druid.minIdle=5
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=60000
spring.datasource.druid.time-between-eviction-runs-millis=60000
spring.datasource.druid.min-evictable-idle-time-millis=300000
spring.datasource.druid.validation-query=SELECT 1 FROM DUAL
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
spring.datasource.druid.pool-prepared-statements=true
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20
spring.datasource.druid.filters=stat,wall,log4j2
spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;
spring.datasource.druid.filter.stat.slow-sql-millis=5000
# 合并多个DruidDataSource的监控数据
spring.datasource.type=com.alibaba.druid.pool.xa.DruidXADataSource
#jta相关参数配置
#spring.jta.log-dir=classpath:tx-logs
#spring.jta.transaction-manager-id=xatx
spring.datasource.druid.use-global-data-source-stat=true
#配置日志输出
spring.datasource.druid.filter.slf4j.enabled=true
spring.datasource.druid.filter.slf4j.statement-close-after-log-enabled=false
spring.datasource.druid.filter.slf4j.statement-create-after-log-enabled=false
spring.datasource.druid.filter.slf4j.result-set-close-after-log-enabled=false
spring.datasource.druid.filter.slf4j.result-set-open-after-log-enabled=false

创建DS注解类

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DS {
    String value() default "scada";
}

创建Confi用来配置多源数据库

@Configuration
public class DataSourceConfig {
    @Value("${spring.datasource.type:com.alibaba.druid.pool.xa.DruidXADataSource}")
    String xaDataSourceClassName;
    @Value("${mybatis-plus.mapper-locations}")
    private String mapperLocations;

    @Primary
    @Bean(name = "bigdataDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.druid.drive")
    public DataSource bigdataDataSource() {
        String sourceName = "drive";
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(((XADataSource) DataSourceBuilder.create().build()));
        xaDataSource.setXaDataSourceClassName(xaDataSourceClassName);
        xaDataSource.setUniqueResourceName(sourceName);
        xaDataSource.setPoolSize(5);
        return xaDataSource;
    }

    @Bean(name = "platDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.druid.scada")
    public DataSource platDataSource() {
        String sourceName = "scada";
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSourceClassName(xaDataSourceClassName);
        xaDataSource.setUniqueResourceName(sourceName);
        xaDataSource.setPoolSize(5);
        return xaDataSource;

    }

    // 配置数据源
    @Bean(name = "dynamicDataSource")
    public DynamicDataSource dataSource(@Qualifier("bigdataDataSource") DataSource bigdataDataSource,
                                        @Qualifier("platDataSource") DataSource platDataSource) {
        Map targetDataSources = new HashMap<>();
        targetDataSources.put("drive", bigdataDataSource);
        targetDataSources.put("scada", platDataSource);
        return new DynamicDataSource(bigdataDataSource, targetDataSources);
    }
    }

创建自定义的数据库类

public interface DataSourceNames {
    String DRIVE = "drive";
    String SCADA = "scada";
    String LOCATION = "location";

}

创建本地线程以及set,get来动态切换数据库

public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final ThreadLocal contextHolder = new ThreadLocal<>();
    public  static final String DEFAULT_SQL = "drive";
    public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

    public static String getDataSource() {
        return contextHolder.get();
    }

    public static void clearDataSource() {
        contextHolder.remove();
    }

}

利用AOP来完成动态数据库的切换

@Aspect
@Component
@Order(-100)
public class DynamicDataSourceAspect {
    private  static final Logger loger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
    /**
     * 前置增强,方法执行前,通过JoinPoint访问连接点上下文的信息
     *
     * @param joinPoint
     */
    @Before("@annotation(com.dfdk.datamanage.config.DS)")
    public void beforeSwitchDataSource(JoinPoint joinPoint) {
        // 获取连接点的方法签名对象
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取方法
        Method method = methodSignature.getMethod();
        // 设置默认的数据源为Master,防止切库出现异常执行失败的情况
        String dataSource = DynamicDataSource.DEFAULT_SQL;
        // 判断方法上是否标注了@RouteDataSource
        if (method.isAnnotationPresent(DS.class)) {
            DS routeDataSource = method.getDeclaredAnnotation(DS.class);
            // 获取@RouteDataSource上的value的值
            dataSource = routeDataSource.value();
        }
        // 设置数据源
        loger.info("当前切换到"+dataSource);
        DynamicDataSource.setDataSource(dataSource);
    }

    /**
     * 后置增强,清空DatasourceContextHolder,防止threadLocal误用带来的内存泄露
     */
    @After("@annotation(com.dfdk.datamanage.config.DS)")
    public void afterSwitchDataSource() {
        // 方法执行完成后,清除threadlocal中持有的database
        DynamicDataSource.clearDataSource();
    }
}

只需在service层加方法上加入注解就可以了,@DS注解默认是注解中定义的值Spring boot+Mybatis-plus+多源数据库动态切换+druid 使用AOP动态切换_第1张图片
最后启动类加一个注解以及开启包扫描

@SpringBootApplication(exclude = {
		DataSourceAutoConfiguration.class
})
@MapperScan("com.dfdk.datamanage.mapper")

这样就能通过注解实现数据库动态SQL的切换了,不过要注意分包哈

通过jta管理实现分布式事务可以查看我下一篇文章
https://blog.csdn.net/qq_38727189/article/details/99712740

你可能感兴趣的:(Spring boot+Mybatis-plus+多源数据库动态切换+druid 使用AOP动态切换)