您的位置:

Springboot多数据源配置和切换

一、Springboot配置双数据源

在Spring Boot应用程序中配置多个数据源是很常见的需求。由于Spring Boot的自动配置机制,使得我们可以轻松优雅的实现多数据源配置和切换。 若要配置双数据源,则需要使用Spring Boot的自动装配机制执行以下操作:

1. 确保我们的应用程序中依赖了Spring Boot的MyBatis Starter,这将为MyBatis集成提供必要的依赖关系和自动配置。在pom.xml文件中添加以下依赖:

<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.1.0</version>
</dependency>

2. 在application.properties中配置双数据源的各项信息:

# primary datasource
spring.datasource.primary.url=jdbc:mysql://localhost:3306/primary_db
spring.datasource.primary.username=dbuser
spring.datasource.primary.password=dbpassword
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver

# secondary datasource
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondary_db
spring.datasource.secondary.username=dbuser
spring.datasource.secondary.password=dbpassword
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver

3. 创建主数据源和次数据源的数据源类:

@Configuration
@MapperScan(basePackages = "com.example.demo.mapper.primary", sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class PrimaryDataSourceConfig {

    @Autowired
    private DataSourceProperties dataSourceProperties;

    @Bean(name = "primaryDataSource")
    @Primary
    public DataSource primaryDataSource() {
        return dataSourceProperties.initializeDataSourceBuilder().build();
    }

    @Bean(name = "primarySqlSessionFactory")
    @Primary
    public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource primaryDataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(primaryDataSource);
        return sessionFactory.getObject();
    }

    @Bean(name = "primaryTransactionManager")
    @Primary
    public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource primaryDataSource) {
        return new DataSourceTransactionManager(primaryDataSource);
    }

    @Bean(name = "primarySqlSessionTemplate")
    @Primary
    public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier("primarySqlSessionFactory") SqlSessionFactory primarySqlSessionFactory) {
        return new SqlSessionTemplate(primarySqlSessionFactory);
    }
}

@Configuration
@MapperScan(basePackages = "com.example.demo.mapper.secondary", sqlSessionTemplateRef  = "secondarySqlSessionTemplate")
public class SecondaryDataSourceConfig {

    @Autowired
    private DataSourceProperties dataSourceProperties;

    @Bean(name = "secondaryDataSource")
    public DataSource secondaryDataSource() {
        return dataSourceProperties.initializeDataSourceBuilder().build();
    }

    @Bean(name = "secondarySqlSessionFactory")
    public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(secondaryDataSource);
        return sessionFactory.getObject();
    }

    @Bean(name = "secondaryTransactionManager")
    public DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
        return new DataSourceTransactionManager(secondaryDataSource);
    }

    @Bean(name = "secondarySqlSessionTemplate")
    public SqlSessionTemplate secondarySqlSessionTemplate(@Qualifier("secondarySqlSessionFactory") SqlSessionFactory secondarySqlSessionFactory) {
        return new SqlSessionTemplate(secondarySqlSessionFactory);
    }
}

4. 创建controller并在其中注入主数据源的mapper和次数据源的mapper

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private PrimaryUserMapper primaryUserMapper;

    @Autowired
    private SecondaryUserMapper secondaryUserMapper;

    @GetMapping("/primary/list")
    public List getPrimaryUsers() {
        return primaryUserMapper.getAll();
    }

    @GetMapping("/secondary/list")
    public List
    getSecondaryUsers() {
        return secondaryUserMapper.getAll();
    }
}

   
  

5. 启动应用并测试多数据源的配置是否正确。

二、Mybatis多数据源配置SpringBoot

当使用Mybatis作为数据持久层框架时,需要按以下步骤在SpringBoot中配置多数据源:

1. 在pom.xml文件中添加以下依赖关系:

<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.1.0</version>
</dependency>

2. 在application.properties或application.yml文件中配置数据源信息:

# primary datasource
spring.datasource.primary.url=jdbc:mysql://localhost:3306/primary_db
spring.datasource.primary.username=dbuser
spring.datasource.primary.password=dbpassword
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver

# secondary datasource
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondary_db
spring.datasource.secondary.username=dbuser
spring.datasource.secondary.password=dbpassword
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver

3. 创建数据源类、SqlSessionFactory、SqlSessionTemplate和TransactionManager:

@Configuration
@MapperScan(basePackages = "com.example.demo.mapper.primary", sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class PrimaryDataSourceConfig {

    @Autowired
    private DataSource primaryDataSource;

    @Bean(name = "primarySqlSessionFactory")
    @Primary
    public SqlSessionFactory primarySqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(primaryDataSource);
        return sessionFactory.getObject();
    }

    @Bean(name = "primaryTransactionManager")
    @Primary
    public DataSourceTransactionManager primaryTransactionManager() {
        return new DataSourceTransactionManager(primaryDataSource);
    }

    @Bean(name = "primarySqlSessionTemplate")
    @Primary
    public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier("primarySqlSessionFactory") SqlSessionFactory primarySqlSessionFactory) {
        return new SqlSessionTemplate(primarySqlSessionFactory);
    }
}

@Configuration
@MapperScan(basePackages = "com.example.demo.mapper.secondary", sqlSessionTemplateRef  = "secondarySqlSessionTemplate")
public class SecondaryDataSourceConfig {

    @Autowired
    private DataSource secondaryDataSource;

    @Bean(name = "secondarySqlSessionFactory")
    public SqlSessionFactory secondarySqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(secondaryDataSource);
        return sessionFactory.getObject();
    }

    @Bean(name = "secondaryTransactionManager")
    public DataSourceTransactionManager secondaryTransactionManager() {
        return new DataSourceTransactionManager(secondaryDataSource);
    }

    @Bean(name = "secondarySqlSessionTemplate")
    public SqlSessionTemplate secondarySqlSessionTemplate(@Qualifier("secondarySqlSessionFactory") SqlSessionFactory secondarySqlSessionFactory) {
        return new SqlSessionTemplate(secondarySqlSessionFactory);
    }
}

4. 创建主数据源mapper和次数据源mapper,并在其中分别使用@Qualifier注解注入对应的数据源的SqlSessionTemplate。

5. 对于需要使用次数据源的Service或Dao类中,使用@Qualifier注解指定对应的SqlSessionTemplate。

6. 启动应用并测试多数据源的配置是否正确。

三、Springboot动态配置数据源

无论是配置双数据源还是多数据源,都需要在application.properties或application.yml文件中预先配置好数据源信息。然而,在生产环境中,可能需要根据用户的访问请求,动态选择不同的数据源。因此,需要在运行时动态配置数据源,具体步骤如下:

1. 在pom.xml文件中添加以下依赖关系:

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

2. 配置与数据源相关的bean:

@Configuration
public class DynamicDataSourceConfig {

    @Autowired
    private DataSourceProperties dataSourceProperties;

    private final Map dataSources = new ConcurrentHashMap<>();

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        dataSource.setUrl(dataSourceProperties.getUrl());
        dataSource.setUsername(dataSourceProperties.getUsername());
        dataSource.setPassword(dataSourceProperties.getPassword());
        return new DynamicRoutingDataSource(dataSource);
    }

    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        return sessionFactory.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate() throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory());
    }
}

  

3. 创建动态数据源类DynamicRoutingDataSource和动态数据源上下文ThreadLocal:

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

    public DynamicRoutingDataSource(DataSource defaultTargetDataSource) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }
}

public class DynamicDataSourceContextHolder {

    private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>();

    public static void setDataSourceKey(String key) {
        CONTEXT_HOLDER.set(key);
    }

    public static String getDataSourceKey() {
        return CONTEXT_HOLDER.get();
    }

    public static void clearDataSourceKey() {
        CONTEXT_HOLDER.remove();
    }
}

  

4. 创建DynamicDataSourceAspect类,在函数执行前获取数据源的key并设置到DynamicDataSourceContextHolder中,函数执行完毕后清除DynamicDataSourceContextHolder。

@Aspect
@Component
public class DynamicDataSourceAspect {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Before("execution(* com.example.demo.service.*.*(..))")
    public void before(JoinPoint point) {
        String dataSourceKey = point.getSignature().getName().contains("primary") ? "primary" : "secondary";
        DynamicDataSourceContextHolder.setDataSourceKey(dataSourceKey);
        logger.info(String.format("设置数据源为 %s", dataSourceKey));
    }

    @After("execution(* com.example.demo.service.*.*(..))")
    public void after(JoinPoint point) {
        DynamicDataSourceContextHolder.clearDataSourceKey();
        logger.info("清除数据源");
    }
}

5. 在需要多数据源的Service实现类中,添加以下注解,这将激活DynamicDataSourceAspect,从而动态设置数据源:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private PrimaryUserMapper primaryUserMapper;

    @Autowired
    private SecondaryUserMapper secondaryUserMapper;

    @Primary
    @DataSource(key = "primary")
    public List getAllPrimaryUsers() {
        return primaryUserMapper.getAll();
    }

    @DataSource(key = "secondary")
    public List
    getAllSecondaryUsers() {
        return secondaryUserMapper.getAll();
    }

    // ...
}

   
  

四、Springboot配置多数据源Druid

如果需要使用连接池,可以选择使用阿里巴巴的Druid。具体步骤如下:

1. 在pom.xml文件中添加以下依赖关系:

<dependency>
    <groupId>com.alibaba
    <artifactId>druid-spring-boot-starter
    <version>1.2.5
</dependency>

2. 在application.properties或application.yml文件中配置DruidDataSource的各项信息:

# primary datasource
spring.datasource.primary.url=jdbc:mysql://localhost:3306/primary_db?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
spring.datasource.primary.username=dbuser
spring.datasource.primary.password=dbpassword
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.primary.type=com.alibaba.druid.pool.DruidDataSource
# druid specific config
spring.datasource.primary.initial-size=0
spring.datasource.primary.min-idle=0
spring.datasource.primary.max-active=20
spring.datasource.primary.max-wait=-1
spring.datasource.primary.time-between-eviction-runs-millis=60000
spring.datasource.primary.min-evictable-idle-time-millis=300000
spring.datasource.primary.validation-query=SELECT 1 FROM DUAL
spring.datasource.primary.test-while-idle=true
spring.datasource.primary.test-on-borrow=false
spring.datasource.primary.test-on-return=false
spring.datasource.primary.pool-prepared-statements=true
spring.datasource.primary.max-pool-prepared-statement-per-connection-size=20
spring.datasource.primary.filters=stat,wall,log4j
spring.datasource.primary.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

# secondary datasource
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondary_db?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
spring.datasource.secondary.username=dbuser
spring.datasource.secondary.password=dbpassword
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.secondary.type=com.alibaba.druid.pool.DruidDataSource
# druid specific config
spring.datasource.secondary.initial-size=0
spring.datasource.secondary.min-idle=0
spring.datasource.secondary.max-active=20
spring.datasource.secondary.max-wait=-1
spring.datasource.secondary.time-between-eviction-runs-millis=600