springboot+mybaits-plus 多資料來源+事務操作(親測有效)
乾貨
配置檔案
@Configuration
@MapperScan({ "com.cn.springboot.*.mapper**"})
public class DynamicDataSourceConfig {
/**
* 在方法上註解configurationProperties時,將會把屬性注入到返回結果的bean中
*/
@ConfigurationProperties(prefix = "spring.datasource.first")
@Bean(name = "firstDatasource")
@Primary
public DruidDataSource firstDatasource() throws SQLException {
return new DruidDataSource();
}
/**
* 在方法上註解configurationProperties時,將會把屬性注入到返回結果的bean中
*/
@ConfigurationProperties(prefix = "spring.datasource.second")
@Bean(name = "secondDatasource")
public DruidDataSource secondDatasource() throws SQLException {
return new DruidDataSource();
}
@Bean(name = "datasource")
public DynamicDataSource dynamicDataSource(@Qualifier(value = "firstDatasource") DataSource firstDatasource,
@Qualifier(value = "secondDatasource") DataSource secondDatasource) {
DynamicDataSource bean = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("firstDatasource", firstDatasource);
targetDataSources.put("secondDatasource", secondDatasource);
bean.setTargetDataSources(targetDataSources);
bean.setDefaultTargetDataSource(firstDatasource);
return bean;
}
@Bean(name = "sqlSessionFactory")
@ConfigurationProperties(prefix = "mybatis-plus")
public SqlSessionFactory sqlSessionFactory(
@Qualifier(value = "datasource") DynamicDataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
/**
* 事務配置,考慮多資料來源情況下 ,這個加了反而事務不起效
* @return
*/
// @Bean
// public PlatformTransactionManager txManager(@Qualifier(value = "datasource") DynamicDataSource dataSource) {
// return new DataSourceTransactionManager(dataSource);
// }
}
package com.cn.springboot.config.mybaits1;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource{
/**
* 取得當前使用哪個資料來源
*
* @return
*/
@Override
protected Object determineCurrentLookupKey(){
return DbContextHolder.getDbType();
}
}
package com.cn.springboot.config.mybaits1;
public enum DBTypeEnum {
FIRSTDATASOURCE("firstDataSource"), SECONDDATASOURCE("secondDataSource");
private String value;
DBTypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
package com.cn.springboot.config.mybaits1;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Order(-100) //這是為了保證AOP在事務註解之前生效,Order的值越小,優先順序越高
public class DataSourceInterceptor {
Logger logger = LoggerFactory.getLogger(DataSourceInterceptor.class);
@Pointcut(value = "execution(public * com.cn.springboot.first.service..*.*(..))")
private void firstServicePointcut() {
};
@Pointcut(value = "execution(public * com.cn.springboot.second.service..*.*(..))")
private void secondServicePointcut() {
};
/**
* 切換資料來源1
*/
@Before("firstServicePointcut()")
public void firstDataSourceInterceptor() {
logger.debug("切換到資料來源{}..............................", "firstDataSource");
DbContextHolder.setDbType(DBTypeEnum.FIRSTDATASOURCE);
}
/**
* 切換資料來源2
*/
@Before("secondServicePointcut()")
public void secondDataSourceInterceptor() {
logger.debug("切換到資料來源{}.......................", "secondDataSource");
DbContextHolder.setDbType(DBTypeEnum.SECONDDATASOURCE);
}
}
package com.cn.springboot.config.mybaits1;
import javax.sql.DataSource;
import org.springframework.beans.factory.InitializingBean;
public final class DataSourceBeanWrapper<T extends DataSource> implements InitializingBean {
private T dataSource;
public DataSourceBeanWrapper(T realDataSource) {
super();
this.dataSource = realDataSource;
}
public T getDataSource() {
return dataSource;
}
public void setDataSource(T dataSource) {
this.dataSource = dataSource;
}
@Override
public void afterPropertiesSet() throws Exception {
if (this.dataSource == null) {
throw new IllegalArgumentException("Property 'dataSources' is required");
}
if (this.dataSource instanceof InitializingBean) {
((InitializingBean) dataSource).afterPropertiesSet();
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("DataSourceBeanWrapper =");
builder.append(getClass().getName() + "@" + Integer.toHexString(hashCode()));
builder.append("; dataSource=");
builder.append(dataSource);
return builder.toString();
}
}
logging.path=/user/local/log
logging.level.com.cn.springboot=DEBUG
logging.level.org.springframework.web=INFO
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
#----DS1----
spring.datasource.first.driver-class-name =
spring.datasource.first.url =
spring.datasource.first.username =
spring.datasource.first.password =
#----DS2---
spring.datasource.second.druid.driver-class-name = com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.second.url =
spring.datasource.second.username =
spring.datasource.second.password =
#---------------------------------------------------------------------------------------
spring.datasource.druid.initial-size = 5
spring.datasource.druid.max-active = 20
spring.datasource.druid.min-idle = 5
spring.datasource.druid.max-wait= 30000
#\u4EE5\u4E0A\u4E3A\u6700\u57FA\u672C\u914D\u7F6E\u3002
#\u6CE8\uFF1Adruid\u5982\u679C\u4E0D\u914D\u7F6EFilter,\u9ED8\u8BA4\u662F\u5F00\u542F\u7684\uFF0C\u8B6C\u5982web-stat-filter\u3001stat-view-servlet\u7B49\uFF0C\u53EF\u4EE5\u901A\u8FC7\u914D\u7F6E\u5C5E\u6027\u8986\u76D6\u9ED8\u8BA4\u914D\u7F6E\uFF0C\u4F8B\u5982\uFF1A
# WebStatFilter monitor
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.web-stat-filter.url-pattern= \*
spring.datasource.druid.web-stat-filter.exclusions= *.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*
spring.datasource.druid.web-stat-filter.session-stat-enable= false
spring.datasource.druid.web-stat-filter.session-stat-max-count= 1000
spring.datasource.druid.web-stat-filter.profile-enable=true
# StatViewServlet ,it configs who can visit
spring.datasource.druid.stat-view-servlet.enabled= true
spring.datasource.druid.stat-view-servlet.url-pattern= /druid/*
spring.datasource.druid.stat-view-servlet.reset-enable= true
spring.datasource.druid.stat-view-servlet.login-username= admin
spring.datasource.druid.stat-view-servlet.login-password= admin123
spring.datasource.druid.stat-view-servlet.allow= 127.0.0.1
#mybatis-plus
#mapper
#mapper.xml路徑
# 如果是放在src/main/java目錄下 classpath:/com/yourpackage/*/mapper/*Mapper.xml
# 如果是放在resource目錄 classpath:/mapper/*Mapper.xm
mybatis-plus.mapper-locations=classpath*:/mapper/*Mapper.xml
#\u5B9E\u4F53
mybatis-plus.typeAliasesPackage=com.cn.springboot.*.entity
#主鍵型別 0:"資料庫ID自增", 1:"使用者輸入ID",2:"全域性唯一ID (數字型別唯一ID)", 3:"全域性唯一ID UUID";
mybatis-plus.global-config.id-type=0
#欄位策略 0:"忽略判斷",1:"非 NULL 判斷"),2:"非空判斷"
mybatis-plus.global-config.field-strategy=2
#駝峰下劃線轉換
mybatis-plus.global-config.db-column-underline=true
#重新整理mapper 除錯神器
mybatis-plus.global-config.refresh-mapper: true
#資料庫大寫下劃線轉換
#mybatis-plus.global-config.capital-mode: true
#序列介面實現類配置
#mybatis-plus.global-config.key-generator: com.baomidou.springboot.xxx
#邏輯刪除配置(下面3個配置)
mybatis-plus.global-config.logic-delete-value: 0
mybatis-plus.global-config.logic-not-delete-value: 1
#自定義SQL注入器
#mybatis-plus.global-config.sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
#自定義填充策略介面實現
#mybatis-plus.global-config.meta-object-handler: com.baomidou.springboot.xxx
#開啟駝峰轉換
mybatis-plus.configuration.mapUnderscoreToCamelCase: true
mybatis-plus.configuration.cache-enabled: false