Java事务那点儿事儿

事务的定义

事务是指访问并可能更新数据库中各种数据项的一个程序操作单元。

作为一个java web开发者,最早我们入门的时候应该都接触过jdbc事务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Connection connection = null;
PreparedStatement preparedStatement = null;
Class.forName("com.mysql.jdbc.Driver");
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost/test");
connection.setAutoCommit(false);
preparedStatement = connection.prepareStatement("INSERT UserInfo (username,age) VALUES (?,?)");
preparedStatement.setString(1,"Tom");
preparedStatement.setInt(2, 23);
preparedStatement.execute();
connection.commit();
} catch (Exception e) {
e.printStackTrace();
connection.rollback();
}finally {
if (preparedStatement !=null) preparedStatement.close();
if (connection != null) connection.close();
}

java大致有三种事务模型,大致分为本地事务模型、编程式事务模型、声明式事务模型。

本地事务模型(Local Transaction Model)

本地事务模型,是通过本地资源器(Local Resource Manager)来管理事务,而不依赖于编程框架。资源管理器是用于通信的实际的数据提供者,比如我们常用的jdbc访问数据库:其资源管理器是由数据库驱动和数据库管理系统共同来实现的。对于JMS来说,资源管理器是建立topic和queue的连接工厂。所以使用的本地事务管理模型,开发人员管理是连接而不是事务。实际的事务是由JMS和数据库管理系统的提供者来管理。

编程式事务模型(Programmatic Transaction Model)

相对于本地事务模型,编程式事务模型利用JTA(Java Transaction API)以及底层的事务服务实现来提供事务的支持,更加灵活,突破种种限制。此时开发人员编程的对象是事务而不是连接了,通过javax.transaction.UserTransaction接口,调用begin()方法开始一个事务,通过commit()rollback()方法终止这个事务。使用编程式事务模型固然很灵活,但一旦业务逻辑变得复杂管理起来便如噩梦一般,而且容易出错。但有些场景还是很有用的。

1、因为事务是非常消耗资源的,为了优化性能,有时候我们希望将一些无关的耗时较长的数据加载、校验等逻辑从事务范围内剥离出来,从而降低事务执行带来的资源消耗。
2、当一个业务可能需要多次远程调用才能完成,按道理应该由客户端来开启事务,此时使用jta时,就需要使用UserTransaction接口和编程式事务,远程调用的方法使用声明式事务。

声明式事务模型(Declarative Transaction Model)

声明式事务模型,使用编程框架或容器帮助完成了事务的开启、提交、回滚,开发者只需要告诉何时出现何种异常是否需要回滚即可。

事务的ACID特性

ACID即,原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
原子性,操作的一组数据集合要么全部成功要么全部失败;一致性,保证数据库数据时刻避免数据被置于不一致的状态;隔离性,各个独立事务之间交互程度,对于同一数据访问的未提交事务直接相互不受影响;持久性,事务提交后保证修改的数据能够永久保存。

JTA与JTS

