打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Spring Boot 集成 Mybatis 实现双数据源



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

添加依赖

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

  1. <dependency>

  2.    <groupId>org.mybatis.spring.boot</groupId>

  3.    <artifactId>mybatis-spring-boot-starter</artifactId>

  4. </dependency>

  5. <dependency>

  6.    <groupId>com.alibaba</groupId>

  7.    <artifactId>druid</artifactId>

  8. </dependency>

  9. <dependency>

  10.    <groupId>com.oracle</groupId>

  11.    <artifactId>ojdbc6</artifactId>

  12. </dependency>

添加启动类

  1. @EnableMybatis

  2. @EnableTransactionManagement

  3. @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })

  4. public class Application {

  5.    public static void main(String[] args) {

  6.        SpringApplication.run(ServiceApplication.class, args);

  7.    }

  8. }

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }):这里用到了双数据源,需要排除数据源的自动配置,如果只有一个数据源用Spring Boot的自动配置就行。

@EnableTransactionManagement:开启事务支持。

@EnableMybatis:开启Mybatis功能

  1. @Target(ElementType.TYPE)

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Documented

  4. @Import(MybatisConfig.class)

  5. public @interface EnableMybatis {

  6. }

Mybatis配置类

  1. @Configuration

  2. @MapperScan(basePackages = DSConfig.BASE_PACKAGES)

  3. public class MybatisConfig implements DSConfig {

  4.    @Primary

  5.    @Bean

  6.    public DynamicDataSource dynamicDataSource(@Qualifier(DB_MASTER) DataSource master,

  7.            @Qualifier(DB_SLAVE) DataSource slave) {

  8.        Map<Object, Object> dsMap = new HashMap<>();

  9.        dsMap.put(DB_MASTER, master);

  10.        dsMap.put(DB_MASTER, slave);

  11.        DynamicDataSource dynamicDataSource = new DynamicDataSource();

  12.        dynamicDataSource.setDefaultTargetDataSource(master);

  13.        dynamicDataSource.setTargetDataSources(dsMap);

  14.        return dynamicDataSource;

  15.    }

  16.    @Bean

  17.    public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {

  18.        return new DataSourceTransactionManager(dynamicDataSource);

  19.    }

  20.    @Bean

  21.    public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource)

  22.            throws Exception {

  23.        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();

  24.        sessionFactory.setDataSource(dynamicDataSource);

  25.        sessionFactory.setMapperLocations(

  26.                ((ResourcePatternResolver) new PathMatchingResourcePatternResolver())

  27.                        .getResources(DSConfig.MAPPER_LOCATIONS));

  28.        return sessionFactory.getObject();

  29.    }

  30. }

DSConfig常量类:

  1. public interface DSConfig {

  2.    String DS_PREFIX = 'spring.datasource';

  3.    String DS_ACTIVE = 'active';

  4.    String DB_MASTER = 'db-master';

  5.    String DB_SLAVE = 'db-slave';

  6.    String DRUID = 'druid';

  7.    String DRUID_MONITOR_USERNAME = 'spring.druid.username';

  8.    String DRUID_MONITOR_PASSWORD = 'spring.druid.password';

  9.    String DRUID_MONITOR_URL = '/druid/*';

  10.    String DRUID_FILTER_EXCLUSIONS = '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*';

  11.    String DRUID_FILTER_URL = '/*';

  12.    String BASE_PACKAGES = 'com.example.**.mapper';

  13.    String MAPPER_LOCATIONS = 'mapper/**/*.xml';

  14. }

连接池配置类

