springboot動態(tài)數(shù)據(jù)源+分布式事務(wù)的實(shí)現(xiàn)
1.引入jta-atomikos
這個springboot 是自帶的。我的springboot版本為2.5.9,數(shù)據(jù)庫為mysql。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>2.配置數(shù)據(jù)源(application.yml)
spring:
# 數(shù)據(jù)源配置
jta:
enabled: true
datasource:
master:
# 主庫數(shù)據(jù)源
url:
username:
password:
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
# 從庫數(shù)據(jù)源
url:
username:
password:
driver-class-name: com.mysql.cj.jdbc.Driver新建文件 DataSourceConfig ,配置數(shù)據(jù)源及線程池。你有幾個數(shù)據(jù)源就建立幾個XXXDataSource方法,最后放到dynamicDataSource方法中的Map中去。事務(wù)管理一定要用JtaTransactionManager。SqlSessionFactory 是可以不用寫的,如果要寫的話,如果你用了mybatis-plus,一定要用MybatisSqlSessionFactoryBean,不然在啟動或者調(diào)用Mybatis-plus的方法時,會報找不到bean的錯。
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;
import javax.sql.DataSource;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@Aspect
@Configuration
@EnableTransactionManagement
public class DataSourceConfig {
@Autowired
private Environment env;
@Bean(name = "masterDataSource")
public DataSource masterDataSource() {
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
ds.setUniqueResourceName("master");
ds.setXaDataSourceClassName("com.mysql.cj.jdbc.MysqlXADataSource");
ds.setXaProperties(getXaProperties("master"));
ds.setMaxPoolSize(10);
ds.setMinPoolSize(5);
ds.setBorrowConnectionTimeout(60);
return ds;
}
@Bean(name = "slaveDataSource")
public DataSource slaveDataSource() {
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
ds.setUniqueResourceName("slave");
ds.setXaDataSourceClassName("com.mysql.cj.jdbc.MysqlXADataSource");
ds.setXaProperties(getXaProperties("slave"));
ds.setMaxPoolSize(10);
ds.setMinPoolSize(5);
ds.setBorrowConnectionTimeout(60);
return ds;
}
@Bean
@Primary
public DataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource) {
DynamicDataSource dataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource);
targetDataSources.put("slave", slaveDataSource);
dataSource.setTargetDataSources(targetDataSources);
dataSource.setDefaultTargetDataSource(masterDataSource);
return dataSource;
}
@Bean
public UserTransaction userTransaction() throws SystemException {
UserTransactionImp userTransactionImp = new UserTransactionImp();
userTransactionImp.setTransactionTimeout(10000);
return userTransactionImp;
}
@Bean(initMethod = "init", destroyMethod = "close")
public UserTransactionManager userTransactionManager() {
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setForceShutdown(false);
return userTransactionManager;
}
@Bean
public PlatformTransactionManager transactionManager(UserTransaction userTransaction,
UserTransactionManager userTransactionManager) {
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setUserTransaction(userTransaction);
jtaTransactionManager.setTransactionManager(userTransactionManager);
return jtaTransactionManager;
}
private Properties getXaProperties(String dataSourceType) {
Properties xaProps = new Properties();
String username = getPropertyValue("spring.datasource." + dataSourceType + ".username");
String password = getPropertyValue("spring.datasource." + dataSourceType + ".password");
String url = getPropertyValue("spring.datasource." + dataSourceType + ".url");
xaProps.put("user", username);
xaProps.put("password", password);
xaProps.put("url", url);
return xaProps;
}
private String getPropertyValue(String str) {
return env.getProperty(str);
}
}新建文件DynamicDataSource,這個類繼承AbstractRoutingDataSource。
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
String dataSourceKey = DataSourceContextHolder.getDataSource();
if (dataSourceKey == null) {
return "master"; // 默認(rèn)數(shù)據(jù)源
}
return dataSourceKey;
}
}建立一個上下文DataSourceContextHolder文件,用于讀取當(dāng)前數(shù)據(jù)源。
public class DataSourceContextHolder {
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void setDataSource(String dataSourceName) {
threadLocal.set(dataSourceName);
}
public static String getDataSource() {
return threadLocal.get();
}
public static void clearDataSource() {
threadLocal.remove();
}
}3.配置注解
這個沒啥好說的,文件名自己定義都可以。注意后面的默認(rèn)數(shù)據(jù)源參數(shù),配置了的話一定要是你的。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MDS {
String value() default "master";
}4.切面
在類上使用上面的自定義注解。然后用切面去切換數(shù)據(jù)源。這里我用了3個判斷,首先會先判斷方法上有沒有自定義注解,其次類上,都沒有就用默認(rèn)的數(shù)據(jù)源。
import cn.hutool.core.annotation.AnnotationUtil;
import com.rs.common.annotation.MDS;
import com.rs.framework.config.dynamic.DataSourceContextHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Order(0)
@Component
public class DataSourceAspect {
@Before("@within(mds) || @annotation(mds)")
public void before(JoinPoint joinPoint, MDS mds) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Class<?> targetClass = method.getDeclaringClass();
Object value = AnnotationUtil.getAnnotationValue(targetClass, MDS.class);
if(mds != null && mds.value() != null){
DataSourceContextHolder.setDataSource(mds.value());
} else if (value != null) {
DataSourceContextHolder.setDataSource((String) value);
} else {
DataSourceContextHolder.setDataSource("master");
}
}
@AfterReturning(pointcut = "@within(mds) ||@annotation(mds)", returning = "returnVal")
public void afterReturning(JoinPoint joinPoint, MDS mds, Object returnVal) {
DataSourceContextHolder.clearDataSource();
}
@AfterThrowing(pointcut = "@within(mds) ||@annotation(mds)", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, MDS mds, Throwable ex) {
DataSourceContextHolder.clearDataSource();
}
}5.如何使用
一個方法中執(zhí)行不同數(shù)據(jù)源操作。getUserPageList方法使用的是默認(rèn)數(shù)據(jù)源master,即最后的返回是返回master的查詢結(jié)果。updateBusinessStatus方法使用的是從庫數(shù)據(jù)源,在執(zhí)行完sql后我寫了一個異常。
這里需要注意的是,一定要寫@Transactional(rollbackFor = Exception.class),且是寫在方法上!?。e為了省事寫在類上,寫在類上發(fā)生異常不會回滾(親測)。
@Override
@Transactional(rollbackFor = Exception.class)
public IPage<SysUserDto> getUserPageList(SysUser user, Page<SysUser> page) {
user = new SysUser();
user.setUserId(29L);
user.setDelFlag("0");
sysUserMapper.updateById(user);
flwProcessService.updateBusinessStatus("1851531692768452608",3);
return sysUserMapper.getUserPageList(user,page.page());
}
@Override
@MDS("slave")
@Transactional(rollbackFor = Exception.class)
public void updateBusinessStatus(String procInstId, Integer businessStatus) {
flwProcessMapper.update(null,new UpdateWrapper<FlwProcess>().lambda()
.eq(FlwProcess::getProcInstId,procInstId)
.set(FlwProcess::getBusinessStatus,businessStatus)
);
int i = 1/0;
}注意事項
一個方法不同數(shù)據(jù)源的操作@Transactional(rollbackFor = Exception.class)寫在類上?。?!
一個方法不同數(shù)據(jù)源的操作@Transactional(rollbackFor = Exception.class)寫在類上?。。?/p>
一個方法不同數(shù)據(jù)源的操作@Transactional(rollbackFor = Exception.class)寫在類上?。?!
到此這篇關(guān)于springboot動態(tài)數(shù)據(jù)源+分布式事務(wù)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)springboot動態(tài)數(shù)據(jù)源+分布式事務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot中動態(tài)數(shù)據(jù)源配置與使用詳解
- SpringBoot配置動態(tài)數(shù)據(jù)源的實(shí)戰(zhàn)詳解
- SpringBoot自定義動態(tài)數(shù)據(jù)源的流程步驟
- SpringBoot實(shí)現(xiàn)動態(tài)數(shù)據(jù)源切換的項目實(shí)踐
- SpringBoot動態(tài)數(shù)據(jù)源連接測試的操作詳解
- SpringBoot實(shí)現(xiàn)動態(tài)數(shù)據(jù)源切換的方法總結(jié)
- springboot配置多數(shù)據(jù)源(靜態(tài)和動態(tài)數(shù)據(jù)源)
- SpringBoot中動態(tài)數(shù)據(jù)源是實(shí)現(xiàn)與用途
- springboot 動態(tài)數(shù)據(jù)源的實(shí)現(xiàn)方法(Mybatis+Druid)
相關(guān)文章
java swing實(shí)現(xiàn)簡單的五子棋游戲
這篇文章主要為大家詳細(xì)介紹了java swing實(shí)現(xiàn)簡單的五子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-03-03
SpringBoot整合Shiro思路(最新超詳細(xì))
這篇文章主要介紹了SpringBoot整合Shiro思路(最新超詳細(xì)),本文內(nèi)容比較長,通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03
Java導(dǎo)入、導(dǎo)出excel用法步驟保姆級教程(附封裝好的工具類)
這篇文章主要介紹了Java導(dǎo)入、導(dǎo)出excel的相關(guān)資料,講解了使用Java和ApachePOI庫將數(shù)據(jù)導(dǎo)出為Excel文件,包括創(chuàng)建工作簿、工作表、行和單元格,設(shè)置樣式和字體,合并單元格,添加公式和下拉選擇等功能,需要的朋友可以參考下2025-03-03
Springboot2.x+ShardingSphere實(shí)現(xiàn)分庫分表的示例代碼
這篇文章主要介紹了Springboot2.x+ShardingSphere實(shí)現(xiàn)分庫分表的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
Java實(shí)現(xiàn)PDF在線預(yù)覽功能(四種方式)
這篇文章主要介紹了Java實(shí)現(xiàn)PDF在線預(yù)覽功能的四種方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
在Spring Boot中淺嘗內(nèi)存泄漏的實(shí)戰(zhàn)記錄
本文給大家分享在Spring Boot中淺嘗內(nèi)存泄漏的實(shí)戰(zhàn)記錄,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2025-04-04
關(guān)于Java鎖性能提高(鎖升級)機(jī)制的總結(jié)
這篇文章主要介紹了關(guān)于Java鎖性能提高(鎖升級)機(jī)制的總結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05

