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);
}