Springboot+Mybatis+Pagehelper+Aop動態配置Oracle、Mysql數據源


 

本文鏈接: https://blog.csdn.net/wjy511295494/article/details/78825890

Springboot+Mybatis+Pagehelper+Aop動態配置Oracle、Mysql數據源

用公司新搭的maven腳手架創建springboot工程,因為腳手架功能未完善,創建出的工程主要就是引了springboot基礎包並創建了目錄結構,所以需要自己添加框架來搭建工程,也能通過這個過程來更深入了解相關框架,提升自己。
* springboot程序入口:TianlianModelServerApplication.java

package com.tianlian.server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.PropertySource; //因為用springboot默認的數據源只能配置一套,而我們需要從多個數據源來查詢數據, //因此用@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})來關閉自動配置功能。 @SpringBootApplication(exclude={DataSourceAutoConfiguration.class}) //用@PropertySource來加載properties文件 @PropertySource(value={"classpath:dev/properties/dubbo.properties","classpath:dev/properties/mysql.properties","classpath:dev/properties/oracle.properties"}) //加載xml配置文件 @ImportResource(locations={"classpath:configs/dubbo-customers.xml","classpath:configs/dubbo-server.xml"}) @ComponentScan(basePackages = "com.tianlian.server") //開啟AOP代理自動配置 @EnableAspectJAutoProxy public class TianlianModelServerApplication { public static void main(String[] args) { SpringApplication.run(TianlianModelServerApplication.class, args); } } 

 

 

  • 讀取mysql配置:MySqlDataSourceConfig.java(OracleDataSourceConfig.java類似)
package com.tianlian.server.configs; import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; /** *用@Value注解來給屬性賦值,分別創建oracle數據源的bean、mysql數據源的bean, *並用@componen把bean交給spring管理,方便后面創建不同數據源時使用。 */ @Data @Component public class MySqlDataSourceConfig { @Value("${mysql.jdbc.driverClassName}") private String driverClassName; @Value("${mysql.jdbc.url}") private String url; @Value("${mysql.jdbc.username}") private String username; @Value("${mysql.jdbc.password}") private String password; @Value("${mysql.jdbc.initialSize}") private Integer initialSize; @Value("${mysql.jdbc.maxActive}") private Integer maxActive; @Value("${mysql.jdbc.minPoolSize}") private Integer minPoolSize; @Value("${mysql.jdbc.maxWait}") private Long maxWait; @Value("${mysql.jdbc.minIdle}") private Integer minIdle; @Value("${mysql.jdbc.timeBetweenEvictionRunsMillis}") private Long timeBetweenEvictionRunsMillis; @Value("${mysql.jdbc.minEvictableIdleTimeMillis}") private Long minEvictableIdleTimeMillis; @Value("${mysql.jdbc.validationQuery}") private String validationQuery; @Value("${mysql.jdbc.testWhileIdle}") private Boolean testWhileIdle; @Value("${mysql.jdbc.testOnBorrow}") private Boolean testOnBorrow; @Value("${mysql.jdbc.testOnReturn}") private Boolean testOnReturn; @Value("${mysql.jdbc.maxOpenPreparedStatements}") private Integer maxOpenPreparedStatements; @Value("${mysql.jdbc.removeAbandoned}") private Boolean removeAbandoned; @Value("${mysql.jdbc.removeAbandonedTimeout}") private Integer removeAbandonedTimeout; @Value("${mysql.jdbc.logAbandoned}") private Boolean logAbandoned; @Value("${mysql.jdbc.poolPreparedStatements}") private Boolean poolPreparedStatements; @Value("${mysql.jdbc.filters}") private String filters; } 

 

  • 配置mybatis:MyBatisConfigNew.java