随着项目规模的逐渐壮大,像JDBC这样的本地事务很快暴露出他无法跨数据库进行事务管理的弊端,面对越来越多的分布式场景,此时JTA事务就应运而生。
JTA(Java Transaction API)java事务API,是开发者针对事务进行编程管理的API,允许完成跨多个XA资源的分布式事务;JTS(Java Transaction Service)被开源或商业应用服务采用,实现了JTA的底层事务服务。JTA和JTS之间的关系跟JDBC与对应的底层数据库驱动类似。我们在使用编程式事务时,我们仅仅需要使用的唯一接口为javax.transaction.UserTransaction:

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
102
103
104
105
106
107
108
109
/**
* The UserTransaction interface defines the methods that allow an
* application to explicitly manage transaction boundaries.
*/
public interface UserTransaction {

/**
* Create a new transaction and associate it with the current thread.
*
* @exception NotSupportedException Thrown if the thread is already
* associated with a transaction and the Transaction Manager
* implementation does not support nested transactions.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
void begin() throws NotSupportedException, SystemException;

/**
* Complete the transaction associated with the current thread. When this
* method completes, the thread is no longer associated with a transaction.
*
* @exception RollbackException Thrown to indicate that
* the transaction has been rolled back rather than committed.
*
* @exception HeuristicMixedException Thrown to indicate that a heuristic
* decision was made and that some relevant updates have been committed
* while others have been rolled back.
*
* @exception HeuristicRollbackException Thrown to indicate that a
* heuristic decision was made and that all relevant updates have been
* rolled back.
*
* @exception SecurityException Thrown to indicate that the thread is
* not allowed to commit the transaction.
*
* @exception IllegalStateException Thrown if the current thread is
* not associated with a transaction.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*/
void commit() throws RollbackException,
HeuristicMixedException, HeuristicRollbackException, SecurityException,
IllegalStateException, SystemException;

/**
* Roll back the transaction associated with the current thread. When this
* method completes, the thread is no longer associated with a transaction.
*
* @exception SecurityException Thrown to indicate that the thread is
* not allowed to roll back the transaction.
*
* @exception IllegalStateException Thrown if the current thread is
* not associated with a transaction.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
void rollback() throws IllegalStateException, SecurityException,
SystemException;

/**
* Modify the transaction associated with the current thread such that
* the only possible outcome of the transaction is to roll back the
* transaction.
*
* @exception IllegalStateException Thrown if the current thread is
* not associated with a transaction.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
void setRollbackOnly() throws IllegalStateException, SystemException;

/**
* Obtain the status of the transaction associated with the current thread.
*
* @return The transaction status. If no transaction is associated with
* the current thread, this method returns the Status.NoTransaction
* value.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
int getStatus() throws SystemException;

/**
* Modify the timeout value that is associated with transactions started
* by the current thread with the begin method.
*
* <p> If an application has not called this method, the transaction
* service uses some default value for the transaction timeout.
*
* @param seconds The value of the timeout in seconds. If the value is zero,
* the transaction service restores the default value. If the value
* is negative a SystemException is thrown.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
void setTransactionTimeout(int seconds) throws SystemException;
}

我们如果暂时挂起或者恢复一个事务,此时可以使用TransactionManager:

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/**
* The TransactionManager interface defines the methods that allow an
* application server to manage transaction boundaries.
*/
public interface TransactionManager {

/**
* Create a new transaction and associate it with the current thread.
*
* @exception NotSupportedException Thrown if the thread is already
* associated with a transaction and the Transaction Manager
* implementation does not support nested transactions.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
public void begin() throws NotSupportedException, SystemException;

/**
* Complete the transaction associated with the current thread. When this
* method completes, the thread is no longer associated with a transaction.
*
* @exception RollbackException Thrown to indicate that
* the transaction has been rolled back rather than committed.
*
* @exception HeuristicMixedException Thrown to indicate that a heuristic
* decision was made and that some relevant updates have been committed
* while others have been rolled back.
*
* @exception HeuristicRollbackException Thrown to indicate that a
* heuristic decision was made and that all relevant updates have been
* rolled back.
*
* @exception SecurityException Thrown to indicate that the thread is
* not allowed to commit the transaction.
*
* @exception IllegalStateException Thrown if the current thread is
* not associated with a transaction.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
public void commit() throws RollbackException,
HeuristicMixedException, HeuristicRollbackException, SecurityException,
IllegalStateException, SystemException;

/**
* Obtain the status of the transaction associated with the current thread.
*
* @return The transaction status. If no transaction is associated with
* the current thread, this method returns the Status.NoTransaction
* value.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
public int getStatus() throws SystemException;

/**
* Get the transaction object that represents the transaction
* context of the calling thread.
*
* @return the <code>Transaction</code> object representing the
* transaction associated with the calling thread.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
public Transaction getTransaction() throws SystemException;

/**
* Resume the transaction context association of the calling thread
* with the transaction represented by the supplied Transaction object.
* When this method returns, the calling thread is associated with the
* transaction context specified.
*
* @param tobj The <code>Transaction</code> object that represents the
* transaction to be resumed.
*
* @exception InvalidTransactionException Thrown if the parameter
* transaction object contains an invalid transaction.
*
* @exception IllegalStateException Thrown if the thread is already
* associated with another transaction.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*/
public void resume(Transaction tobj)
throws InvalidTransactionException, IllegalStateException,
SystemException;

/**
* Roll back the transaction associated with the current thread. When this
* method completes, the thread is no longer associated with a
* transaction.
*
* @exception SecurityException Thrown to indicate that the thread is
* not allowed to roll back the transaction.
*
* @exception IllegalStateException Thrown if the current thread is
* not associated with a transaction.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
public void rollback() throws IllegalStateException, SecurityException,
SystemException;

/**
* Modify the transaction associated with the current thread such that
* the only possible outcome of the transaction is to roll back the
* transaction.
*
* @exception IllegalStateException Thrown if the current thread is
* not associated with a transaction.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
public void setRollbackOnly() throws IllegalStateException, SystemException;

/**
* Modify the timeout value that is associated with transactions started
* by the current thread with the begin method.
*
* <p> If an application has not called this method, the transaction
* service uses some default value for the transaction timeout.
*
* @param seconds The value of the timeout in seconds. If the value is zero,
* the transaction service restores the default value. If the value
* is negative a SystemException is thrown.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
public void setTransactionTimeout(int seconds) throws SystemException;

/**
* Suspend the transaction currently associated with the calling
* thread and return a Transaction object that represents the
* transaction context being suspended. If the calling thread is
* not associated with a transaction, the method returns a null
* object reference. When this method returns, the calling thread
* is not associated with a transaction.
*
* @return Transaction object representing the suspended transaction.
*
* @exception SystemException Thrown if the transaction manager
* encounters an unexpected error condition.
*
*/
public Transaction suspend() throws SystemException;
}

并不是使用UserTransaction或者TransactionManager就将JDBC这样的本地事务转换成JTA事务,JTA事务要求Connection、DataSource、Resource必须符合XA规范,并实现了XA规范的接口才能参与到JTA事务当中(目前主流的数据库都支持XA规范);也即要想使用JTA事务,需要有一个实现了javax.sql.XAConnectionjavax.sql.XADataSourcejavax.transaction.xa.XAResource的JDBC驱动程序,XADataSource相当于XAConnection的对象工厂,使用JTA事务,必须使用XADataSource获取一个XAConnection对象作为XA连接。XA连接(javax.sql.XAConnection)与非XA连接(javax.sql.Connection)区别在于,XA连接可以参与JTA事务并不支持自动提交。

JTA事务优点很明显支持了分布式事务,缺点是实现复杂,JTA UserTransaction通常需要利用JDNI来获取,即需要同时使用JTA和JNDI 虽然JTA是java提供的一套支持分布式事务的API,但是不同的J2EE平台的实现也不一样所以使用起来也不方便。 因为JTA事务的复杂性,目前业界只要的分布式事务解决方案主要有异步消息确保型、TCC、最大努力通知等。

标准的分布式事务

一个标准的分布式事务包括一个事务管理器和一个或多个资源管理器(资源管理器是任意的持久化数据存储),事务管理器承担着参与事务的成员之间的通信职责。

JTA 深度历险-原理与实现 https://www.ibm.com/developerworks/cn/java/j-lo-jta/

坚持原创技术分享,您的支持将鼓励我继续创作!