springboot+mybatis 配置多数据源

百度查了一天,有的麻烦,有的没说完。。。

本文为转载,原文地址

各种问题的解决方案

 

首先要将spring boot自带的DataSourceAutoConfiguration禁掉,因为它会读取application.properties文件的spring.datasource.*属性并自动配置单数据源。在@SpringBootApplication注解中添加exclude属性即可:

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })

然后在application.yml中配置多数据源连接信息:

spring:
  datasource: 
      boundary: 
          url: jdbc:mysql://127.0.0.1:3306/boundary?useUnicode=true&characterEncoding=utf8&useSSL=false
          username: root
          password: root
          driver-class-name: com.mysql.jdbc.Driver
      member: 
          url: jdbc:mysql://127.0.0.1:3306/member?useUnicode=true&characterEncoding=utf8&useSSL=false
          username: root
          password: root
          driver-class-name: com.mysql.jdbc.Driver

 

由于我们禁掉了自动数据源配置,因些下一步就需要手动将这些数据源创建出来:

/**
 * @author qhy
 * 
 * 创建时间:2018年2月9日 上午9:58:40
 */
@Configuration
public class DataSourceConfig {

    @Bean(name = "boundaryDS")
    @ConfigurationProperties(prefix = "spring.datasource.boundary") // application.properteis中对应属性的前缀
    public DataSource dataSource1() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "memberDS")
    @ConfigurationProperties(prefix = "spring.datasource.member") // application.properteis中对应属性的前缀
    public DataSource dataSource2() {
        return DataSourceBuilder.create().build();
    }
    
    /**
     * 动态数据源: 通过AOP在不同数据源之间动态切换
     * @return
     */
    @Bean(name = "dynamicDataSource")
    public DataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(dataSource1());

        // 配置多数据源
        Map<Object, Object> dsMap = new HashMap<Object,Object>(5);
        dsMap.put("boundaryDS", dataSource1());
        dsMap.put("memberDS", dataSource2());

        dynamicDataSource.setTargetDataSources(dsMap);

        return dynamicDataSource;
    }

}

 

 

接下来需要配置两个mybatis的SqlSessionFactory分别使用不同的数据源:

/**
 * 指定包使用member数据源
 * @author qhy
 * 
 * 创建时间:2018年2月9日 上午10:00:18
 */
@Configuration
@MapperScan(basePackages = {"com.boundary.member.core.mapper"}, sqlSessionFactoryRef = "memberSqlSessionFactory")
public class MemberDbConfig {
    
    @Autowired
    @Qualifier("dynamicDataSource")
    private DataSource memberDS;


    @Bean(name="memberSqlSessionFactory")
    public SqlSessionFactory memberSqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(memberDS); // 使用member数据源, 连接member库
        // 使用多数据源后,mybatis的配置失效
        factoryBean.setConfigLocation(DynamicDataSource.getResource());
        
        return factoryBean.getObject();

    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate1() throws Exception {
        SqlSessionTemplate template = new SqlSessionTemplate(memberSqlSessionFactory()); // 使用上面配置的Factory
        return template;
    }
}

 

配置第二个sqlSessionFactory:

/**
 * 指定包使用boundary数据源
 * @author qhy
 * 
 * 创建时间:2018年2月9日 上午10:01:32
 */
@Configuration
@MapperScan(basePackages = {"com.boundary.member.core.wjjmapper"}, sqlSessionFactoryRef = "boundarySqlSessionFactory")
public class BoundaryDbConfig {
    @Autowired
    @Qualifier("dynamicDataSource")
    private DataSource boundaryDS;

    @Bean(name="boundarySqlSessionFactory")
    public SqlSessionFactory boundarySqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(boundaryDS);
        // 使用多数据源后,mybatis的配置失效
        factoryBean.setConfigLocation(DynamicDataSource.getResource());
        return factoryBean.getObject();

    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate2() throws Exception {
        SqlSessionTemplate template = new SqlSessionTemplate(boundarySqlSessionFactory());
        return template;
    }
}

 

首先定义一个ContextHolder, 用于保存当前线程使用的数据源名:

然后自定义一个javax.sql.DataSource接口的实现,这里只需要继承Spring为我们预先实现好的父类AbstractRoutingDataSource即可:

/**
 * 动态切换数据源
 * 
 * @author qhy
 * 
 *         创建时间:2018年2月9日 上午10:04:50
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    /**
     * 默认数据源
     */
    public static final String DEFAULT_DS = "memberDS";

    /**
     * mybatis配置文件
     */
    private static Resource mybatisResource = null;

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static Resource getResource() {
        if (mybatisResource == null) {
            mybatisResource = new ClassPathResource("mybatis-config.xml");
        }
        return mybatisResource;
    }

    @Override
    protected Object determineCurrentLookupKey() {
        String dbName = contextHolder.get();
        // logger.info("数据源为[{}]", dbName);
        return dbName;
    }

    /**
     * 设置数据源名
     * 
     * @param dbType
     */
    public static void setDB(String dbType) {
        contextHolder.set(dbType);
    }

    /**
     * 获取数据源名
     * 
     * @return
     */
    // public static String getDB() {
    // return (contextHolder.get());
    // }

    /**
     * 设置为默认数据源
     */
    public static void clearDB() {
        contextHolder.set(DEFAULT_DS);
        // contextHolder.remove();
    }
}

 

自定义注释@DS用于在编码时指定方法使用哪个数据源:

/**
 * 切换数据源注解
 * @author qhy
 * 
 * 创建时间:2018年2月9日 上午10:19:05
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface DS {
    String value() default "memberDS";
}

 

编写AOP切面,实现切换逻辑:

/**
 * @author qhy
 * 
 * 创建时间:2018年2月9日 上午10:21:46
 */
@Aspect
@Component
@ComponentScan
@EnableAspectJAutoProxy //spring自动切换JDK动态代理和CGLIB
public class DynamicDataSourceAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);
    
    /**
     * <li>Before       : 在方法执行前进行切面</li>
     * <li>execution    : 定义切面表达式</li>
     * <p>public * com.eparty.ccp.*.impl..*.*(..)
     * <li>public :匹配所有目标类的public方法,不写则匹配所有访问权限</li>
     * <li>第一个* :方法返回值类型,*代表所有类型 </li>
     * <li>第二个* :包路径的通配符</li>
     * <li>第三个..* :表示impl这个目录下所有的类,包括子目录的类</li>
     * <li>第四个*(..) : *表示所有任意方法名,..表示任意参数</li>
     * </p>
     *
     * @param point 切面
     */
    @Before("execution(public * com.boundary.member.core.service.impl..*.*(..))")
    public void before(JoinPoint point) {
        this.beforeSwitchDS(point);
    }

    @After("execution(public * com.boundary.member.core.service.impl..*.*(..))")
    public void after(JoinPoint point){
//        LoginTokenHolder.resetToken();
        this.afterSwitchDS(point);
    }
    
//    @Before("@annotation(com.boundary.member.core.config.DS)")
    public void beforeSwitchDS(JoinPoint point){

        //获得当前访问的class
        Class<?> className = point.getTarget().getClass();

        //获得访问的方法名
        String methodName = point.getSignature().getName();
        //得到方法的参数的类型
        @SuppressWarnings("rawtypes")
        Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
        String dataSource = DynamicDataSource.DEFAULT_DS;
        try {
            // 得到访问的方法对象
            Method method = className.getMethod(methodName, argClass);

            // 判断是否存在@DS注解
            if (method.isAnnotationPresent(DS.class)) {
                DS annotation = method.getAnnotation(DS.class);
                // 取出注解中的数据源名
                dataSource = annotation.value();

                // 切换数据源
                logger.info("切换到[{}]数据源", dataSource);
                DynamicDataSource.setDB(dataSource);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


//    @After("@annotation(com.boundary.member.core.config.DS)")
    public void afterSwitchDS(JoinPoint point){
        DynamicDataSource.clearDB();
    }
}

 

最后在service层添加注解    @DS("memberDS")    即可愉快地动态切换数据源;

既然都是转载的,为什么我要专门写下来?因为原博主漏了两个东西;

 

@Qualifier("dynamicDataSource")

 

这个地方,不了解的人不知道该填什么;

还有,使用动态数据源后,将不再使用application.yml文件中指定的mybatis-config.xml;

需要手动设置:

mybatisResource = new ClassPathResource("mybatis-config.xml");

 

开启多数据源后,需要手动配置事务:

/**
     * 配置事务管理
     * @param dataSource
     * @return
     */
    @Bean
    public PlatformTransactionManager tranManager(@Qualifier("dynamicDataSource")DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

 

还有连接池的问题:

查看原文

在数据源下添加如下配置:

          max-idle: 10
          max-wait: 10000
          min-idle: 5
          initial-size: 5
          validation-query: SELECT 1
          test-on-borrow: false
          test-while-idle: true
          time-between-eviction-runs-millis: 18800

 

 

暂时记得这些,可能还有坑被解决了,但是没记下来,想到再改;

 

我配置切换数据源的拦截器,只拦截了service包,所以自己定义的拦截器需要手动指定数据源

 

posted @ 2018-02-10 09:33  Implementsrt  阅读(75)  评论(0编辑  收藏  举报