Mybatis(拦截器实现)通用mapper及全ORM实现(五)-- springboot+mybatis多数据源设置
发布日期:2022-02-22 18:04:24 浏览次数:13 分类:技术文章

本文共 7572 字,大约阅读时间需要 25 分钟。

本篇实际上和mybatisext项目并没有太大关系了,但在实际项目中脱离不开多个数据源,尤其是主从分离,同样网上一些资料大同小异而且大部分并不能真正解决问题,所以单独提出来说一下

假设我们就是要解决一个主从分离,数据源定义在了application.properties中如下:

datasources.master.driverClassName=com.mysql.cj.jdbc.Driverdatasources.master.url=xxxdatasources.master.username=xxxdatasources.master.password=xxxdatasources.master.type=com.alibaba.druid.pool.DruidDataSourcedatasources.master.filters=statdatasources.master.maxActive= 20datasources.master.initialSize= 1datasources.master.maxWait= 60000datasources.master.minIdle =1datasources.master.timeBetweenEvictionRunsMillis= 60000datasources.master.minEvictableIdleTimeMillis=300000datasources.master.validationQuery= select 'x'datasources.master.testWhileIdle= truedatasources.master.testOnBorrow=falsedatasources.master.testOnReturn= falsedatasources.master.poolPreparedStatements=truedatasources.master.maxOpenPreparedStatements= 20datasources.slave.driverClassName=com.mysql.cj.jdbc.Driverdatasources.slave.url=xxxdatasources.slave.username=xxxdatasources.slave.password=xxxdatasources.slave.type=com.alibaba.druid.pool.DruidDataSourcedatasources.slave.filters=statdatasources.slave.maxActive= 20datasources.slave.initialSize= 1datasources.slave.maxWait= 60000datasources.slave.minIdle =1datasources.slave.timeBetweenEvictionRunsMillis= 60000datasources.slave.minEvictableIdleTimeMillis=300000datasources.slave.validationQuery= select 'x'datasources.slave.testWhileIdle= truedatasources.slave.testOnBorrow=falsedatasources.slave.testOnReturn= falsedatasources.slave.poolPreparedStatements=truedatasources.slave.maxOpenPreparedStatements= 20

定义一个枚举类,针对这两个数据源

public enum DataSourceType {    Master,    Slave,}

动态数据源定义,保证线程安全引用了ThreadLocal类记录当前使用的数据源

public class DynamicDataSource extends AbstractRoutingDataSource {    private static final ThreadLocal
contextHolder = new ThreadLocal<>(); public static void setDataSourceType(DataSourceType dataSourceType){ contextHolder.set(dataSourceType); } public static DataSourceType getDataSourceType() { return contextHolder.get(); } public static void clear(){ contextHolder.remove(); } @Override protected Object determineCurrentLookupKey(){ return DynamicDataSource.getDataSourceType(); }}

然后进行mybatis配置的注入

