SpringBoot實現Mybatis多數據源方案

背景

目前報表導出須要多數據庫的數據,所以咱們須要作Mybatis多數據源的配置javascript

咱們以前使用Spring的AbstractRoutingDataSourcejava

作資源隔離redis限制請求頻率及資源隔離mysql

可是事實上咱們確實存在兩個數據源【非讀寫分離】git

兩個數據源徹底不一樣 換言之在業務上徹底不等價【即A數據源的數據和B數據源的數據不一樣】web

而讀寫分離是A數據源和B數據源的數據相同【至少邏輯等同,好比分片好比讀寫分離】redis

固然利用上述方法依然是能夠完成多數據源,只是須要作動態切換spring

本次咱們使用另外一種實現方式sql

在SpringBoot+MyBatis實現多個SqlSessionFactorytypescript

步驟

因爲咱們系統使用多數據源咱們須要定義兩個數據源數據庫

在application.properties中須要定義兩個數據源

spring.datasource.url=jdbc:mysql://192.168.1.7:3306/f6dms_20160522?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.username=root spring.datasource.password=root spring.datasource2.url=jdbc:mysql://192.168.1.7:3306/f6dms_1116_prod_backup?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true spring.datasource2.driver-class-name=com.mysql.jdbc.Driver spring.datasource2.username=root spring.datasource2.password=root

咱們使用spring.dataSource2做爲第二個數據源的prefix

 

當只有一個數據源的時候因爲druid-starter會自動註冊

@Configuration @ConditionalOnClass(com.alibaba.druid.pool.DruidDataSource.class) @AutoConfigureBefore(DataSourceAutoConfiguration.class) @EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class}) @Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class}) public class DruidDataSourceAutoConfigure { @Bean @ConditionalOnMissingBean public DataSource dataSource() { return new DruidDataSourceWrapper(); } } @ConfigurationProperties("spring.datasource.druid") class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean { @Autowired private DataSourceProperties basicProperties; @Override public void afterPropertiesSet() throws Exception { //if not found prefix 'spring.datasource.druid' jdbc properties ,'spring.datasource' prefix jdbc properties will be used. if (super.getUsername() == null) { super.setUsername(basicProperties.determineUsername()); } if (super.getPassword() == null) { super.setPassword(basicProperties.determinePassword()); } if (super.getUrl() == null) { super.setUrl(basicProperties.determineUrl()); } if (super.getDriverClassName() == null) { super.setDriverClassName(basicProperties.determineDriverClassName()); } } @Autowired(required = false) public void addStatFilter(StatFilter statFilter) { super.filters.add(statFilter); } @Autowired(required = false) public void addConfigFilter(ConfigFilter configFilter) { super.filters.add(configFilter); } @Autowired(required = false) public void addEncodingConvertFilter(EncodingConvertFilter encodingConvertFilter) { super.filters.add(encodingConvertFilter); } @Autowired(required = false) public void addSlf4jLogFilter(Slf4jLogFilter slf4jLogFilter) { super.filters.add(slf4jLogFilter); } @Autowired(required = false) public void addLog4jFilter(Log4jFilter log4jFilter) { super.filters.add(log4jFilter); } @Autowired(required = false) public void addLog4j2Filter(Log4j2Filter log4j2Filter) { super.filters.add(log4j2Filter); } @Autowired(required = false) public void addCommonsLogFilter(CommonsLogFilter commonsLogFilter) { super.filters.add(commonsLogFilter); } @Autowired(required = false) public void addWallFilter(WallFilter wallFilter) { super.filters.add(wallFilter); } }

當DataSource未註冊時會自動註冊DruidWrapper

可是咱們須要兩個數據源所以必須本身註冊

咱們定義一個抽象DataSource

public abstract class AbstractDataSourceConfig { private String driverClassName; /** * JDBC url of the database. */ private String url; /** * Login user of the database. */ private String username; /** * Login password of the database. */ private String password; public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } protected DataSource getDatasource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(this.getUrl()); dataSource.setUsername(this.getUsername()); dataSource.setPassword(this.getPassword()); dataSource.setDriverClassName(this.getDriverClassName()); return dataSource; } }

定義數據源1【注意使用了Primary】 primary的做用是當按照類型註冊的時候當容器中存在多個將會注入這個Bean

/** * @author qixiaobo */ @Configuration @ConfigurationProperties("spring.datasource") public class DataSourceConfig1 extends AbstractDataSourceConfig { @Bean(PRIMARY_DATA_SOURCE_NAME) @Primary public DataSource dataSource1() { DataSource datasource = getDatasource(); return datasource; } }

定義數據源2【注意ConditionalOnProperty會監控系統中存在該property纔會註冊該Bean】

/** * @author qixiaobo */ @Configuration @ConfigurationProperties("spring.datasource2") @ConditionalOnProperty(name = "spring.datasource2.url", matchIfMissing = false) public class DataSourceConfig2 extends AbstractDataSourceConfig { @Bean(SECOND_DATA_SOURCE_NAME) public DataSource dataSource2() { DataSource datasource = getDatasource(); return datasource; } }

以下咱們註冊MybatisConfiguar

