Spring Boot +Mybatis 多數據源的配置和使用

1、在application.properties中添加數據庫連接配置
    mybatis.type-aliases-package=com.yc.edusys.bean
    mybatis.mapper-locations=classpath:mapper/*Mapper.xml
    
    # 第一個數據源
    jdbc1.driverClassName = com.mysql.jdbc.Driver
    jdbc1.url = jdbc:mysql://127.0.0.1:3306/edusys?useUnicode=true&characterEncoding=utf-8
    jdbc1.username = root
    jdbc1.password = a
    
    # 第二個數據源
    jdbc2.driverClassName = com.mysql.jdbc.Driver
    jdbc2.url = jdbc:mysql://127.0.0.1:3306/usersys?useUnicode=true&characterEncoding=utf-8
    jdbc2.username = root
    jdbc2.password = a
    
2、創建一個枚舉類 DatabaseType列出所有數據源的數據庫名稱
public enum DatabaseType {
    edusys,usersys
}

3、DynamicDataSource繼承AbstractRoutingDataSource並重寫其中的方法determineCurrentLookupKey()
/**
 * 使用DatabaseContextHolder獲取當前線程的DatabaseType
 * 動態數據源,需要繼承AbstractRoutingDataSource
 * @author navy
 * @company 源辰信息
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<DataSourceType>();

    public static void setDatabaseType(DataSourceType type){
        contextHolder.set(type);
    }

    protected Object determineCurrentLookupKey() {  // determine: 決定、限定  CurrentLookupKey : 當前查找關鍵字
        return contextHolder.get();
    }
    
    public static void resetDataSourceType(){
        contextHolder.set(DataSourceType.edusys);
    }
}


4、MyBatisConfig中生成2個數據源DataSource的bean與value
/**
 * springboot集成mybatis的基本入口 
 * 1、創建數據源(如果採用的是默認的tomcat-jdbc數據源,則不需要)
 * 2、創建SqlSessionFactory 
 * 3、配置事務管理器,除非需要使用事務,否則不用配置
 * 
 * 通過讀取application.properties文件生成兩個數據源(eduSysDataSource、userSysDataSource)
 * 使用以上生成的兩個數據源構造動態數據源dataSource
 * @Primary:指定在同一個接口有多個實現類可以注入的時候,默認選擇哪一個,而不是讓@Autowire註解報錯(一般用於多數據源的情況下)
 * @Qualifier:指定名稱的注入,當一個接口有多個實現類的時候使用(在本例中,有兩個DataSource類型的實例,需要指定名稱注入)
 * @Bean:生成的bean實例的名稱是方法名(例如上邊的@Qualifier註解中使用的名稱是前邊兩個數據源的方法名,而這兩個數據源也是使用@Bean註解進行注入的
 * 通過動態數據源構造SqlSessionFactory和事務管理器(如果不需要事務,後者可以去掉)
 */
@Configuration // 該註解類似於spring配置文件
// 指定需要掃描的包(mapper.xml文件存放的包路徑)
// @MapperScan(basePackages = "mapper")
public class MyBatisConfig {
    @Autowired
    private Environment env;

    /**
     * 創建數據源(數據源的名稱:方法名可以取爲XXXDataSource(),XXX爲數據庫名稱,該名稱也就是數據源的名稱)
     * 
     * @Bean: 方法級別上的註解,相當於
     * <beans>
     *         <bean id="方法名" class="此方法返回的對象"/>
     * </beans>
     */
    @Bean
    public DataSource eduSysDataSource() throws Exception {
        Properties props = new Properties();
        props.put("driverClassName", env.getProperty("jdbc1.driverClassName"));
        props.put("url", env.getProperty("jdbc1.url"));
        props.put("username", env.getProperty("jdbc1.username"));
        props.put("password", env.getProperty("jdbc1.password"));
        return DruidDataSourceFactory.createDataSource(props);
    }

