一、什么是Java事务 通常的观念认为,事务仅与数据库相关。2 d$ T5 |: B t8 l8 y9 W
事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)的缩写。事务的原子性表示事务执行过程中的任何失败都将导致事务所做的任何修改失效。一致性表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态。隔离性表示在事务执行过程中对数据的修改,在事务提交之前对其他事务不可见。持久性表示已提交的数据在事务执行失败时,数据的状态都应该正确。. r" a0 F; `1 V5 ^6 b$ E: H8 ~1 H* f
通俗的理解,事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令。更简答的说就是:要么全部执行成功,要么撤销不执行。' w) z( U6 r% @% |
既然事务的概念从数据库而来,那Java事务是什么?之间有什么联系?' a8 _. [0 ^# y' e0 s
实际上,一个Java应用系统,如果要操作数据库,则通过JDBC来实现的。增加、修改、删除都是通过相应方法间接来实现的,事务的控制也相应转移到Java程序代码中。因此,数据库操作的事务习惯上就称为Java事务。1 M5 u4 o J$ I1 M Z
二、为什么需要事务4 |: ?( d1 J5 ]+ I, g g% D8 F# Y
事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问。具一个简单例子:比如银行转帐业务,账户A要将自己账户上的1000元转到B账户下面,A账户余额首先要减去1000元,然后B账户要增加1000 元。假如在中间网络出现了问题,A账户减去1000元已经结束,B因为网络中断而操作失败,那么整个业务失败,必须做出控制,要求A账户转帐业务撤销。这才能保证业务的正确性,完成这个操走就需要事务,将A账户资金减少和B账户资金增加方到一个事务里面,要么全部执行成功,要么操作全部撤销,这样就保持了数据的安全性。
8 ~+ m5 P* m/ U& d5 a) B, t5 Q# x 三、Java事务的类型
U% h4 u# F5 U7 k7 @! T Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。# c( n$ ^9 d& Z" m8 a
1、JDBC事务
; {( O# n' q+ |+ Y6 S' s* c JDBC 事务是用 Connection 对象控制的。JDBC Connection 接口( java.sql.Connection )提供了两种事务模式:自动提交和手工提交。 java.sql.Connection 提供了以下控制事务的方法:
5 r, t' y( b% I( p public void setAutoCommit(boolean). G' ]* b: r3 K( [
public boolean getAutoCommit()
: D! c% j$ E0 {& I2 ]/ K public void commit()) x$ k4 v, t: C' v/ j
public void rollback()
8 p2 ?% Y! Z0 T4 u! K4 n, X* H9 p 使用 JDBC 事务界定时,您可以将多个 SQL 语句结合到一个事务中。JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。# D. X: q1 W3 V% a+ v3 g
2、JTA(Java Transaction API)事务' G% k' b+ n* B2 g) ~
JTA是一种高层的,与实现无关的,与协议无关的API,应用程序和应用服务器可以使用JTA来访问事务。
- U& z2 W p. D# { JTA允许应用程序执行分布式事务处理--在两个或多个网络计算机资源上访问并且更新数据,这些数据可以分布在多个数据库上。JDBC驱动程序的JTA支持极大地增强了数据访问能力。
( n3 u+ X7 L% } 如果计划用 JTA 界定事务,那么就需要有一个实现 javax.sql.XADataSource 、 javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驱动程序。一个实现了这些接口的驱动程序将可以参与 JTA 事务。一个 XADataSource 对象就是一个 XAConnection 对象的工厂。 XAConnection s 是参与 JTA 事务的 JDBC 连接。 _% S) v2 Q2 D9 ~
您将需要用应用服务器的管理工具设置 XADataSource 。从应用服务器和 JDBC 驱动程序的文档中可以了解到相关的指导。8 f# Z" c( G3 L( b
J2EE 应用程序用 JNDI 查询数据源。一旦应用程序找到了数据源对象,它就调用 javax.sql.DataSource.getConnection() 以获得到数据库的连接。
6 f2 |8 k( }1 O2 S# }! ]0 B XA 连接与非 XA 连接不同。一定要记住 XA 连接参与了 JTA 事务。这意味着 XA 连接不支持 JDBC 的自动提交功能。同时,应用程序一定不要对 XA 连接调用 java.sql.Connection.commit() 或者 java.sql.Connection.rollback() 。相反,应用程序应该使用 UserTransaction.begin()、 UserTransaction.commit() 和 serTransaction.rollback() 。
3 z# m6 q4 l8 [- n/ W 3、容器事务
# r; p( r& X' c( X 容器事务主要是J2EE应用服务器提供的,容器事务大多是基于JTA完成,这是一个基于JNDI的,相当复杂的API实现。相对编码实现JTA事务管理,我们可以通过EJB容器提供的容器事务管理机制(CMT)完成同一个功能,这项功能由J2EE应用服务器提供。这使得我们可以简单的指定将哪个方法加入事务,一旦指定,容器将负责事务管理任务。这是我们土建的解决方式,因为通过这种方式我们可以将事务代码排除在逻辑编码之外,同时将所有困难交给J2EE容器去解决。使用EJB CMT的另外一个好处就是程序员无需关心JTA API的编码,不过,理论上我们必须使用EJB。
* j/ x. m$ c; H 四、三种事务差异
, K8 C4 o9 s0 r* }) J$ ]' D q3 | 1、JDBC事务控制的局限性在一个数据库连接内,但是其使用简单。
4 O' A$ K; q4 ~2 @5 A+ I. K 2、JTA事务的功能强大,事务可以跨越多个数据库或多个DAO,使用也比较复杂。
U# Y" i8 _' }' \0 X5 Y; { 3、容器事务,主要指的是J2EE应用服务器提供的事务管理,局限于EJB应用使用。- a2 J, z( [6 a6 ^
五、总结5 @! ^* M, e6 g4 [
事务控制是构建J2EE应用不可缺少的一部分,合理选择应用何种事务对整个应用系统来说至关重要。一般说来,在单个JDBC 连接连接的情况下可以选择JDBC事务,在跨多个连接或者数据库情况下,需要选择使用JTA事务,如果用到了EJB,则可以考虑使用EJB容器事务。
, Q! F9 M! T* K$ Q</p> byte[] bytes = s.getBytes("Unicode");
3 z2 D# ~% Q9 S int n = 0; // 表示当前的字节数
' t' ?* a2 @4 C4 d0 Z; z8 v int i = 2; // 要截取的字节数,从第3个字节开始1 W0 p4 m# V3 q( ?' \. f @7 n
for (; i < bytes.length && n < length; i++)5 g( G+ M9 P* d- V1 H
{5 t% i4 C& h' T$ @
// 奇数位置,如3、5、7等,为UCS2编码中两个字节的第二个字节
: J! l9 u D" k$ d" s if (i % 2 == 1)9 n+ Z% U1 j# _ U
{
9 X/ N0 M1 ~) M) x1 r7 i, Y- r n++; // 在UCS2第二个字节时n加19 L4 a" g1 R2 m, D: T7 a' R
}
/ U2 L) c; W+ G/ ?) @ else' w6 ]" G/ Z- {+ H( y. [
{* y5 V) V$ ^- ]; c. J8 d( M$ V
// 当UCS2编码的第一个字节不等于0时,该UCS2字符为汉字,一个汉字算两个字节
, ~; _- r, _; x5 u3 | if (bytes != 0)
% [( ]! H# P( Z% ` {
/ W( B8 c0 U8 c6 L% d n++;
5 [% m% C' C3 y5 V9 J$ ^% L }
8 q. j4 r2 z9 |, a7 L C; U' o( {8 M }
0 U/ o9 R, k* W, x }
9 l; t( ]- H Z2 h8 Z // 如果i为奇数时,处理成偶数
6 e) I" K) }) m+ v# O1 q. {+ [ T if (i % 2 == 1)3 X C. Z% m( P, z+ r- o, }
{
) j# S$ {9 ^- C" [0 o // 该UCS2字符是汉字时,去掉这个截一半的汉字
; S+ s+ w3 g ~# G# R if (bytes[i - 1] != 0)& @2 ^# B- L5 ]
i = i - 1;$ E' \+ I$ e* b8 i @9 e
// 该UCS2字符是字母或数字,则保留该字符
& K* X; ^, \, i9 \ else
) o2 L I3 e( Q1 H i = i + 1;
) N( z( {8 @7 O) w }4 L9 C1 z0 t' C- }! k, ]$ s
return new String(bytes, 0, i, "Unicode");
9 E- Q8 Y3 m2 ]8 R! f6 o- F }
# l: Y1 ^; \& c" \% t1 V5 X 下面代码使用了bSubstring方法:
) \4 G9 L3 {- q& d! l; }6 | String s = "a加b等于c,如果a等1、b等于2,那么c等3";$ e- g# o" \/ |1 _5 x; _
System.out.println(bSubstring(s, 6));
4 s0 M7 j0 b7 P' P& O: Q% v% X 上面的代码截取的字符串是"a加b等"。 |