package com.tianlian.server.configs; import com.alibaba.druid.pool.DruidDataSource; import com.github.pagehelper.PageHelper; import java.util.HashMap; import java.util.Map; import java.util.Properties; import javax.sql.DataSource; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @Configuration @MapperScan(basePackages = "com.tianlian.server.dao.mapper") public class MyBatisConfigNew { @Autowired private MySqlDataSourceConfig mysqlJdbcMapper; @Autowired private OracleDataSourceConfig oracleJdbcMapper; /** *根據oracle和mysql的配置屬性bean分別創建oracle數據源bean、mysql數據源bean */ @Bean public DataSource mysqlDataSource() throws Exception { DruidDataSource datasource = new DruidDataSource(); datasource.setUrl(mysqlJdbcMapper.getUrl()); datasource.setUsername(mysqlJdbcMapper.getUsername()); datasource.setPassword(mysqlJdbcMapper.getPassword()); datasource.setDriverClassName(mysqlJdbcMapper.getDriverClassName()); datasource.setInitialSize(mysqlJdbcMapper.getInitialSize()); datasource.setMinIdle(mysqlJdbcMapper.getMinIdle()); datasource.setMaxActive(mysqlJdbcMapper.getMaxActive()); datasource.setMaxWait(mysqlJdbcMapper.getMaxWait()); datasource.setTimeBetweenEvictionRunsMillis(mysqlJdbcMapper.getTimeBetweenEvictionRunsMillis()); datasource.setMinEvictableIdleTimeMillis(mysqlJdbcMapper.getMinEvictableIdleTimeMillis()); datasource.setValidationQuery(mysqlJdbcMapper.getValidationQuery()); datasource.setTestWhileIdle(mysqlJdbcMapper.getTestWhileIdle()); datasource.setTestOnBorrow(mysqlJdbcMapper.getTestOnBorrow()); datasource.setTestOnReturn(mysqlJdbcMapper.getTestOnReturn()); datasource.setPoolPreparedStatements(mysqlJdbcMapper.getPoolPreparedStatements()); return datasource; } @Bean public DataSource oracleDataSource() throws Exception { DruidDataSource datasource = new DruidDataSource(); datasource.setUrl(oracleJdbcMapper.getUrl()); datasource.setUsername(oracleJdbcMapper.getUsername()); datasource.setPassword(oracleJdbcMapper.getPassword()); datasource.setDriverClassName(oracleJdbcMapper.getDriverClassName()); datasource.setInitialSize(oracleJdbcMapper.getInitialSize()); datasource.setMinIdle(oracleJdbcMapper.getMinIdle()); datasource.setMaxActive(oracleJdbcMapper.getMaxActive()); datasource.setMaxWait(oracleJdbcMapper.getMaxWait()); datasource .setTimeBetweenEvictionRunsMillis(oracleJdbcMapper.getTimeBetweenEvictionRunsMillis()); datasource.setMinEvictableIdleTimeMillis(oracleJdbcMapper.getMinEvictableIdleTimeMillis()); datasource.setValidationQuery(oracleJdbcMapper.getValidationQuery()); datasource.setTestWhileIdle(oracleJdbcMapper.getTestWhileIdle()); datasource.setTestOnBorrow(oracleJdbcMapper.getTestOnBorrow()); datasource.setTestOnReturn(oracleJdbcMapper.getTestOnReturn()); datasource.setPoolPreparedStatements(oracleJdbcMapper.getPoolPreparedStatements()); return datasource; } /** * 創建動態數據源,將上面創建的數據源交給動態數據源管理(即放到抽象類AbstractRoutingDataSource中的targetDataSources管理) */ @Bean @Primary public DynamicDataSource dataSource( @Qualifier("mysqlDataSource") DataSource mysqlDataSource, @Qualifier("oracleDataSource") DataSource oracleDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DatabaseType.mysqlDb, mysqlDataSource); targetDataSources.put(DatabaseType.oracleDb, oracleDataSource); DynamicDataSource dataSource = new DynamicDataSource(); // 該方法是AbstractRoutingDataSource的方法 dataSource.setTargetDataSources(targetDataSources); // 默認的datasource設置為myTestDbDataSource dataSource.setDefaultTargetDataSource(mysqlDataSource); return dataSource; } /** *將動態數據源交給mybatis的SqlSessionFactoryBean,並添加PageHelper分頁插件。 *因為要切換數據源,必須要把PageHelper的autoRuntimeDialect屬性設置為true才能在不同類新的數據源切換時, *使用不同數據源的分頁方式。 */ @Bean public SqlSessionFactory sqlSessionFactory( @Qualifier("mysqlDataSource") DataSource mysqlDataSource, @Qualifier("oracleDataSource") DataSource oracleDataSource) throws Exception { SqlSessionFactoryBean fb = new SqlSessionFactoryBean(); fb.setDataSource(this.dataSource(mysqlDataSource, oracleDataSource)); PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); //分頁插件 PageInterceptor pageHelper = new PageInterceptor(); 
Properties properties = new Properties();
properties.setProperty("reasonable", "true");
properties.setProperty("supportMethodsArguments", "true");
properties.setProperty("returnPageInfo", "check");
properties.setProperty("params", "count=countSql");
properties.setProperty("autoRuntimeDialect", "true");
pageHelper.setProperties(properties);
//添加插件
fb.setPlugins(new Interceptor[]{pageHelper});
//添加XML目錄
try {
fb.setMapperLocations(resolver.getResources("classpath:mapper/**/*.xml"));
return fb.getObject();
} catch (Exception e)
{
e.printStackTrace(); throw new RuntimeException(e); }
}
@Bean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory)
{ return new SqlSessionTemplate(sqlSessionFactory); } /** * 配置事務管理器 */
@Bean public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource)
throws Exception { return new DataSourceTransactionManager(dataSource); } }
  • 用ThreadLocal為每個線程保存各自選擇的數據源:DataSourceContextHolder.java
