欢迎光临
我们一直在努力

Spring Boot 集成 Mybatis 实现双数据源

作者:不羁码农 

Spring Boot 集成 Mybatis 实现双数据源

 

这里用到了Spring Boot + Mybatis + DynamicDataSource配置动态双数据源,可以动态切换数据源实现数据库的读写分离。

添加依赖

加入Mybatis启动器,这里添加了Druid连接池、Oracle数据库驱动为例。

<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的自动配置就行。

@EnableTransactionManagement:开启事务支持。
@EnableMybatis:开启Mybatis功能
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MybatisConfig.class)
public @interface EnableMybatis {
}

Mybatis配置类

@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常量类:

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连接池的自动配置类:

@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连接池的自动配置。

导入的配置文件:

@Configuration
@ComponentScan(basePackages = "com.example.common.config.properties")
public class PropertiesConfig {
}

DruidMasterProperties、DruidSlaveProperties属性文件读取的配置省略。

连接池监控配置类:

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();
 }
}
 收藏 (0) 打赏

您可以选择一种方式赞助本站

支付宝扫一扫赞助

微信钱包扫描赞助

未经允许不得转载:英协网 » Spring Boot 集成 Mybatis 实现双数据源

分享到: 生成海报
avatar

热门文章

  • 评论 抢沙发

    • QQ号
    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址

    登录

    忘记密码 ?

    切换登录

    注册

    我们将发送一封验证邮件至你的邮箱, 请正确填写以完成账号注册和激活