SpringCloud学习心得(九) Springboot与Mabatis集成——动态数据源

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/money9sun/article/details/82254984

复制第八节的项目,重命名为quartz-mybatis-multi-dynamic

动态数据源,本项目通过AOP注解的方式实现动态数据源。两个数据源已经在上几节中建立。

1、pom文件省略和第八节相同

2、配置文件:

#datasource config
jdbc:
  driverClassName: com.mysql.jdbc.Driver
  url: jdbc:mysql://127.0.0.1/bpm?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
  username: root
  password: 123
 
#datasource config2
jdbc2:
  driverClassName: com.mysql.jdbc.Driver
  url: jdbc:mysql://127.0.0.1/goods?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
  username: root
  password: 123
 
#mybatis config
mybatis:
  typeAliasesPackage: com.hotkidceo.springcloud.domain
  mapperLocations: classpath:mapper/bpm/*.xml

3、增加两个工具类到util包下

第一个:DataSourceContextHolder 主要用于对访问线程设置数据源名称

public class DataSourceContextHolder {
    
    public static final Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class);
    
    public static final String DEFAULT_DS = "bpmDS";
    
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    // 设置数据源名
    public static void setDB(String dbType) {
        log.debug("切换到{}数据源", dbType);
        contextHolder.set(dbType);
    }
    
    // 获取数据源名
    public static String getDB() {
        return (contextHolder.get());
    }

    // 清除数据源名
    public static void clearDB() {
        contextHolder.remove();
    }
}

第二个:DynamicDataSource 继承 AbstractRoutingDataSource类

关于AbstractRoutingDataSource请参照https://blog.csdn.net/fangdengfu123/article/details/70139644

public class DynamicDataSource extends AbstractRoutingDataSource{

    private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);
    
    @Override
    protected Object determineCurrentLookupKey() {
        log.debug("数据源为{}", DataSourceContextHolder.getDB());

        return DataSourceContextHolder.getDB();
    }

}

4、在config包下,修改DataSourceConfig类

增加如下代码:

    /**
     * 动态数据源: 通过AOP在不同数据源之间动态切换
     * @return
     */
    @Bean(name = "dynamicDS")
    public DataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(dataSourceBPM());

        // 配置多数据源
        Map<Object, Object> dsMap = new HashMap(5);
        dsMap.put("bmpDS", dataSourceBPM());
        dsMap.put("goodsDS", dataSourceGoods());

        dynamicDataSource.setTargetDataSources(dsMap);

        return dynamicDataSource;
    }

5、在config包下,增加动态数据源配置类MybatisDBDynamicConfig(把同包下其他的配置类的注解@@Configuration注释掉,避免混淆)

@Configuration
@MapperScan(basePackages = {"com.hotkidceo.springcloud.dao.bpm"},sqlSessionFactoryRef="sqlSessionFactory")
public class MybatisDBDynamicConfig {
    @Autowired
    private Environment env;    
    
    @Autowired
    @Qualifier("dynamicDS")
    private DataSource dynamicDS;
    
    @Bean
    @Primary
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dynamicDS);
        //下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
        factoryBean.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));
        return factoryBean.getObject();
    }
    
    @Bean
    @Primary
    public SqlSessionTemplate sqlSessionTemplateBPM() throws Exception {
        SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory()); // 使用上面配置的Factory
        return template;
    }
}

6、新建切面类 DynamicDataSourceAspect,对注解DS进行切面,之前更新数据源的名称,之后将清空

@Aspect
@Component
public class DynamicDataSourceAspect {
    
    @Before("@annotation(DS)")
    public void beforeSwitchDS(JoinPoint point){
        
        // 获得当前访问的class
        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);
            
            // 判断是否存在@DS注解
            if(method.isAnnotationPresent(DS.class)){
                DS annotaion = method.getAnnotation(DS.class);
                // 取出注解中的数据源名
                dataSource = annotaion.value();
            }
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        // 切换数据源
        DataSourceContextHolder.setDB(dataSource);
    }
    
    @After("@annotation(DS)")
    public void afterSwitchDS(JoinPoint point){
        DataSourceContextHolder.clearDB();
    }
}

至此,配置完成,调用userController的不同方法会进入不同的数据库中操作。主要原因是注解,如下:

@Service
public class UserService {

    @Autowired
    private UserDao userDao;
    
    @DS("bpmDS")
    public int addUser(UserDO user){
        return userDao.insert(user);
    }
    
    public UserDO getUserById(Long id){
        return userDao.selectByPrimaryKey(id);
    }
    
    @DS("goodsDS")
    public List<UserDO> getAllUsers(){
        return userDao.getAllUsers();
    }
}

请大家自行尝试

git地址:https://gitee.com/EricLoveMia/eureka-client-test-quartz-mybatis-muti-dynamic.git

猜你喜欢

转载自blog.csdn.net/money9sun/article/details/82254984