Druid连接池的自动配置类:

  1. @Configuration

  2. @Import({ PropertiesConfig.class })

  3. @ConditionalOnClass(DruidDataSource.class)

  4. @ConditionalOnProperty(prefix = DSConfig.DS_PREFIX, value = DSConfig.DS_ACTIVE, havingValue = DSConfig.DRUID)

  5. public class DruidAutoConfig implements DSConfig {

  6.    private Logger logger = LoggerUtils.getLogger(this);

  7.    @Bean(name = DB_MASTER, initMethod = 'init', destroyMethod = 'close')

  8.    public DataSource dataSourceMaster(DruidMasterProperties masterProperties) throws SQLException {

  9.        logger.debug('master properties: {}', masterProperties.toString());

  10.        DruidDataSource dds = new DruidDataSource();

  11.        dds.setDriverClassName(masterProperties.getDriverClassName());

  12.        dds.setUrl(masterProperties.getUrl());

  13.        dds.setUsername(masterProperties.getUsername());

  14.        dds.setPassword(masterProperties.getPassword());

  15.        dds.setInitialSize(masterProperties.getInitialSize());

  16.        dds.setMinIdle(masterProperties.getMinIdle());

  17.        dds.setMaxActive(masterProperties.getMaxActive());

  18.        dds.setMaxWait(masterProperties.getMaxWait());

  19.        dds.setTimeBetweenEvictionRunsMillis(masterProperties.getTimeBetweenEvictionRunsMillis());

  20.        dds.setMinEvictableIdleTimeMillis(masterProperties.getMinEvictableIdleTimeMillis());

  21.        dds.setValidationQuery(masterProperties.getValidationQuery());

  22.        dds.setTestOnBorrow(masterProperties.isTestOnBorrow());

  23.        dds.setTestWhileIdle(masterProperties.isTestWhileIdle());

  24.        dds.setTestOnReturn(masterProperties.isTestOnReturn());

  25.        dds.setPoolPreparedStatements(masterProperties.isPoolPreparedStatements());

  26.        dds.setMaxPoolPreparedStatementPerConnectionSize(

  27.                masterProperties.getMaxPoolPreparedStatementPerConnectionSize());

  28.        dds.setFilters(masterProperties.getFilters());

  29.        return dds;

  30.    }

  31.    @Bean(name = DB_SLAVE, initMethod = 'init', destroyMethod = 'close')

  32.    public DataSource dataSourceSlave(DruidSlaveProperties slaveProperties) throws SQLException {

  33.        logger.debug('slave properties: {}', slaveProperties.toString());

  34.        DruidDataSource dds = new DruidDataSource();

  35.        dds.setDriverClassName(slaveProperties.getDriverClassName());

  36.        dds.setUrl(slaveProperties.getUrl());

  37.        dds.setUsername(slaveProperties.getUsername());

  38.        dds.setPassword(slaveProperties.getPassword());

  39.        dds.setInitialSize(slaveProperties.getInitialSize());

  40.        dds.setMinIdle(slaveProperties.getMinIdle());

  41.        dds.setMaxActive(slaveProperties.getMaxActive());

  42.        dds.setMaxWait(slaveProperties.getMaxWait());

  43.        dds.setTimeBetweenEvictionRunsMillis(slaveProperties.getTimeBetweenEvictionRunsMillis());

  44.        dds.setMinEvictableIdleTimeMillis(slaveProperties.getMinEvictableIdleTimeMillis());

  45.        dds.setValidationQuery(slaveProperties.getValidationQuery());

  46.        dds.setTestOnBorrow(slaveProperties.isTestOnBorrow());

  47.        dds.setTestWhileIdle(slaveProperties.isTestWhileIdle());

  48.        dds.setTestOnReturn(slaveProperties.isTestOnReturn());

  49.        dds.setPoolPreparedStatements(slaveProperties.isPoolPreparedStatements());

  50.        dds.setMaxPoolPreparedStatementPerConnectionSize(

  51.                slaveProperties.getMaxPoolPreparedStatementPerConnectionSize());

  52.        dds.setFilters(slaveProperties.getFilters());

  53.        return dds;

  54.    }

  55.    @Bean

  56.    public ServletRegistrationBean druidServletRegistrationBean(EnvConfig env) {

  57.        String username = env.getStringValue(DSConfig.DRUID_MONITOR_USERNAME);

  58.        String password = env.getStringValue(DSConfig.DRUID_MONITOR_PASSWORD);

  59.        return new ServletRegistrationBean(new DruidStatViewServlet(username, password),

  60.                DSConfig.DRUID_MONITOR_URL);

  61.    }

  62.    @Bean

  63.    public FilterRegistrationBean druidFilterRegistrationBean() {

  64.        WebStatFilter wsf = new WebStatFilter();

  65.        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();

  66.        filterRegistrationBean.setFilter(wsf);

  67.        filterRegistrationBean.setUrlPatterns(Arrays.asList(DSConfig.DRUID_FILTER_URL));

  68.        filterRegistrationBean.setInitParameters(

  69.                Collections.singletonMap('exclusions', DSConfig.DRUID_FILTER_EXCLUSIONS));

  70.        return filterRegistrationBean;

  71.    }

  72. }