@Configuration@MapperScan({"cw.frame.mybatisext.test.mapper"})public class MyBatisConfig {    @Autowired    private Environment environment;    @Autowired    private MySqlInterceptor mySqlInterceptor;    @Bean    public MySqlInterceptor mySqlInterceptor(){        return new MySqlInterceptor();    }    @Bean    public DataSource masterDataSource() throws Exception{        return DruidDataSourceFactory.createDataSource(this.getDataSourceProperty("datasources.master."));    }    @Bean    public DataSource slaveDataSource() throws Exception{        return DruidDataSourceFactory.createDataSource(this.getDataSourceProperty("datasources.slave."));    }    /**     * @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错     * @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)     * @DependsOn 解决循环依赖问题,在系统自动创建datasource前创建指定数据源     * @param masterDataSource     * @param slaveDataSource     * @return     */    @Bean    @Primary    @DependsOn({ "masterDataSource", "slaveDataSource"})    public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slaveDataSource") DataSource slaveDataSource){        Map
dataSourceMap = new HashMap<>(); dataSourceMap.put(DataSourceType.Master, masterDataSource); dataSourceMap.put(DataSourceType.Slave, slaveDataSource); DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setTargetDataSources(dataSourceMap); dynamicDataSource.setDefaultTargetDataSource(masterDataSource); return dynamicDataSource; } /** * 根据数据源创建SqlSessionFactory * mybatis拦截器需要手动设置一下 fb.setPlugins(new Interceptor[]{mySqlInterceptor}); * @param dynamicDataSource * @return * @throws Exception */ @Bean public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception{ SqlSessionFactoryBean fb = new SqlSessionFactoryBean(); // 因为手动创建了SqlSessionFactory,所以需要手动设置拦截器 fb.setPlugins(new Interceptor[]{mySqlInterceptor}); fb.setDataSource(dynamicDataSource); return fb.getObject(); } /** * 配置事务管理器 * @param dataSource * @return * @throws Exception */ @Bean public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception{ return new DataSourceTransactionManager(dataSource); } private Properties getDataSourceProperty(String prefixKey){ String[] keys = { "driverClassName", "url", "username", "password", "type", "filters", "maxActive", "initialSize", "maxWait", "minIdle", "timeBetweenEvictionRunsMillis", "minEvictableIdleTimeMillis", "validationQuery", "testWhileIdle", "testOnBorrow", "testOnReturn", "poolPreparedStatements", "maxOpenPreparedStatements" }; Properties properties = new Properties(); for (String key : keys){ properties.put(key, environment.getProperty(prefixKey + key)); } return properties; }}

这里特别说明一下:

@DependsOn({ "masterDataSource", "slaveDataSource"}),告知springboot这个bean依赖另外两个bean,保证了注入顺序,要不会报错或不起作用
另外由于手动创建了SqlSessionFactory,所以拦截器的定义也要手动设置一下(代码中已有注释说明),要不按照普通的配置拦截器将无法生效

最后在做一个切片定义

@Aspect@Componentpublic class DynamicDataSourceAspect {    @Before("@annotation(DataSource)")    public void beforeExecute(JoinPoint point){    }    @Around("@annotation(DataSource)")    public Object aroundExecute(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{        Class
classType = proceedingJoinPoint.getTarget().getClass(); String methodName = proceedingJoinPoint.getSignature().getName(); Class[] argClass = ((MethodSignature)proceedingJoinPoint.getSignature()).getParameterTypes(); DataSourceType lastDataSourceType = null; boolean resetDataSourceType = false; Method method = classType.getMethod(methodName, argClass); if (method.isAnnotationPresent(DataSource.class)){ DataSourceType dataSourceType = method.getAnnotation(DataSource.class).value(); lastDataSourceType = DynamicDataSource.getDataSourceType(); if (lastDataSourceType == null){ DynamicDataSource.setDataSourceType(dataSourceType); resetDataSourceType = true; } else { switch (lastDataSourceType){ case Master: break; case Slave: if (dataSourceType == DataSourceType.Master){ DynamicDataSource.setDataSourceType(dataSourceType); resetDataSourceType = true; } break; } } } Object result = proceedingJoinPoint.proceed(); if (resetDataSourceType){ if (lastDataSourceType == null){ DynamicDataSource.clear(); } else { DynamicDataSource.setDataSourceType(lastDataSourceType); } } return result; } @After("@annotation(DataSource)") public void afterExecute(JoinPoint point){ }}

所有带@DataSource注解的都会进入切片,动态设置数据源

当然代码中你想手动设置也可以通过:DynamicDataSource.setDataSourceType 方法进行设置

到此为止,介绍完了关于mybatis的拦截器全ORM实现的方案和mybatisext的项目,源代码

转载地址:https://blog.csdn.net/erct/article/details/82703889 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Mybatis(拦截器实现)通用mapper及全ORM实现(四)
下一篇:Mac 安装 hadoop+hive+hbase+spark

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2024年03月27日 09时11分05秒