一、事务概念
(1): 事务( Transaction )是并发控制的单位,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。通过事务, SQL Server 能将逻辑相关的一组操作绑定在一起,以便服务器保持数据的完整性。
(2): 事务通常是以 BEGIN TRANSACTION 开始,以 COMMIT 或 ROLLBACK 结束。
COMMIT 表示提交,即提交事务的所有操作。具体地说就是将事务中所有对数据库的更新写回到磁盘上的物理数据库中去,事务正常结束。
ROLLBACK 表示回滚,即在事务运行的过程中发生了某种故障,事务不能继续进行,系统将事务中对数据库的所有以完成的操作全部撤消,滚回到事务开始的状态。
(3): 事务运行的三种模式 :
A: 自动提交事务
每条单独的语句都是一个事务。每个语句后都隐含一个 COMMIT 。
B: 显式事务
以 BEGIN TRANSACTION 显式开始,以 COMMIT 或 ROLLBACK 显式结束。
C: 隐性事务
在前一个事务完成时,新事务隐式启动,但每个事务仍以 COMMIT 或 ROLLBACK 显式结束。
(4): 事务的特性 (ACID 特性 )
A: 原子性 (Atomicity)
事务是数据库的逻辑工作单位,事务中包括的诸操作要么全做,要么全不做。
B: 一致性 (Consistency)
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
C: 隔离性 (Isolation)
一个事务的执行不能被其他事务干扰。
D: 持续性 / 永久性 (Durability)
一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
注 : 事务是恢复和并发控制的基本单位。
二、事务状态
事务有以下几种状态:
Status.STATUS_NO_TRANSACTION | 当前线程中没有事务存在 |
Status.STATUS_MARKED_ROLLBACK | 当前事务已经被设置为setRollback标记 |
Status.STATUS_ACTIVE | 当前事务处于运行中 |
Status.STATUS_COMPLETED | 当前事务已经回滚或提交 |
下面是j2ee规范中定义的事务状态:
public interface javax.transaction.Status { public static final int STATUS_ACTIVE = 0; public static final int STATUS_MARKED_ROLLBACK = 1; public static final int STATUS_PREPARED = 2; public static final int STATUS_COMMITTED = 3; public static final int STATUS_ROLLEDBACK = 4; public static final int STATUS_UNKNOWN = 5; public static final int STATUS_NO_TRANSACTION = 6; public static final int STATUS_PREPARING = 7; public static final int STATUS_COMMITTING = 8; public static final int STATUS_ROLLING_BACK = 9; }
三、事务传播方式
通常在一个事务中执行的所有代码都会在这个事务中运行。但是,如果一个事务上下文已经存在,有几个选项可以指定一个事务性方法的执行行为:例如,简单地在现有的事务中继续运行(大多数情况);或者挂起现有事务,创建一个新的事务。
事务传播方式:
PROPAGATION_REQUIRED | 0 | 外部没有事务,启动一个新的事务;否则加入外部的事务 |
PROPAGATION_SUPPORTS | 1 | 外部没有事务,则运行在非事务上下文中;否则,加入外部事务 |
PROPAGATION_MANDATORY | 2 | 外部没有事务,抛出运行期异常;否则,加入外部事务 |
PROPAGATION_REQUIRES_NEW | 3 | 无论外部是否存在事务,均启动一个新事务;如果存在外部事务,则先挂起外部事务,然后启动新事务 |
PROPAGATION_NOT_SUPPORTED | 4 | 无论外部是否存在事务,都运行在非事务上下文中,如果外部有事务,则挂起外部事务 |
PROPAGATION_NEVER | 5 | 如果外部存在事务,则抛出运行期异常,否则,运行在非事务上下文中 |
四、隔离级别
在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同:
- 未授权读取(Read Uncommited):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过"排他写锁"实现。
- 授权读取(Read Committed):允许不可重复读取,但不允许脏读取。这可以通过"瞬间共享读锁"和"排他写锁"实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
- 可重复读取(Repeatable Read):禁止不可重复读取和脏读取,但是有时可能出现幻影数据。这可以通过"共享读锁"和"排他写锁"实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
- 序列化(Serializable):提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过"行级锁"是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
隔离级别:
默认的隔离级别 | ISOLATION_DEFAULT | -1 |
未授权读 | ISOLATION_READ_UNCOMMITTED | 1 |
授权读取 | ISOLATION_READ_COMMITTED | 2 |
可重复读取 | ISOLATION_REPEATABLE_READ | 4 |
序列化 | ISOLATION_SERIALIZABLE | 8 |
如果在事务开始接口中不指定事务的隔离级别的话,那么就用用户在事务配置中指定的隔离级别。默认情况下,事务配置中的事务隔离级别为数据库默认的隔离级别。
相关数据库的默认的隔离级别如下:
- oracle=2(ISOLATION_READ_COMMITTED)
- db2=2(ISOLATION_READ_COMMITTED)
- SQLServer=2(ISOLATION_READ_COMMITTED)
- informix=0(TRANSACTION_NONE)
五、事务同步
在事务操作过程中,提供了事务同步机制;可以在事务挂起,事务恢复,事务提交之前,事务完成之前,以及事务完成之后做其他用户自定义的扩展。
可以通过事务同步注册接口把事务同步注册进来。
六、本地事务、全局事务(又叫JTA事务或者分布式事务)
- 全局事务 - 资源管理器管理和协调的事务,可以跨越多个数据库和进程。资源管理器一般使用 XA 二阶段提交协议与"企业信息系统"(EIS) 或数据库进行交互。
- 本地事务 - 在单个 EIS 或数据库的本地并且限制在单个进程内的事务。本地事务不涉及多个数据来源。
本地和全局事务都使用 javax.transaction.UserTransaction 接口划分界限,客户端必须使用此接口。本地事务不使用事务管理器,因而处理速度更快。
起初,所有事务都是本地的。如果非 XA 数据源连接是事务范围中登记的第一个资源连接,当"另一个"XA 数据源连接加入此连接时,该非 XA 数据源连接就成为全局事务。如果另一个非 XA 数据源连接试图加入,就会产生异常。
J2EE开发者有两个事务管理的选择: 全局 或 本地 事务。全局事务由应用服务器管理,使用JTA。局部事务是和资源相关的,比如一个和JDBC连接关联的事务。这个选择有深刻的含义。例如,全局事务可以用 于多个事务性的资源(典型例子是关系数据库和消息队列)。使用局部事务,应用服务器不需要参与事务管理,并且不能帮助确保跨越多个资源(需要指出的是多数 应用使用单一事务性的资源)的事务的正确性。
全局事务: 全局事务有一个重大的缺陷,代码需要使用JTA:一个笨重的API(部分是因为它的异常模型)。此外,JTA的UserTransaction通常需要从JNDI获得,这意味着我们为了JTA,需要 同时 使用JNDI 和 JTA。显然全部使用全局事务限制了应用代码的重用性,因为JTA通常只在应用服务器的环境中才能使用。 以前,使用全局事务的首选方式是通过EJB的 CMT (容器管理事务 ):CMT是 声明式事务管理 的一种形式(区别于 编程式事务管理 )。 EJB的CMT不需要任何和事务相关的JNDI查找,虽然使用EJB本身肯定需要使用JNDI。它消除了大多数(不是全部)硬编码的方式去控制事务。重大 的缺陷是CMT绑定在JTA和应用服务器环境上,并且只有我们选择使用EJB实现业务逻辑,或者至少处于一个事务化EJB的外观(Facade)后才能使 用它。EJB有如此多的诟病,尤其是存在其它声明式事务管理时,EJB不是一个吸引人的建议。
本地事务: 本地事务容易使用,但也有明显的缺点:它们不能用于多个事务性资源。例如,使用JDBC连接事务管理的代码不能用于全局的JTA事务中。另一个缺点是局部事务趋向于入侵式的编程模型。
七、javax.transaction.Transaction、 javax.transaction.TransactionManager、 javax.transaction.UserTransaction
Java TM 2 Platform, Enterprise Edition(J2EE)简化了分布式事务管理应用程序的编写。J2EE包括了两套规范,用来支持分布式的事务,一种是Java Transaction API(JTA),另一种是Java Transaction Service(JTS)。JTA是一种高层的,与实现无关的,与协议无关的API,应用程序和应用服务器可以使用JTA来访问事务。JTS则规定了支持 JTA的事务管理器的实现规范,在高层API之下实现了OMG Object Transaction Service(OTS) 1.1规范的Java映射。JTS使用Internet Inter-ORB Protocol(IIOP)来传播事务。作为J2EE平台实现的一部分,SUN实现了一个支持JTS的事务管理器,同时还实现了JTA。
JTA和JTS让J2EE应用服务器完成事务管理,这样就使得组件开发人员摆脱了事务管理的负担。开发者只需在部署描述符中声明事务管理属性,便可以使得EJB组件参与到事务之中,由应用服务器来负责事务的管理。
- JTS规范定义了事务管理器的实现。JTS规范中定义的事务管理器在高层支持JTA接口规范,在底层则实现了OTS1.1(CORBA Object Transaction Service)的标准Java映射。OMG使用IDL(接口定义语言)定义了事务服务语言中性的实现,JTS则对这个IDL的事务服务实现作了标准的 Java映射。JTS使用OTS接口实现了互操作和移植性。OTS接口定义了一组标准的机制,使得JTS事务管理器之间可以使用 IIOP(Internet InterORB协议)来生成并传播事务上下文。
- 从事务管理器的角度出发,事务服务的具体实现是不需要暴露出来的,只需要定义高层接口,使得事务服务的用户可以驱动事务界限、资源获取、 事务同步和事务恢复过程。JTA的目的是定义事务管理器所要求的本地Java接口,从而在企业级分布计算环境中支持事务管理。下图中的小半圆代表JTA规 范。
J2EE事务服务的层次关系
企业级Java中间件的分布式事务服务包括五层:事务管理器(Transaction Manager)、应用服务器(Application Server)、资源管理器(Resource Manager)、应用程序(Application Program)和通信资源管理器(Communication Resource Manager)。每一层都通过实现一组事务API和相关机制参与到分布式事务处理系统中。
- 事务管理器:是一个系统级的组件,是事务服务的访问点。提供了一组服务和相关的管理机制,用于支持事务划分、事务资源管理、事务同步和事务上下文的传播。
- 应用服务器(或者称为TP monitor)提供了支持应用程序运行环境的基础设施,这个运行环境包括了事务状态管理。应用服务器的一个例子是EJB服务器。
- 资源管理器(通过资源适配器[Resource Adapter],资源适配器类似于数据库连接)为应用程序提供了对资源的访问。资源管理器通过实现一组事务资源接口来参与到分布式事务中。事务管理器使 用这组事务资源接口在处理事务联系、事务完成和事务恢复的相关工作。资源管理器的一个例子是关系数据库服务器。
- 基于组件的事务性应用运行在应用服务器环境中,需要依赖应用服务器通过事务属性声明设置所提供的事务管理支持。这种类型应用的典型例子是EJB。除此之外,一些独立的Java客户端程序需要使用应用服务器或事务管理器所提供的高层接口来控制事务界限。
- 通讯资源管理器(CRM)支持事务上下文的传播和事务服务的访问请求。JTA文档中没有规定通信的要求。请参考JTS规范[2]获得有关事务管理器之间互操作的详细信息。
从事务管理器的角度出发,事务服务的具体实现是不需要暴露出来的,只需要定义高层接口,使得事务服务的用户可以驱动事务界限、资源获取、事务同步和 事务恢复过程。JTA的目的是定义事务管理器所要求的本地Java接口,从而在企业级分布计算环境中支持事务管理。下图中的小半圆代表JTA规范。
JTS中规定的事务管理器的实现
本节从事务管理器实现者的角度描述了实现方面的要求。如下图,事务管理器必须实现JTA接口,用于支持应用服务器和资源管理器。不要求实现对 JDBC 1.0 Driver和非JTA资源管理器支持。也不要求实现对各种CORBA应用实体的支持,如事务客户端(Transactional Client)、事务服务器(Transactional Server)和恢复服务器(Recoverable Server)。
Java Transaction API
Java Transaction API由三部分组成:高层的应用事务划分接口(供事务客户使用)、高层的事务管理器接口(供应用服务器使用)和X/Open XA协议的标准Java映射(供事务性资源管理器使用)。
1 UserTransaction接口
Javax.transaction.UserTransaction接口使得应用程序能够编程控制事务边界,开启一个全局事务并且使用调用线程与事务处理关联,提供能够编程地控制事务处理范围的应用程序。这个接口可以由Java客户端程序或者EJB来使用。
1)在EJB Server中的UserTransaction支持
EJB中对事务的管理有两种类型:
Bean自管理事务对于自管理事务的EJB,需要从EJB上下文中获取UserTransaction的接口引用,由自己负责完成事务的开始、提交 或者回滚。javax.transaction.UserTransaction方法开启一个全局事务并且使用调用线程与事务处理关联。
try { javax.transaction.UserTransaction userTran = ctx.getUserTransaction(); userTran.begin(); ... //执行事务性的程序逻辑 userTran.commit(); } catch (Exception e){ userTran.rollBack(); throw new Exception("......" ); }
EJB这样处理事务称为编程型事务。 由容器负责事务管理对于这样的EJB,只需在其部署描述符中指定所需的事务相关属性,便可由EJB容器代替EJB进行事务管理。这称为声明式事务。
2) 事务客户端中的UserTransaction支持 Java客户端程序需要首先通过JNDI来获得UserTransaction对象的引用,然后使用该对象的方法完成事务的开始、提交或者回滚。
java.util.Properties env = ... Context ctx = new InitialContext(env); Javax.transaction.UserTransaction userTran = (javax.transaction.UserTransaction)ctx.lookup("javax.transaction.UserTransaction" ); userTran.begin(); try { ... userTran.commit(); } catch (Exception e){ userTran.rollBack(); throw new Exception("......" ); }
2 TransactionManager接口
应用服务器使用javax.transaction.TransactionManager接口来代表受控的应用程序控制事务的边界,允许应用程序服务器来控制代表正在管理的应用程序的事务范围。例如,EJB容器为事务性EJB组件管理事务状态。
3 Transaction接口
使用Transaction接口可以执行与目标对象相关联的事务操作。
4 XAResource接口
Javax.transaction.xa.XAResource接口是基于X/Open CAE规范(分布式事务处理:XA规范)工业标准XA接口的Java映射。 XAResource接口定义了分布式事务处理环境(DTP)中资源管理器和事务管理器之间是如何交互的。资源管理器的资源适配器实现了 XAResource接口,将事务同事务资源联系起来,类似关系数据库的一个连接。
5 Xid接口
Javax.transaction.xa.Xid接口是X/Open事务标识符XID结构的Java映射。这个接口由事务管理器和资源管理器来使用,对于应用程序和应用服务器而言这个接口是不可见的。
下面是j2ee规范中定义的事务接口:
public interface javax.transaction.Transaction { public int getStatus() throws javax.transaction.SystemException; public void commit() throws javax.transaction.RollbackException, javax.transaction.HeuristicMixedException, javax.transaction.HeuristicRollbackException, java.lang.SecurityException, java.lang.IllegalStateException, javax.transaction.SystemException; public void rollback() throws java.lang.IllegalStateException, javax.transaction.SystemException; public void setRollbackOnly() throws java.lang.IllegalStateException, javax.transaction.SystemException; public void registerSynchronization(javax.transaction.Synchronization arg0) throws javax.transaction.RollbackException, java.lang.IllegalStateException, javax.transaction.SystemException; public boolean enlistResource(javax.transaction.xa.XAResource arg0) throws javax.transaction.RollbackException, java.lang.IllegalStateException, javax.transaction.SystemException; public boolean delistResource(javax.transaction.xa.XAResource arg0, int arg1) throws java.lang.IllegalStateException, javax.transaction.SystemException; }
public interface javax.transaction.TransactionManager { public int getStatus() throws javax.transaction.SystemException; public void begin() throws javax.transaction.NotSupportedException, javax.transaction.SystemException; public void commit() throws javax.transaction.RollbackException, javax.transaction.HeuristicMixedException, javax.transaction.HeuristicRollbackException, java.lang.SecurityException, java.lang.IllegalStateException, javax.transaction.SystemException; public void rollback() throws java.lang.IllegalStateException, java.lang.SecurityException, javax.transaction.SystemException; public void setRollbackOnly() throws java.lang.IllegalStateException, javax.transaction.SystemException; public void setTransactionTimeout(int arg0) throws javax.transaction.SystemException; public javax.transaction.Transaction getTransaction() throws javax.transaction.SystemException; public javax.transaction.Transaction suspend() throws javax.transaction.SystemException; public void resume(javax.transaction.Transaction arg0) throws javax.transaction.InvalidTransactionException, java.lang.IllegalStateException, javax.transaction.SystemException; }
public interface javax.transaction.UserTransaction { public int getStatus() throws javax.transaction.SystemException; public void begin() throws javax.transaction.NotSupportedException, javax.transaction.SystemException; public void commit() throws javax.transaction.RollbackException, javax.transaction.HeuristicMixedException, javax.transaction.HeuristicRollbackException, java.lang.SecurityException, java.lang.IllegalStateException, javax.transaction.SystemException; public void rollback() throws java.lang.IllegalStateException, java.lang.SecurityException, javax.transaction.SystemException; public void setRollbackOnly() throws java.lang.IllegalStateException, javax.transaction.SystemException; public void setTransactionTimeout(int arg0) throws javax.transaction.SystemException; }
public interface javax.transaction.xa.XAResource { public static final int TMENDRSCA N = 8388608; public static final int TMFAIL = 536870912; public static final int TMJOIN = 2097152; public static final int TMNOFLAGS = 0; public static final int TMONEPHASE = 1073741824; public static final int TMRESUME = 134217728; public static final int TMSTARTRSCA N = 16777216; public static final int TMSUCCESS = 67108864; public static final int TMSUSPEND = 33554432; public static final int XA_RDONLY = 3; public static final int XA_OK = 0; public int getTransactionTimeout() throws javax.transaction.xa.XAException; public boolean setTransactionTimeout(int arg0) throws javax.transaction.xa.XAException; public boolean isSameRM(javax.transaction.xa.XAResource arg0) throws javax.transaction.xa.XAException; public javax.transaction.xa.Xid[] recover(int arg0) throws javax.transaction.xa.XAException; public int prepare(javax.transaction.xa.Xid arg0) throws javax.transaction.xa.XAException; public void forget(javax.transaction.xa.Xid arg0) throws javax.transaction.xa.XAException; public void rollback(javax.transaction.xa.Xid arg0) throws javax.transaction.xa.XAException; public void end(javax.transaction.xa.Xid arg0, int arg1) throws javax.transaction.xa.XAException; public void start(javax.transaction.xa.Xid arg0, int arg1) throws javax.transaction.xa.XAException; public void commit(javax.transaction.xa.Xid arg0, boolean arg1) throws javax.transaction.xa.XAException; }
public interface javax.transaction.xa.Xid { public static final int MAXGTRIDSIZE = 64; public static final int MAXBQUALSIZE = 64; public int getFormatId(); public byte [] getBranchQualifier(); public byte [] getGlobalTransactionId(); }
public interface javax.transaction.Synchronization { public void beforeCompletion(); public void afterCompletion(int arg0); }