package com.tianlian.server.configs; public class DataSourceContextHolder { /** * 默認數據源 */ public static final DatabaseType DEFAULT_DS = DatabaseType.mysqlDb; //public static final DatabaseType DEFAULT_DS = DatabaseType.oracleDb; private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>(); public static void setDatabaseType(DatabaseType type) { contextHolder.set(type); } public static DatabaseType getDatabaseType() { return contextHolder.get(); } public static void clearDatabaseType() { contextHolder.remove(); } }
  • 繼承抽象類AbstractRoutingDataSource實現抽象方法——選擇切換到哪個數據源的方法:DynamicDataSource.java
package com.tianlian.server.configs; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDatabaseType(); } }
  • 定義一個注解DS,后面用它來標識用哪個數據源來查詢:DS.java
package com.tianlian.server.configs; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface DS { DatabaseType value() default DatabaseType.mysqlDb; } 

 

 

  • 利用spring的aop來實現根據注解來動態切換數據源的動作:DynamicDataSourceAspect.java
package com.tianlian.server.configs; import java.lang.reflect.Method; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Aspect @Component public class DynamicDataSourceAspect { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class); //首先要定義一個切點——mapper接口下的所有查詢方法 @Pointcut(value = "execution(* com.tianlian.server.dao.mapper.*.*(..))") private void pointcut() { } @Around("pointcut()") public void around(ProceedingJoinPoint point) throws Throwable { DataSourceContextHolder.clearDatabaseType(); Object target = point.getTarget(); String method = point.getSignature().getName(); Class<?>[] classz = target.getClass().getInterfaces(); Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()) .getMethod().getParameterTypes(); //設置默認數據源 DatabaseType dataSource = DataSourceContextHolder.DEFAULT_DS; String methodName = ""; try { Method m = classz[0].getMethod(method, parameterTypes); //根據方法上的DS注解的值來設置數據源 if (m != null && m.isAnnotationPresent(DS.class)) { DS annotation = m .getAnnotation(DS.class); dataSource = annotation.value(); methodName = m.getName(); } } catch (Exception e) { logger.error("DataSource switch error:{}", e.getMessage(), e); } finally { logger.info("{} | method {} | datasource {} | begin", ((MethodSignature) point.getSignature()).getMethod().getDeclaringClass(), methodName, dataSource); } DataSourceContextHolder.setDatabaseType(dataSource); point.proceed(); DataSourceContextHolder.clearDatabaseType(); logger.info("{} | method {} | datasource {} | end", ((MethodSignature) point.getSignature()).getMethod().getDeclaringClass(), methodName, dataSource); } }
  • 接下來就可以寫測試用的查詢接口來嘗試一下數據切換功能了。

如果有寫的不好的地方歡迎拍磚

源碼地址:https://github.com/weijiayou/Springboot-DynamicDataSource-Demo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM