1. 程式人生 > >spring boot多資料來源配置,多資料來源事務

spring boot多資料來源配置,多資料來源事務

springboot目錄

需求描述

在業務開發中,需要讀寫分離,或者需要配置多個數據源,接下來我們看看在springboot中如何配置多資料來源,支援事務。

想知道springboot如何載入配置以及選擇使用哪個連線池,請移步

application.properties修改如下,主要是新增雙資料來源:

spring.datasource.master.url=${jdbc.master.url}
spring.datasource.master.username=${jdbc.username}
spring.datasource.master.password
=${jdbc.password} spring.datasource.master.driverClassName=com.mysql.jdbc.Driver spring.datasource.master.max-idle=30 spring.datasource.master.max-wait-millis=10000 spring.datasource.master.min-idle=5 spring.datasource.master.initial-size=5 spring.datasource.master.initSQL=SELECT 2 spring.datasource.master
.connection-init-sqls=SELECT 1 spring.datasource.master.validation-query=SELECT 1 spring.datasource.master.validation-query-timeout=3000 #spring.datasource.master.test-on-borrow=true spring.datasource.master.test-while-idle=true spring.datasource.master.time-between-eviction-runs-millis=10000 spring.datasource
.master.max-active=30 spring.datasource.slave.url=${jdbc.slave.url} spring.datasource.slave.username=${jdbc.username} spring.datasource.slave.password=${jdbc.password} spring.datasource.slave.driverClassName=com.mysql.jdbc.Driver spring.datasource.slave.max-idle=30 spring.datasource.slave.max-wait-millis=10000 spring.datasource.slave.min-idle=5 spring.datasource.slave.initial-size=5 spring.datasource.slave.initSQL=SELECT 2 spring.datasource.slave.connection-init-sqls=SELECT 1 spring.datasource.slave.validation-query=SELECT 1 spring.datasource.slave.validation-query-timeout=3000 #spring.datasource.slave.test-on-borrow=true spring.datasource.slave.test-while-idle=true spring.datasource.slave.time-between-eviction-runs-millis=10000 spring.datasource.slave.max-active=30

然後如果想要springboot載入到這兩個配置,需要自定義資料來源以及事務管理器等:

主庫的配置:

import com.netease.generator.EncryptionInterceptor;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = {"repository的包名"}, sqlSessionTemplateRef  = "masterSqlSessionTemplate")
public class MybatisMasterDbConfig {

    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    @Primary
    public DataSource testDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/master/*.xml"));
        bean.setPlugins(new Interceptor[]{new EncryptionInterceptor()});
        return bean.getObject();
    }

    @Bean(name = "masterTransactionManager")
    @Primary
    public DataSourceTransactionManager testTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "masterSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

從庫的配置類:

import com.netease.generator.EncryptionInterceptor;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;


@Configuration
@MapperScan(basePackages = {"repository的包名"}, sqlSessionTemplateRef = "slaveSqlSessionTemplate")
public class MybatisSlaveDbConfig {

    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource testDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "sqlSessionFactory")
    @Primary
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/slave/*.xml"));
        bean.setPlugins(new Interceptor[]{new EncryptionInterceptor()});
        return bean.getObject();
    }

    @Bean(name = "slaveTransactionManager")
    public DataSourceTransactionManager testTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "slaveSqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

可以看到,上述兩個配置類,是根據包的路徑來區分mapper的【我們這裡使用的是mybatis,並且是使用的mapper.xml檔案】。所以只需要建兩個包路徑即可:

**.**.master.mapper
**.**.slave.mapper

假設master的mapper類下有UserMasterMapper.java
假設slave的mapper類下有 UserSlaveMapper.java
這兩個mapper讀者可以自行使用mysql的自動生成工具生成;對應上面配置類中的@MapperScan指定的basepackage地址。
然後mapper.xml也應該有兩個目錄【在 masterSqlSessionFactory 中指定的目錄地址】:
假設resource/mapper/master下有UserMasterMapper.xml
假設resource/mapper/slave下有UserMasterMapper.xml

經過上面的配置,我們就可以愉快的擼程式碼了:

    @Autowired
    private UserMasterMapper userMasterMapper;
    @Autowired
    private UserSlaveMapper userSlaveMapper ;

事務

接下來說一下事務問題。上面主從兩個資料來源的配置類中,各自配置了一個事務管理器masterSqlSessionTemplateslaveSqlSessionTemplate
我們在寫service方法的時候,一定要標識是使用的哪個事務管理器

@Transactional(rollbackFor = Exception.class, transactionManager = "slaveTransactionManager")
    public void testMultiDST(String mobile) {
    }

如果不加事務管理器,預設是第一個載入的資料來源的事務。為了不出一些不可描述的BUG,強烈建議明確指定使用哪個事務管理器。

配置項說明

在application.properties配置檔案中,我們可以看到

#使用DBCP connection pool時,指定初始化時要執行的sql
spring.datasource.slave.connection-init-sqls=SELECT 'x'
#指定獲取連線時連線校驗的sql查詢語句.
spring.datasource.slave.validation-query=SELECT 'x'
# 指定連線校驗查詢的超時時間.
spring.datasource.slave.validation-query-timeout=3000 
#獲取連線時候驗證,會影響效能(不建議true)
spring.datasource.slave.test-on-borrow=false
#驗證連線的有效性
spring.datasource.slave.test-while-idle=true
#空閒連接回收的時間間隔,與test-while-idle一起使用,設定5分鐘
spring.datasource.slave.time-between-eviction-runs-millis=10000

master也同樣配置了。這幾個引數是為了探測MySQL的連結是否存活,

備註:單庫的配置

spring.datasource.url=${star.jdbc.url}
spring.datasource.username=${jdbc.username}
spring.datasource.password=${jdbc.password}
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.tomcat.max-active=30
spring.datasource.tomcat.max-idle=30
spring.datasource.tomcat.max-wait=10000
spring.datasource.tomcat.min-idle=5
spring.datasource.tomcat.initial-size=5
spring.datasource.tomcat.initSQL=SELECT 2
spring.datasource.tomcat.validation-query=SELECT 'x'
spring.datasource.tomcat.validation-query-timeout=3000
#spring.datasource.tomcat.test-on-borrow=true
spring.datasource.tomcat.test-while-idle=true
spring.datasource.tomcat.time-between-eviction-runs-millis=10000
logging.level.com.dy.springboot.server.mapper=debug

參考資料