spring boot 配置多数据源

记录一下spring boot集成mybatis plus,配置 alibaba druid 多数据源的步骤。

1. 引用mybatis plus,aop,mysql依赖

<dependency>
   <groupId>com.baomidou</groupId>
   <artifactId>mybatis-plus-boot-starter</artifactId>
   <version>${mybatis.plus.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.driver.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

 

2. 生成entity,mapper (略过)

3. 配置mapperlocation,application.yml 配置druid多数据源

server:
  port: 18801


mybatis-plus:
  mapper-locations: classpath:mapper/**.xml
  global-config:
    db-config:
      id-type: auto
      field-strategy: not_empty
      column-underline: true
      logic-delete-value: 0
      logic-not-delete-value: 1
      db-type: mysql
    refresh: false
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: false

druid:
  type: com.alibaba.druid.pool.DruidDataSource
  master:
      url: jdbc:mysql://数据库ip:3306/数据库名称?useUnicode=true&characterEncoding=UTF-8&useSSL=false
      driver-class-name: com.mysql.jdbc.Driver
      username: 数据库账号
      password: 数据库密码
  slave:
      url: jdbc:mysql://数据库ip:3306/数据库名称?useUnicode=true&characterEncoding=UTF-8&useSSL=false
      driver-class-name: com.mysql.jdbc.Driver
      username: 数据库账号
      password: 数据库密码

4.编写database.config

  4.1注入masterDatasource bean和slaveDatasource Bean

package com.ming.project.config.database;

import com.alibaba.druid.support.http.StatViewServlet;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.sql.SQLException;


@Slf4j
@Configuration
@EnableTransactionManagement
public class DataSourceConfiguration {

    @Value("${druid.type}")
    private Class<? extends DataSource> dataSourceType;

    @Bean(name = "masterDataSource")
    @Primary
    @ConfigurationProperties(prefix = "druid.master")
    public DataSource masterDataSource() throws SQLException {
        DataSource masterDataSource = DataSourceBuilder.create().type(dataSourceType).build();
        log.info("------------------master datasource-------------");
        return masterDataSource;
    }

    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "druid.slave")
    public DataSource slaveDataSource() throws SQLException {
        DataSource salveDataSource = DataSourceBuilder.create().type(dataSourceType).build();
        log.info("------------------slave datasource-------------");
        return salveDataSource;
    }

    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean reg = new ServletRegistrationBean();
        reg.setServlet(new StatViewServlet());
        reg.addUrlMappings("/druid/*");
        reg.addInitParameter("allow","localhost");
        reg.addInitParameter("deny","/deny");
        return reg;
    }
}

  4.2 masterDataSource bean、slaveDataSource bean  和 sqlsessionfactory 产生关联,通过AbstractRoutingDataSource,编写实现类集成AbstractRoutingDataSource重写determineCurrentLookupKey方法,返回DatasourceContextHolder持有的数据源    

package com.ming.project.config.database;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.HashMap;

@Configuration
@AutoConfigureAfter(DataSourceConfiguration.class)
public class MybatisPlusConfig {

    @Resource(name = "masterDataSource")
    private DataSource masterDataSource;

    @Resource(name = "slaveDataSource")
    private DataSource slaveDataSource;

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory () throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(roudRobbinDataSourceProxy());
        return sqlSessionFactoryBean.getObject();
    }

    public AbstractRoutingDataSource roudRobbinDataSourceProxy() {
        ReadWriteRoutingDataSource proxy = new ReadWriteRoutingDataSource();
        HashMap<Object, Object> map = new HashMap<>();
        map.put(DatabaseContextHolder.DataBaseType.MASTER,masterDataSource);
        map.put(DatabaseContextHolder.DataBaseType.SLAVE,slaveDataSource);
        proxy.setTargetDataSources(map);
        proxy.setDefaultTargetDataSource(masterDataSource);
        return proxy;
    }
}
package com.ming.project.config.database;

import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

public class ReadWriteRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DatabaseContextHolder.getDataBaseType();
    }
}

  4.3 DatasourceContextHolder封装threadlocal,管理数据源类型

package com.ming.project.config.database;
public class DatabaseContextHolder {

    public enum DataBaseType {
        MASTER, SLAVE;
    }

    private static final ThreadLocal<DataBaseType> contextHolder = new ThreadLocal<>();

    public static void setDataBaseType(DataBaseType dataBaseType) {
        contextHolder.set(dataBaseType);
    }

    public static DataBaseType getDataBaseType() {
        return contextHolder.get() == null ? DataBaseType.MASTER : contextHolder.get();
    }

    public static void clear() {
        contextHolder.remove();
    }

}

   4.4 编写aspect,对annotation进行拦截

package com.ming.project.config.database;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnlyConnection {
}
package com.ming.project.config.database;

import org.apache.logging.log4j.spi.ReadOnlyThreadContextMap;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ReadOnlyConnectionInceptor implements Ordered {

    @Around("@annotation(readOnlyConnection)")
    public Object proceed(ProceedingJoinPoint proceedingJoinPoint, ReadOnlyConnection readOnlyConnection) throws Throwable {
        DatabaseContextHolder.setDataBaseType(DatabaseContextHolder.DataBaseType.SLAVE);
        Object result = proceedingJoinPoint.proceed();
        return result;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

5. 对使用只读数据源的service方法增加注解(略过)

 

总结:多数据源的套路是先在yml中配置多个连接源,接着在配置类中,把多个数据源datasource注入到容器中。mybatis plus通过SqlsessionFactory管理数据源连接数据库,所以要把数据源和SqlSessionFactory关联起来,SqlSessionFactory可以设置AbstractRoutingDataSource类型的数据源,AbstractRoutingDataSource可以把多个数据源放入其中,通过一个map结构,然后通过determineCurrentLookupKey方法返回map的key,找到具体要是用的数据源。所以自己要写一个类ReadWriteRoutingDataSource实现AbstractRoutingDataSource,重写determineCurrentLookupKey方法,至此,多数据源的配置就完成了。但是要使用,怎么使用呢,方法是自定义注解,通过aop的方式拦截有这个注解的方法,在方法执行前设置一下数据源。为了让多个线程使用的数据源隔离,用到了ThreadLocal来管理数据源类型,这里写了一个DatabaseContextHolder做了封装。 好了,现在从配置多数据源到使用都已经完成了。

有需要demo源码的童鞋可以点击这里下载:demo源码

 

posted @ 2020-09-01 23:03  Net_Fly  阅读(208)  评论(0编辑  收藏  举报