事务控制的完整代码位于:http://git.oschina.net/zycgit/hasor,DB模块中。
在开始实现事务管理器之前,我们需要知道。这个系统中都有那些元素?
数据库连接:
数据库连接是封装的最基本的元素,我们上文中提到的引用计数可以封装在这个对象上。
资源管理器:
首先程序不能直接通过 DataSource 取得数据库连接,而是必须通过资源管理器去申请。它有一些充当数据库连接池的角色。但是请大家千万要和数据库连接池区分开,资源管理器在这里的主要作用是管理当前连接。
事务状态:
事务状态是封装了“new”、“suspent”、“savepoint”等状态的封装对象,每当从事务管理器中开启一个行为的事务之后。事务管理器都会创建一个事务状态用以表示。当递交事务的时候也需要程序提供相应的状态,告诉事务管理器执行那个点上的事务操作。
事务管理器:
一切元素的控制中心,它就是核心部件。专门用来实现事务控制的。
---------------------------------------------------------------------------------
构建程序逻辑框架:
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
|
public
class
DefaultTransactionManager
implements
TransactionManager {
/**开启事务*/
public
final
TransactionStatus getTransaction(TransactionBehavior behavior)
throws
SQLException {
return
getTransaction(behavior, TransactionLevel.ISOLATION_DEFAULT);
};
/**开启事务*/
public
final
TransactionStatus getTransaction(TransactionBehavior behavior, TransactionLevel level)
throws
SQLException {
......
}
/**判断连接对象是否处于事务中,该方法会用于评估事务传播属性的处理方式。 */
private
boolean
isExistingTransaction(TransactionStatus defStatus)
throws
SQLException {
......
};
/**初始化一个新的连接,并开启事务。*/
protected
void
doBegin(TransactionStatus defStatus)
throws
SQLException {
......
}
/**递交事务*/
public
void
commit(TransactionStatus status)
throws
SQLException {
......
}
/**回滚事务*/
public
void
rollBack(TransactionStatus status)
throws
SQLException {
......
}
//
/**挂起事务。*/
protected
void
suspend(DefaultTransactionStatus defStatus) {
......
}
/**恢复被挂起的事务。*/
protected
void
resume(DefaultTransactionStatus defStatus) {
......
}
/**commit,rollback。之后的清理工作,同时也负责恢复事务和操作事务堆栈。*/
private
void
cleanupAfterCompletion(DefaultTransactionStatus defStatus) {
}
/**获取数据库连接(线程绑定的)*/
protected
TransactionObject doGetConnection(DefaultTransactionStatus defStatus) {
......
};
/**释放数据库连接*/
protected
void
releaseConnection(DefaultTransactionStatus defStatus) {
......
}
}
|
getTransaction方法
这个方法是我们用于处理开启事务的方法,在上一篇文章中开启事务方面的程序都是从这里开始写的。当创建好事务应当返回一个事务状态对象用 TransactionStatus 接口去封装。
isExistingTransaction方法
这个方法是专门用于判断数据库连接是否存在事务的。我们可以想象,它一定会和 Connection 打交道的。在前面几篇文章中我已经介绍过,几乎每种传播行为都会去判断是否存在事务。因此单独分离出这个方法也比较有用。
doBegin方法
处理开启事务的操作,还记得new状态把。凡是没有开启事务的数据库连接只要开启了事务都是满足了new状态的。因此设置new状态的代码就可以在这里去实现它。
commit方法、rollBack方法
事务的递交和回滚操作入口,在这两个方法里会去判断那一大堆状态以及如何进行递交或回滚的操作。
suspend方法、resume方法
挂起和恢复,挂起方法主要做的事情就是将当前数据库连接清除。然后重新申请一个连接作为当前连接,恢复方法是将一个数据库连接重新作为当前连接。
cleanupAfterCompletion方法
负责清理事务状态的方法。
doGetConnection方法
负责申请数据库连接,并处理引用计数+1。
releaseConnection方法
负责释放连接,引用计数-1。
---------------------------------------------------------------------------------
下面这个是getTransaction方法的实现:
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
|
/**开启事务*/
public
final
TransactionStatus getTransaction(TransactionBehavior behavior, TransactionLevel level)
throws
SQLException {
Hasor.assertIsNotNull(behavior);
Hasor.assertIsNotNull(level);
//1.获取连接
DefaultTransactionStatus defStatus =
new
DefaultTransactionStatus(behavior, level);
defStatus.setTranConn(doGetConnection(defStatus));
/*-------------------------------------------------------------
| 环境已经存在事务
|
| PROPAGATION_REQUIRED :加入已有事务(不处理)
| RROPAGATION_REQUIRES_NEW :独立事务(挂起当前事务,开启新事务)
| PROPAGATION_NESTED :嵌套事务(设置保存点)
| PROPAGATION_SUPPORTS :跟随环境(不处理)
| PROPAGATION_NOT_SUPPORTED:非事务方式(仅挂起当前事务)
| PROPAGATION_NEVER :排除事务(异常)
| PROPAGATION_MANDATORY :强制要求事务(不处理)
===============================================================*/
if
(
this
.isExistingTransaction(defStatus) ==
true
) {
/*RROPAGATION_REQUIRES_NEW:独立事务*/
if
(behavior == RROPAGATION_REQUIRES_NEW) {
this
.suspend(defStatus);
/*挂起当前事务*/
this
.doBegin(defStatus);
/*开启新事务*/
}
/*PROPAGATION_NESTED:嵌套事务*/
if
(behavior == PROPAGATION_NESTED) {
defStatus.markHeldSavepoint();
/*设置保存点*/
}
/*PROPAGATION_NOT_SUPPORTED:非事务方式*/
if
(behavior == PROPAGATION_NOT_SUPPORTED) {
this
.suspend(defStatus);
/*挂起事务*/
}
/*PROPAGATION_NEVER:排除事务*/
if
(behavior == PROPAGATION_NEVER) {
throw
new
IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'"
);
}
return
pushStack(defStatus);
/*入栈*/
}
/*-------------------------------------------------------------
| 环境不经存在事务
|
| PROPAGATION_REQUIRED :加入已有事务(开启新事务)
| RROPAGATION_REQUIRES_NEW :独立事务(开启新事务)
| PROPAGATION_NESTED :嵌套事务(开启新事务)
| PROPAGATION_SUPPORTS :跟随环境(不处理)
| PROPAGATION_NOT_SUPPORTED:非事务方式(不处理)
| PROPAGATION_NEVER :排除事务(不处理)
| PROPAGATION_MANDATORY :强制要求事务(异常)
===============================================================*/
/*PROPAGATION_REQUIRED:加入已有事务*/
if
(behavior == PROPAGATION_REQUIRED ||
/*RROPAGATION_REQUIRES_NEW:独立事务*/
behavior == RROPAGATION_REQUIRES_NEW ||
/*PROPAGATION_NESTED:嵌套事务*/
behavior == PROPAGATION_NESTED) {
this
.doBegin(defStatus);
/*开启新事务*/
}
/*PROPAGATION_MANDATORY:强制要求事务*/
if
(behavior == PROPAGATION_MANDATORY) {
throw
new
IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'"
);
}
//
return
pushStack(defStatus);
/*入栈*/
}
|
有兴趣的同学可以逐行分析,该方法的实现原理是依照《整合七种传播行为》一文。
下面这段代码展示了如何封装数据库连接,包括了如何处理引用计数:
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
|
public
class
ConnectionHolder
implements
SavepointManager {
private
int
referenceCount;
private
DataSource dataSource;
private
Connection connection;
public
ConnectionHolder(DataSource dataSource) {
this
.dataSource = dataSource;
}
/**增加引用计数,一个因为持有人已被请求。*/
public
synchronized
void
requested() {
this
.referenceCount++;
}
/**减少引用计数,一个因为持有人已被释放。*/
public
synchronized
void
released() {
this
.referenceCount--;
if
(!isOpen() &&
this
.connection !=
null
) {
try
{
this
.savepointCounter =
0
;
this
.savepointsSupported =
null
;
this
.connection.close();
}
catch
(SQLException e) {
throw
new
DataAccessException(
"cant not close connection."
, e);
}
finally
{
this
.connection =
null
;
}
}
}
public
boolean
isOpen() {
if
(referenceCount ==
0
)
return
false
;
return
true
;
}
/**获取连接*/
public
synchronized
Connection getConnection()
throws
SQLException {
if
(
this
.isOpen() ==
false
)
return
null
;
if
(
this
.connection ==
null
) {
this
.connection =
this
.dataSource.getConnection();
}
return
this
.connection;
}
/**是否存在事务*/
public
boolean
hasTransaction()
throws
SQLException {
Connection conn = getConnection();
if
(conn ==
null
)
return
false
;
//AutoCommit被标记为 false 表示开启了事务。
return
conn.getAutoCommit() ==
false
?
true
:
false
;
}
}
|
接下来这段代码展示了如何递交事务,回滚事务的处理方式亦是如此:
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
|
public
final
void
commit(TransactionStatus status)
throws
SQLException {
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
/*已完毕,不需要处理*/
if
(defStatus.isCompleted())
throw
new
IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction"
);
/*-------------------------------------------------------------
| 1.无论何种传播形式,递交事务操作都会将 isCompleted 属性置为 true。
| 2.如果事务状态中包含一个未处理的保存点。仅递交保存点,而非递交整个事务。
| 3.事务 isNew 只有为 true 时才真正触发递交事务操作。
===============================================================*/
try
{
prepareCommit(defStatus);
if
(defStatus.isReadOnly() || defStatus.isRollbackOnly()) {
/*回滚情况*/
if
(Hasor.isDebugLogger())
Hasor.logDebug(
"Transactional code has requested rollback"
);
doRollback(defStatus);
}
else
{
/*如果包含保存点,在递交事务时只处理保存点*/
if
(defStatus.hasSavepoint())
defStatus.releaseHeldSavepoint();
else
if
(defStatus.isNewConnection())
doCommit(defStatus);
}
//
}
catch
(SQLException ex) {
doRollback(defStatus);
/*递交失败,回滚*/
throw
ex;
}
finally
{
cleanupAfterCompletion(defStatus);
}
}
|