根据类路径下有DruidDataSource这个类即有Druid这个jar包和配置文件中spring.datasource.active=druid才开启对Druid连接池的自动配置。

导入的配置文件:

  1. @Configuration

  2. @ComponentScan(basePackages = 'com.example.common.config.properties')

  3. public class PropertiesConfig {

  4. }

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

连接池监控配置类:

  1. public class DruidStatViewServlet extends StatViewServlet {

  2.    private static final long serialVersionUID = 1L;

  3.    private String username;

  4.    private String password;

  5.    @Override

  6.    public String getInitParameter(String name) {

  7.        if ('loginUsername'.equals(name)) {

  8.            return username;

  9.        }

  10.        if ('loginPassword'.equals(name)) {

  11.            return password;

  12.        }

  13.        return super.getInitParameter(name);

  14.    }

  15.    public DruidStatViewServlet(String username, String password) {

  16.        super();

  17.        this.username = username;

  18.        this.password = password;

  19.    }

  20.    public String getUsername() {

  21.        return username;

  22.    }

  23.    public String getPassword() {

  24.        return password;

  25.    }

  26. }

在META-INF/spring.factories中加入Druid自动配置映射:

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

  2. com.example.common.config.ds.DruidAutoConfig


切换数据源

切换数据源注解:

  1. @Target(ElementType.METHOD)

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Documented

  4. public @interface DS {

  5.    String value() default DSConfig.DB_MASTER;

  6. }

动态数据源类:

  1. public class DynamicDataSource extends AbstractRoutingDataSource {

  2.    private final Logger logger = LoggerUtils.getLogger(this);

  3.    @Override

  4.    protected Object determineCurrentLookupKey() {

  5.        logger.debug('当前数据源为{}', DataSourceContextHolder.getDS());

  6.        return DataSourceContextHolder.getDS();

  7.    }

  8. }

动态数据源AOP实现类:

  1. @Aspect

  2. @Component

  3. public class DynamicDataSourceAspect {

  4.    @Before('@annotation(DS)')

  5.    public void beforeSwitchDS(JoinPoint point) {

  6.        Class<?> className = point.getTarget().getClass();

  7.        String methodName = point.getSignature().getName();

  8.        Class<?>[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();

  9.        String dataSource = DataSourceContextHolder.DEFAULT_DS;

  10.        try {

  11.            Method method = className.getMethod(methodName, argClass);

  12.            if (method.isAnnotationPresent(DS.class)) {

  13.                DS annotation = method.getAnnotation(DS.class);

  14.                dataSource = annotation.value();

  15.            }

  16.        } catch (Exception e) {

  17.            e.printStackTrace();

  18.        }

  19.        DataSourceContextHolder.setDS(dataSource);

  20.    }

  21.    @After('@annotation(DS)')

  22.    public void afterSwitchDS(JoinPoint point) {

  23.        DataSourceContextHolder.clearDS();

  24.    }

  25. }

绑定当前线程数据源类:

  1. public class DataSourceContextHolder {

  2.    public static final String DEFAULT_DS = DSConfig.DB_MASTER;

  3.    private static final ThreadLocal<String> DS_HOLDER = new ThreadLocal<>();

  4.    public static void setDS(String dbType) {

  5.        DS_HOLDER.set(dbType);

  6.    }

  7.    public static String getDS() {

  8.        return (DS_HOLDER.get());

  9.    }

  10.    public static void clearDS() {

  11.        DS_HOLDER.remove();

  12.    }

  13. }


本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
基于druid和spring的动态数据库以及读写分离
SpringBoot 实现动态切换多数据源,这么做才叫优雅!
Spring Boot MyBatis 动态数据源切换、多数据源,读写分离
为什么用DDS臀疗有利于女性抗衰老
Spring的AbstractRoutingDataSource 实现 数据源DB的动态切换
SpringBoot + Mybatis 多数据源配置
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服