    @Bean
    public DataSource userSysDataSource() throws Exception {
        Properties props = new Properties();
        props.put("driverClassName", env.getProperty("jdbc2.driverClassName"));
        props.put("url", env.getProperty("jdbc2.url"));
        props.put("username", env.getProperty("jdbc2.username"));
        props.put("password", env.getProperty("jdbc2.password"));
        return DruidDataSourceFactory.createDataSource(props);
    }

  
    @Bean
    @Primary
    public DynamicDataSource dataSource(@Qualifier("eduSysDataSource") DataSource eduSysDataSource, @Qualifier("userSysDataSource") DataSource userSysDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        targetDataSources.put(DataSourceType.edusys, eduSysDataSource);
        targetDataSources.put(DataSourceType.usersys, userSysDataSource);

        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSources); // 該方法是AbstractRoutingDataSource的方法
        dataSource.setDefaultTargetDataSource(eduSysDataSource);// 默認的datasource設置爲eduSysDataSource
        return dataSource;
    }

    /**
     * 根據數據源創建SqlSessionFactory
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(ds); // 指定數據源(這個必須有,否則報錯)
        
        // 下邊兩句僅僅用於*.xml文件,如果整個持久層操作不需要使用到xml文件的話(只用註解就可以搞定),則可以不加
        sessionFactoryBean.setTypeAliasesPackage(env.getProperty("mybatis.type-aliases-package"));// 指定基包
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapper-locations")));
        return sessionFactoryBean.getObject();
    }

    /**
     * 配置事務管理器
     */
    @Bean
    public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
        return new DataSourceTransactionManager(dataSource);
    }
}

5、將DynamicDataSource作爲數據源注入到SqlSessionFactory的dataSource屬性中去,並且該dataSource作爲transactionManager的入參來構造DataSourceTransactionManager
/**
 * 使用DatabaseContextHolder獲取當前線程的DatabaseType
 * 動態數據源,需要繼承AbstractRoutingDataSource
 * @author navy
 * @company 源辰信息
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<DataSourceType>();

    public static void setDatabaseType(DataSourceType type){
        contextHolder.set(type);
    }

    protected Object determineCurrentLookupKey() {  // determine: 決定、限定  CurrentLookupKey : 當前查找關鍵字
        return contextHolder.get();
    }
    
    public static void resetDataSourceType(){
        contextHolder.set(DataSourceType.edusys);
    }
}

6、定義一個指定數據源的註解
/**
 * 數據源類型註解
 * @author navy
 * @company 源辰信息
 */
@Retention(RetentionPolicy.RUNTIME) // 在運行時可見
@Target(ElementType.METHOD) // 註解可以用在方法上
public @interface DataSourceTypeAnnotation {
    DataSourceType value() default DataSourceType.edusys;
}

7、創建一個切面,用來切換數據源
/**
 * 切換數據源的切面
 * @author navy
 * @company 源辰信息
 */
@Component
@Aspect
@Order(-10)  // 讓它在事務註解前面起作用
public class DataSourceAspect {
    /**
     * 第一個*表示返回值類型
     * 包名:表示需要攔截的包名,後面的兩個句點表示當前包和當前包的所有子包,com.yc.edusys.dao包、子孫包下所有類的方法
     * 第二個*號:表示類名,*號表示所有的類
     * *(..) : 最後這個星號表示方法名,*號表示所有的方法,後面括弧裏面表示方法的參數,兩個句點表示任何參數
     */
    //@Pointcut("execution(* com.yc.edusys.dao..*.*(..)) && @annotation(com.yc.edusys.annotation.DataSourceTypeAnnotation)")
    //@Pointcut("execution(* com.yc.edusys.biz.impl.*.*(..)) && @annotation(com.yc.edusys.annotation.DataSourceTypeAnnotation)")
    @Pointcut("execution(* com.yc.edusys.biz.impl.*.*(..))")
    public void dataSourcePointcut() {
    }
 
    @Before("dataSourcePointcut()")
    public void doBefore(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        
        DataSourceTypeAnnotation typeAnnotation = method.getAnnotation(DataSourceTypeAnnotation.class);
        if (typeAnnotation == null) { // 如果沒有這個註解,則默認訪問edusys數據庫
            DynamicDataSource.setDatabaseType(DataSourceType.edusys);
            return;
        }
        
        DataSourceType sourceType = typeAnnotation.value();
        if (sourceType == DataSourceType.usersys) {
            DynamicDataSource.setDatabaseType(DataSourceType.usersys);
        }else {
            DynamicDataSource.setDatabaseType(DataSourceType.edusys);
        }
    }
}

8、在對應的方法上使用註解切換數據庫
@DataSourceTypeAnnotation(DataSourceType.usersys)
public int add(RoleInfo rf) {
    if (rf == null || StringUtil.isNull(rf.getRname())) {
        return -1;
    }
    return baseDao.update(RoleInfo.class, "addRole", rf);
}
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章