public class AbstractMyBatisConfigurer { protected static final String SQL_SESSION_FACTORY_NAME = "SqlSessionFactoryBean"; protected static final String TRANSACTION_MANAGER_NAME = "TransactionManager"; protected static final String DATA_SOURCE_NAME = "DataSource"; protected SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource) { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setTypeAliasesPackage(MODEL_PACKAGE); //配置分頁插件,詳情請查閱官方文檔 PageHelper pageHelper = new PageHelper(); Properties properties = new Properties(); properties.setProperty("pageSizeZero", "true"); //分頁尺寸爲0時查詢全部紀錄再也不執行分頁 properties.setProperty("reasonable", "true"); //頁碼<=0 查詢第一頁,頁碼>=總頁數查詢最後一頁 properties.setProperty("supportMethodsArguments", "false"); //支持經過 Mapper 接口參數來傳遞分頁參數 pageHelper.setProperties(properties); //添加插件 factory.setPlugins(new Interceptor[]{pageHelper, new SoInterceptor(), new MybatisTransactionTimeoutInterceptor()}); org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration(); config.setDefaultStatementTimeout(5); config.setDefaultFetchSize(10000); config.setDefaultExecutorType(ExecutorType.REUSE); config.setLogImpl(Slf4jImpl.class); config.setLogPrefix("dao."); factory.setConfiguration(config); return factory; } protected MapperScannerConfigurer getMapperScannerConfigurer() { MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); //配置通用Mapper,詳情請查閱官方文檔 Properties properties = new Properties(); properties.setProperty("mappers", MAPPER_INTERFACE_REFERENCE); properties.setProperty("notEmpty", "false"); //insert、update是否判斷字符串類型!='' 即 test="str != null"表達式內是否追加 and str != '' properties.setProperty("IDENTITY", "MYSQL"); mapperScannerConfigurer.setProperties(properties); return mapperScannerConfigurer; } }
@Configuration public class MybatisConfigurer extends AbstractMyBatisConfigurer { public static final String PRIMARY_SQL_SESSION_FACTORY_NAME = Constants.LEVEL_PRIMARY + SQL_SESSION_FACTORY_NAME; public static final String PRIMARY_TRANSACTION_MANAGER_NAME = Constants.LEVEL_PRIMARY + TRANSACTION_MANAGER_NAME; public static final String PRIMARY_DATA_SOURCE_NAME = Constants.LEVEL_PRIMARY + DATA_SOURCE_NAME; @Bean @Primary public SqlSessionFactory sqlSessionFactoryBean(@Autowired DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = getSqlSessionFactoryBean(dataSource); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/**/*.xml")); return sqlSessionFactoryBean.getObject(); } @Bean public MapperScannerConfigurer mapperScannerConfigurer() { MapperScannerConfigurer mapperScannerConfigurer = getMapperScannerConfigurer(); mapperScannerConfigurer.setBasePackage(MAPPER_PACKAGE); return mapperScannerConfigurer; } @Bean @Primary public DataSourceTransactionManager transactionManager1(@Autowired DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
/* * Copyright (c) 2017. Lorem ipsum dolor sit amet, consectetur adipiscing elit. * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. * Vestibulum commodo. Ut rhoncus gravida arcu. */ package com.f6car.base.config; import com.f6car.base.constant.Constants; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import tk.mybatis.spring.mapper.MapperScannerConfigurer; import javax.sql.DataSource; import static com.f6car.base.config.MyBatisConfigurer2.SECOND_DATA_SOURCE_NAME; import static com.f6car.base.constant.Constants.MAPPER_PACKAGE; /** * @author qixiaobo */ @Configuration @ConditionalOnBean(name = SECOND_DATA_SOURCE_NAME) public class MyBatisConfigurer2 extends AbstractMyBatisConfigurer { public static final String SECOND_TRANSACTION_MANAGER_NAME = Constants.LEVEL_SECOND + TRANSACTION_MANAGER_NAME; public static final String SECOND_SQL_SESSION_FACTORY_NAME = Constants.LEVEL_SECOND + SQL_SESSION_FACTORY_NAME; public static final String SECOND_DATA_SOURCE_NAME = Constants.LEVEL_SECOND + DATA_SOURCE_NAME; @Bean public MapperScannerConfigurer mapperScannerConfigurer2() { MapperScannerConfigurer mapperScannerConfigurer = getMapperScannerConfigurer(); mapperScannerConfigurer.setSqlSessionFactoryBeanName(SECOND_SQL_SESSION_FACTORY_NAME); mapperScannerConfigurer.setBasePackage(MAPPER_PACKAGE + "2"); return mapperScannerConfigurer; } @Bean(name = SECOND_TRANSACTION_MANAGER_NAME) public DataSourceTransactionManager transactionManager2(@Autowired @Qualifier(SECOND_DATA_SOURCE_NAME) DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = SECOND_SQL_SESSION_FACTORY_NAME) public SqlSessionFactory sqlSessionFactoryBean2(@Autowired @Qualifier(SECOND_DATA_SOURCE_NAME) DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = getSqlSessionFactoryBean(dataSource); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper2/**/*.xml")); return sqlSessionFactoryBean.getObject(); } }

mapper註冊爲第一個數據源

mapper2註冊爲第二個數據源

咱們以下文件結構

這樣就能夠完成多數據源的配置

相關文章
相關標籤/搜索