環境:springboot+mybatis
數據庫:mysql+oracle
需求:一個程序中,一部分數據從mysql中查,一部分數據從oracle中查。使用AOP來實現數據庫動態切換
參考網上的mybatis動態數據源,並額外擴展使用AOP
步驟:
1.先定義不同的數據庫連接類型,用來表示mysql數據庫和oracle數據庫,如果還有多個數據庫,繼續定義 (這裏cms表示oracle數據庫,hms表示mysql數據庫)
public enum DataSourceTypeEnum {
cms,hms
}
2.繼承AbstractRoutingDataSource,實現我們自己的動態數據源類型
/**
* 動態數據源
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<DataSourceTypeEnum> contextHolder = new ThreadLocal<>();
public static void setDataSourceType(DataSourceTypeEnum type){
contextHolder.set(type);
}
public static DataSourceTypeEnum getDataSourceType(){
return contextHolder.get();
}
@Override
protected Object determineCurrentLookupKey() {
return getDataSourceType();
}
}
3.定義數據源配置類,在裏面定義數據庫連接1(cms),數據庫連接2(hms),外加主數據源。
數據庫連接1/2有兩組實現,分別使用Druid連接池和Pooled連接池實現,由配置文件指定使用哪種。
主數據源 傳入 數據庫連接1和2 來完成構造,並設置默認數據庫連接
/**
* 數據庫定義,通過配置項datasource.type,來切換數據庫連接池
*/
@Configuration
public class DataSourceConfig {
private static int initTimes = 0;
@Autowired
Environment environment;
/**
* 動態數據源,注入cms的數據庫連接和hms的數據庫連接
*/
@Bean
@Primary
public DynamicDataSource dataSource(@Qualifier("cmsDataSource") DataSource cmsDataSource,
@Qualifier("hmsDataSource") DataSource hmsDataSource) {
assert initTimes==0; //數據源只能初始化一次
initTimes++;
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceTypeEnum.cms, cmsDataSource);
targetDataSources.put(DataSourceTypeEnum.hms, hmsDataSource);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);// 該方法是AbstractRoutingDataSource的方法
dataSource.setDefaultTargetDataSource(cmsDataSource);// 默認的datasource設置爲myTestDbDataSource
return dataSource;
}
@Bean("cmsDataSource")
@ConditionalOnProperty(value = "datasource.type", havingValue = "druid")
public DataSource cmsDataSourceDruid() throws Exception {
Properties props = new Properties();
props.put("driverClassName", environment.getProperty("cms.db.driverClass"));
props.put("url", environment.getProperty("cms.db.url"));
props.put("username", environment.getProperty("cms.db.username"));
props.put("password", environment.getProperty("cms.db.password"));
props.put("initialPoolSize", environment.getProperty("cms.db.initialPoolSize"));
props.put("initialMaxPoolSize", environment.getProperty("cms.db.initialMaxPoolSize"));
props.put("initialMinPoolSize", environment.getProperty("cms.db.initialMinPoolSize"));
return DruidDataSourceFactory.createDataSource(props);
}
@Bean("hmsDataSource")
@ConditionalOnProperty(value = "datasource.type", havingValue = "druid")
public DataSource hmsDataSourceDruid() throws Exception {
Properties props = new Properties();
props.put("driverClassName", environment.getProperty("hms.db.driverClass"));
props.put("url", environment.getProperty("hms.db.url"));
props.put("username", environment.getProperty("hms.db.username"));
props.put("password", environment.getProperty("hms.db.password"));
props.put("initialPoolSize", environment.getProperty("hms.db.initialPoolSize"));
props.put("initialMaxPoolSize", environment.getProperty("hms.db.initialMaxPoolSize"));
props.put("initialMinPoolSize", environment.getProperty("hms.db.initialMinPoolSize"));
return DruidDataSourceFactory.createDataSource(props);
}
@Bean("cmsDataSource")
@ConditionalOnProperty(value = "datasource.type", havingValue = "jdbc")
public DataSource cmsDataSourceJdbc() throws Exception {
PooledDataSource pooledDataSource = new PooledDataSource();
pooledDataSource.setDriver(environment.getProperty("cms.db.driverClass"));
pooledDataSource.setUrl(environment.getProperty("cms.db.url"));
pooledDataSource.setUsername(environment.getProperty("cms.db.username"));
pooledDataSource.setPassword(environment.getProperty("cms.db.password"));
return pooledDataSource;
}
@Bean("hmsDataSource")
@ConditionalOnProperty(value = "datasource.type", havingValue = "jdbc")
public DataSource hmsDataSourceJdbc() throws Exception {
PooledDataSource pooledDataSource = new PooledDataSource();
pooledDataSource.setDriver(environment.getProperty("hms.db.driverClass"));
pooledDataSource.setUrl(environment.getProperty("hms.db.url"));
pooledDataSource.setUsername(environment.getProperty("hms.db.username"));
pooledDataSource.setPassword(environment.getProperty("hms.db.password"));
return pooledDataSource;
}
}
4.編寫mybatis配置類,傳入我們定義的動態數據庫
@Configuration
@MapperScan("demo.dao")
public class MybatisConfig {
@Autowired
DataSourceConfig dataSourceConfig;
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("cmsDataSource") DataSource cmsDataSource,
@Qualifier("hmsDataSource") DataSource hmsDataSource)
throws Exception{
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(dataSourceConfig.dataSource(cmsDataSource,hmsDataSource));//多次調用dataSource,返回同一個bean
fb.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:demo/dao/*.xml"));
fb.setDatabaseIdProvider(getDatabaseIdProvider());
return fb.getObject();
}
public DatabaseIdProvider getDatabaseIdProvider(){
DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
Properties properties = new Properties();
properties.setProperty("Oracle","oracle");
properties.setProperty("Mysql","mysql");
properties.setProperty("Microsoft SQL Server","sqlserver");
databaseIdProvider.setProperties(properties);
return databaseIdProvider;
}
}
5.到這裏就已經可以切換數據庫,正常使用了,例如:
@Service
public class IntegerateHelper {
@Autowired
IntegrateInfoMapper cmsMapper; //mysql對應的sql語句
@Autowired
CtrlIntegrationInfoMapper hmsMapper; //oracle對應的sql語句
//默認的,這裏是查詢mysql庫
public IntegrateInfo queryIntegrateInfo(Short integrateId){
IntegrateInfo integrateInfo = cmsMapper.selectByPrimaryKey(integrateId);
return integrateInfo;
}
//進行數據庫切換,查詢oracle數據庫,查詢完,再切換回mysql數據庫
public CtrlIntegrationInfo queryCtrlVendorInfo(String integrateName){
DynamicDataSource.setDataSourceType(DataSourceTypeEnum.hms);
CtrlIntegrationInfo integrationInfo = hmsMapper.selectByName(integrateName);
DynamicDataSource.setDataSourceType(DataSourceTypeEnum.cms);
return integrationInfo;
}
}
6.每次需要數據庫切換,都要手動在代碼前後添加兩行代碼,可以使用AOP來幫助我們完成切換
定義一個註解(注意:因爲數據庫切換不支持事務,所以需要聲明成Propagation.NOT_SUPPORTED。如果不聲明,在事務中進行數據庫切換,會切換不成功,還是在默認數據庫中執行)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public @interface ChooseHmsDatabase {
}
再將這個註解作爲切面,定義一個around的AOP
@Aspect
@Component
public class ChangeDbAop {
@Pointcut("@annotation(ChooseHmsDatabase)")
public void pointcut(){}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
DynamicDataSource.setDataSourceType(DataSourceTypeEnum.hms); //切換到hms的庫
try {
return joinPoint.proceed();
}finally {
DynamicDataSource.setDataSourceType(DataSourceTypeEnum.cms); //切換回cms的庫
}
}
}
7.在需要數據庫切換到oracle庫的地方,註解上@ChooseHmsDatabase就行了
@Service
public class IntegerateHelper {
@Autowired
IntegrateInfoMapper cmsMapper;
@Autowired
CtrlIntegrationInfoMapper hmsMapper;
public IntegrateInfo queryIntegrateInfo(Short integrateId){
IntegrateInfo integrateInfo = cmsMapper.selectByPrimaryKey(integrateId);
return integrateInfo;
}
//使用註解,就會自動在方法調用時切換到另一個數據庫,方法調用後,自動切換會原來的數據庫
@ChooseHmsDatabase
public CtrlIntegrationInfo queryCtrlVendorInfo(String integrateName){
CtrlIntegrationInfo integrationInfo = hmsMapper.selectByName(integrateName);
return integrationInfo;
}
}
最後,附上gradle架包引用和數據庫的配置
compile("org.springframework.boot:spring-boot-starter")
compile("org.springframework.boot:spring-boot-starter-aop")
//mybatis相關架包
compile("com.baomidou:mybatis-plus-boot-starter:2.3")
compile group: 'org.mybatis.generator', name: 'mybatis-generator-core', version: '1.4.0'
compile group: 'com.alibaba', name: 'druid', version: '1.1.22'
compile group: 'com.github.pagehelper', name: 'pagehelper', version: '5.1.11'
compile("com.microsoft.sqlserver:mssql-jdbc:6.4.0.jre8")
compile group: 'com.oracle.ojdbc', name: 'ojdbc8', version: '19.3.0.0'
compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.18'
################## 選擇數據庫連接池實現
datasource.type=druid
############# oracle的數據庫連接配置 #############
cms.db.url=jdbc:oracle:thin:@127.0.0.1:1521:xe
cms.db.driverClass=oracle.jdbc.driver.OracleDriver
cms.db.username=user
cms.db.password=123456
cms.db.initialPoolSize=5
cms.db.initialMaxPoolSize=20
cms.db.initialMinPoolSize=5
############## mysql的數據庫連接配置 #####################
hms.db.url=jdbc:mysql://10.10.213.168:3306/test
hms.db.driverClass=com.mysql.jdbc.Driver
hms.db.username=root
hms.db.password=123456
hms.db.initialPoolSize=5
hms.db.initialMaxPoolSize=20
hms.db.initialMinPoolSize=5