java多数据源事务管理_Spring中实现多数据源事务管理 - CSDN博客
前言
由于项目中引入了多个数据源,并且需要对多个数据源进行写操作,那么多数据源的事务管理自然成了不可避免的问题,这也让我对 @Transactional注解有了进一步的理解(但实际上也并不是非常深入)
然而这是一个演进的过程,刚开始项目中并没有使用 @Transactional指定具体的 TransactionManager,所以新增一个数据源后,对原有的事务产生了影响了,这也是偶尔在一次测试报错而结果没有回滚之后才发现的,遂对于 @Transactional注解的一些参数项进行了了解。
研究
由于容器中存在两个 TransactionManager,那么被 @Transactional注解的方法到底使用了哪个 TransactionManager来进行事务管理,抑或是同时使用了两个 TransactionManager来进行事务管理都是我们需要搞清楚的问题。
首先我们先看看 @Transactional注解上有没有提供配置项来指定 TransactionManager,果不其然,发现 value属性就是用来指定具体 TransactionManager的,通过 id或者 name来指定唯一一个 TransactionManager,那么对于只需要一个事务管理的方法,问题就简单多了: @Transactional(value = "database2TransactionManager")
public void test(String a) {
// business operation
}1
2
3
4
关于不指定TransactionManager时会使用哪一个TransactionManager,有兴趣的童鞋可以参考另一篇文章,讲的比较清晰: http://blog.sina.com.cn/s/blog_8f61307b0100ynfb.html
好了,回到我们研究的问题,那么对于需要写入多个数据源的业务方法该怎么办呢?
进一步研究
看来 @Transactional是没有提供这种功能了,那么就自己写了一个吧。我记得 Spring中的事务管理分编程式事务和声明式事务。我们平时使用的 @Transactional就是声明式事务,它的好处其实也就是灵活度更高、代码的耦合性更低,最终的事务管理实现还是一样的,只不过将具体逻辑都剥离到了切面中。所以我们可以手写一个切面来写一次“编程式事务”,当然在具体应用时,还是声明式的。
Java中一般编程式事务的写法: public class UserServiceImpl implements UserService {
@Resource
private TransactionManager txManager;
@Resource
private UserDao userDao;
@Resource
private AddressDao addressDao;
public boolean saveUser(User user) {
TransactionDefinition txDefinition = new TransactionDefinition();
TransactionStatus txStatus = txManager.getTransaction(txDefinition);
boolean result = false;
try {
result = userDao.save(user);
if(!result){
return false;
}
result = addressDao.save(user.getId(), user.getAddress());
txManager.commit(txStatus);
} catch (Exception e) {
result = false;
txManager.rollback(txStatus);
}
return result;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
我们借用这个逻辑将事务管理相关提取到切面中,并在进入目标方法之前,让多个 TransactionManager都开启事务,并在成功执行后一并提交或失败后一并回滚,具体代码: /**
* @author Zhu
* @date 2015-7-15
* @version 0.0.1
* @description
*/
public class MultiTransactionalAspect {
private Logger logger = LoggerFactory.getLogger(getClass());
public Object around(ProceedingJoinPoint pjp,
MultiTransactional multiTransactional) throws Throwable {
Stack dataSourceTransactionManagerStack = new Stack();
Stack transactionStatuStack = new Stack();
try {
if (!openTransaction(dataSourceTransactionManagerStack,
transactionStatuStack, multiTransactional)) {
return null;
}
Object ret = pjp.proceed();
commit(dataSourceTransactionManagerStack, transactionStatuStack);
return ret;
} catch (Throwable e) {
rollback(dataSourceTransactionManagerStack, transactionStatuStack);
logger.error(String.format(
"MultiTransactionalAspect, method:%s-%s occors error:", pjp
.getTarget().getClass().getSimpleName(), pjp
.getSignature().getName()), e);
throw e;
}
}
/**
* @author Zhu
* @date 2015-7-25下午7:55:46
* @description
* @param dataSourceTransactionManagerStack
* @param transactionStatuStack
* @param values
*/
private boolean openTransaction(
Stack dataSourceTransactionManagerStack,
Stack transactionStatuStack,
MultiTransactional multiTransactional) {
String[] transactionMangerNames = multiTransactional.values();
if (ArrayUtils.isEmpty(multiTransactional.values())) {
return false;
}
for (String beanName : transactionMangerNames) {
DataSourceTransactionManager dataSourceTransactionManager = (DataSourceTransactionManager) ContextHolder
.getBean(beanName);
TransactionStatus transactionStatus = dataSourceTransactionManager
.getTransaction(new DefaultTransactionDefinition());
transactionStatuStack.push(transactionStatus);
dataSourceTransactionManagerStack
.push(dataSourceTransactionManager);
}
return true;
}
/**
* @author Zhu
* @date 2015-7-25下午7:56:39
* @description
* @param dataSourceTransactionManagerStack
* @param transactionStatuStack
*/
private void commit(
Stack dataSourceTransactionManagerStack,
Stack transactionStatuStack) {
while (!dataSourceTransactionManagerStack.isEmpty()) {
dataSourceTransactionManagerStack.pop().commit(
transactionStatuStack.pop());
}
}
/**
* @author Zhu
* @date 2015-7-25下午7:56:42
* @description
* @param dataSourceTransactionManagerStack
* @param transactionStatuStack
*/
private void rollback(
Stack dataSourceTransactionManagerStack,
Stack transactionStatuStack) {
while (!dataSourceTransactionManagerStack.isEmpty()) {
dataSourceTransactionManagerStack.pop().rollback(
transactionStatuStack.pop());
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
整体结构很清晰:
1. 首先根据指定的多个 TransactionManager依次开启事务,这个次序不影响,因为其实大家都是平等的。
2. 其次就是调用目标方法执行具体的业务逻辑
3. 若是成功返回则提交每个事务,若中途报错,那么就回滚每个事务
其中为什么要用 Stack来保存 TransactionManager和 TransactionStatus呢?那是因为 Spring的事务处理是按照 LIFO/stack behavior的方式进行的。如若顺序有误,则会报错: java.lang.IllegalStateException: Cannot deactivate transaction synchronization - not active
at org.springframework.transaction.support.TransactionSynchronizationManager.clearSynchronization(TransactionSynchronizationManager.java:313)
at org.springframework.transaction.support.TransactionSynchronizationManager.clear(TransactionSynchronizationManager.java:451)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager.java:986)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:782)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactio1
2
3
4
5
6
题外话
刚开始碰到这个问题的时候,先想到的是分布式事务管理,也去看了JTA相关的文章,但是好像比较麻烦,而且都是一些老文章,于是想试试自己实现,最后也实现了。所以想知道 JTA TransactionManager究竟有什么用呢?
java多数据源事务管理_Spring中实现多数据源事务管理 - CSDN博客相关推荐
- 新版CSDN中如何快速转载别人的CSDN博客,详细方法与步骤!!!
前言 作为DSCN博客用户小萌新,有可能自己写的博客还不够好,或者当看到别人写的特别好的博客时,就想转载(有些人可能会问:不是可以收藏吗?当然可以收藏,但是有些人也会想转载下载,不可能一个一个字再敲吧 ...
- hikari数据源配置类_Spring中的“多数据源”之详解
注意事项 不同数据源的配置要分开 关注每次使用的数据源 有多个DataSource时系统如何判断 对应的设施(事务.ORM)如何选择DataSource 多数据源配置(二选一) 配置@Primary类 ...
- CSDN博客中嵌入背景音乐
CSDN博客中嵌入背景音乐 如果想在CSDN博客中设置背景音乐要自己插入HTML代码 在编辑博客有两种模式,HTML编辑器(默认)和markDown编辑器 切换到HTML编辑器,点击上方插入HTML代 ...
- log4jdbc oracle,通过weblogic配置log4jdbc数据源,在项目中使用该数据源,输出sql
通过weblogic配置log4jdbc数据源,在项目中使用该数据源,输出sql 通过weblogic配置log4jdbc数据源,在项目中使用该数据源,输出sql 说一下配置: 服务器weblogic ...
- 操作系统核心原理-5.内存管理(中):分页内存管理
在上一篇介绍的几种多道编程的内存管理模式中,以交换内存管理最为灵活和先进.但是这种策略也存在很多重大问题,而其中最重要的两个问题就是空间浪费和程序大小受限.那么有什么办法可以解决交换内存存在的这些问题 ...
- java用正则表达式判断字符串中是否仅包含英文字母、数字和汉字_灵思致远Leansmall的博客-CSDN博客_java判断字符串只包含数字字母
import java.util.regex.Matcher; import java.util.regex.Pattern;public class StrValidate {// 纯数字priva ...
- 解决管理工具中没有Internet 信息服务(IIS)管理器
控制面板下的系统和安全下的管理工具中没有Internet 信息服务(IIS)管理器, 按路径"c:\windows\system32\inetsrv"确认是否有iis.m ...
- CSDN博客 论坛—— 我的2013 年度征文活动火爆进行中 【已结束】
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 2013 ...
- java支持html5视频流技术Pseudostreaming – Roden的专栏 – CSDN博客
文章目录[隐藏] java支持html5视频流技术Pseudostreaming 首页博客学院 下载论坛APP 问答商城活动VIP会员专题招聘ITeyeGitChat 图文课 写博客 消息 登录注册 ...
最新文章
- 域服务器 ssl证书,服务器添加ssl证书及域名绑定
- 使用OC语言批量修改文件名称
- Java泛型主题讨论
- python123手机版math库-python学习笔记---math,random,operator(三)
- Mongo DB教程及SQL与Mongo DB查询的映射
- 《Essential C++》笔记之设计一个泛型算法(二)
- 推荐系统系列教程之十五:一网打尽协同过滤、矩阵分解和线性模型
- position:fixed 失效
- 传奇服务器维护活力值怎么算,电竞传奇活力值怎么补满 电竞传奇快速恢复方法...
- Java类权限和类成员权限举例解析
- 华为eNSP-动态路由实验
- 手心拼音输入法 v1.0 官方版
- 天眼查 乱码 java_反爬虫解析-字体替换(天眼查/猫眼电影)
- Win10 64位+Mysql5.7+主从同步配置
- 无线网卡无信号无服务器,无线网卡不能连接网络是什么原因?
- “卖给我一支笔”:乔布斯会如何应对这个经典问题?
- 用python写名片管理系统
- Python 批量合并多个txt文件
- 漂洋过海看你(英文版) BY 郭圣怡
- UI自动化测试环境搭建 Python+Selenium+RobotFramework