這裏用到了Spring Boot + Mybatis + DynamicDataSource配置動態雙數據源,能夠動態切換數據源實現數據庫的讀寫分離。css
加入Mybatis啓動器,這裏添加了Druid鏈接池、Oracle數據庫驅動爲例。spring
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> </dependency>
@EnableMybatis @EnableTransactionManagement @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) public class Application { public static void main(String[] args) { SpringApplication.run(ServiceApplication.class, args); } }
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }): 這裏用到了雙數據源,須要排除數據源的自動配置,若是隻有一個數據源用Spring Boot的自動配置就行。sql
@EnableTransactionManagement:開啓事務支持。數據庫
@EnableMybatis:開啓Mybatis功能微信
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(MybatisConfig.class) public @interface EnableMybatis { }
@Configuration @MapperScan(basePackages = DSConfig.BASE_PACKAGES) public class MybatisConfig implements DSConfig { @Primary @Bean public DynamicDataSource dynamicDataSource(@Qualifier(DB_MASTER) DataSource master, @Qualifier(DB_SLAVE) DataSource slave) { Map<Object, Object> dsMap = new HashMap<>(); dsMap.put(DB_MASTER, master); dsMap.put(DB_MASTER, slave); DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setDefaultTargetDataSource(master); dynamicDataSource.setTargetDataSources(dsMap); return dynamicDataSource; } @Bean public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource) { return new DataSourceTransactionManager(dynamicDataSource); } @Bean public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dynamicDataSource); sessionFactory.setMapperLocations( ((ResourcePatternResolver) new PathMatchingResourcePatternResolver()) .getResources(DSConfig.MAPPER_LOCATIONS)); return sessionFactory.getObject(); } }
DSConfig常量類:session
public interface DSConfig { String DS_PREFIX = "spring.datasource"; String DS_ACTIVE = "active"; String DB_MASTER = "db-master"; String DB_SLAVE = "db-slave"; String DRUID = "druid"; String DRUID_MONITOR_USERNAME = "spring.druid.username"; String DRUID_MONITOR_PASSWORD = "spring.druid.password"; String DRUID_MONITOR_URL = "/druid/*"; String DRUID_FILTER_EXCLUSIONS = "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"; String DRUID_FILTER_URL = "/*"; String BASE_PACKAGES = "com.example.**.mapper"; String MAPPER_LOCATIONS = "mapper/**/*.xml"; }
Druid鏈接池的自動配置類:mybatis
@Configuration @Import({ PropertiesConfig.class }) @ConditionalOnClass(DruidDataSource.class) @ConditionalOnProperty(prefix = DSConfig.DS_PREFIX, value = DSConfig.DS_ACTIVE, havingValue = DSConfig.DRUID) public class DruidAutoConfig implements DSConfig { private Logger logger = LoggerUtils.getLogger(this); @Bean(name = DB_MASTER, initMethod = "init", destroyMethod = "close") public DataSource dataSourceMaster(DruidMasterProperties masterProperties) throws SQLException { logger.debug("master properties: {}", masterProperties.toString()); DruidDataSource dds = new DruidDataSource(); dds.setDriverClassName(masterProperties.getDriverClassName()); dds.setUrl(masterProperties.getUrl()); dds.setUsername(masterProperties.getUsername()); dds.setPassword(masterProperties.getPassword()); dds.setInitialSize(masterProperties.getInitialSize()); dds.setMinIdle(masterProperties.getMinIdle()); dds.setMaxActive(masterProperties.getMaxActive()); dds.setMaxWait(masterProperties.getMaxWait()); dds.setTimeBetweenEvictionRunsMillis(masterProperties.getTimeBetweenEvictionRunsMillis()); dds.setMinEvictableIdleTimeMillis(masterProperties.getMinEvictableIdleTimeMillis()); dds.setValidationQuery(masterProperties.getValidationQuery()); dds.setTestOnBorrow(masterProperties.isTestOnBorrow()); dds.setTestWhileIdle(masterProperties.isTestWhileIdle()); dds.setTestOnReturn(masterProperties.isTestOnReturn()); dds.setPoolPreparedStatements(masterProperties.isPoolPreparedStatements()); dds.setMaxPoolPreparedStatementPerConnectionSize( masterProperties.getMaxPoolPreparedStatementPerConnectionSize()); dds.setFilters(masterProperties.getFilters()); return dds; } @Bean(name = DB_SLAVE, initMethod = "init", destroyMethod = "close") public DataSource dataSourceSlave(DruidSlaveProperties slaveProperties) throws SQLException { logger.debug("slave properties: {}", slaveProperties.toString()); DruidDataSource dds = new DruidDataSource(); dds.setDriverClassName(slaveProperties.getDriverClassName()); dds.setUrl(slaveProperties.getUrl()); dds.setUsername(slaveProperties.getUsername()); dds.setPassword(slaveProperties.getPassword()); dds.setInitialSize(slaveProperties.getInitialSize()); dds.setMinIdle(slaveProperties.getMinIdle()); dds.setMaxActive(slaveProperties.getMaxActive()); dds.setMaxWait(slaveProperties.getMaxWait()); dds.setTimeBetweenEvictionRunsMillis(slaveProperties.getTimeBetweenEvictionRunsMillis()); dds.setMinEvictableIdleTimeMillis(slaveProperties.getMinEvictableIdleTimeMillis()); dds.setValidationQuery(slaveProperties.getValidationQuery()); dds.setTestOnBorrow(slaveProperties.isTestOnBorrow()); dds.setTestWhileIdle(slaveProperties.isTestWhileIdle()); dds.setTestOnReturn(slaveProperties.isTestOnReturn()); dds.setPoolPreparedStatements(slaveProperties.isPoolPreparedStatements()); dds.setMaxPoolPreparedStatementPerConnectionSize( slaveProperties.getMaxPoolPreparedStatementPerConnectionSize()); dds.setFilters(slaveProperties.getFilters()); return dds; } @Bean public ServletRegistrationBean druidServletRegistrationBean(EnvConfig env) { String username = env.getStringValue(DSConfig.DRUID_MONITOR_USERNAME); String password = env.getStringValue(DSConfig.DRUID_MONITOR_PASSWORD); return new ServletRegistrationBean(new DruidStatViewServlet(username, password), DSConfig.DRUID_MONITOR_URL); } @Bean public FilterRegistrationBean druidFilterRegistrationBean() { WebStatFilter wsf = new WebStatFilter(); FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(wsf); filterRegistrationBean.setUrlPatterns(Arrays.asList(DSConfig.DRUID_FILTER_URL)); filterRegistrationBean.setInitParameters( Collections.singletonMap("exclusions", DSConfig.DRUID_FILTER_EXCLUSIONS)); return filterRegistrationBean; } }
根據類路徑下有DruidDataSource這個類即有Druid這個jar包和配置文件中spring.datasource.active=druid纔開啓對Druid鏈接池的自動配置。oracle
導入的配置文件:app
@Configuration @ComponentScan(basePackages = "com.example.common.config.properties") public class PropertiesConfig { }
DruidMasterProperties、DruidSlaveProperties屬性文件讀取的配置省略。ide
鏈接池監控配置類:
public class DruidStatViewServlet extends StatViewServlet { private static final long serialVersionUID = 1L; private String username; private String password; @Override public String getInitParameter(String name) { if ("loginUsername".equals(name)) { return username; } if ("loginPassword".equals(name)) { return password; } return super.getInitParameter(name); } public DruidStatViewServlet(String username, String password) { super(); this.username = username; this.password = password; } public String getUsername() { return username; } public String getPassword() { return password; } }
在META-INF/spring.factories中加入Druid自動配置映射:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.common.config.ds.DruidAutoConfig
切換數據源註解:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DS { String value() default DSConfig.DB_MASTER; }
動態數據源類:
public class DynamicDataSource extends AbstractRoutingDataSource { private final Logger logger = LoggerUtils.getLogger(this); @Override protected Object determineCurrentLookupKey() { logger.debug("當前數據源爲{}", DataSourceContextHolder.getDS()); return DataSourceContextHolder.getDS(); } }
動態數據源AOP實現類:
@Aspect @Component public class DynamicDataSourceAspect { @Before("@annotation(DS)") public void beforeSwitchDS(JoinPoint point) { Class<?> className = point.getTarget().getClass(); String methodName = point.getSignature().getName(); Class<?>[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes(); String dataSource = DataSourceContextHolder.DEFAULT_DS; try { Method method = className.getMethod(methodName, argClass); if (method.isAnnotationPresent(DS.class)) { DS annotation = method.getAnnotation(DS.class); dataSource = annotation.value(); } } catch (Exception e) { e.printStackTrace(); } DataSourceContextHolder.setDS(dataSource); } @After("@annotation(DS)") public void afterSwitchDS(JoinPoint point) { DataSourceContextHolder.clearDS(); } }
綁定當前線程數據源類:
public class DataSourceContextHolder { public static final String DEFAULT_DS = DSConfig.DB_MASTER; private static final ThreadLocal<String> DS_HOLDER = new ThreadLocal<>(); public static void setDS(String dbType) { DS_HOLDER.set(dbType); } public static String getDS() { return (DS_HOLDER.get()); } public static void clearDS() { DS_HOLDER.remove(); } }
掃描關注咱們的微信公衆號,乾貨天天更新。