1.事务管理
一个数据库事务是一个被视为单一的工作单元的操作序列。这些操作应该要么完整地执行,要么完全不执行。
事务的概念可以描述为具有以下四个关键属性(ACID,是指数据库管理系统(DBMS)在写入或更新資料的过程中,为保证事务(transaction)是正确可靠的,所必須具备的四个特性):
-
原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。
-
一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。
-
隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。
-
持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。
一个真正的 RDBMS 数据库(关系型数据库)系统将为每个事务保证所有的四个属性。使用 SQL 发布到数据库中的事务的简单视图如下:
-
使用 begin transaction 命令开始事务。
-
使用 SQL 查询语句执行各种删除、更新或插入操作。
-
如果所有的操作都成功,则执行提交操作,否则回滚所有操作。
局部事物 vs. 全局事务
局部事务是特定于一个单一的事务资源,如一个 JDBC 连接,而全局事务可以跨多个事务资源事务,如在一个分布式系统中的事务。
局部事务管理在一个集中的计算环境中是有用的,该计算环境中应用程序组件和资源位于一个单位点,而事务管理只涉及到一个运行在一个单一机器中的本地数据管理器。局部事务更容易实现。
全局事务管理需要在分布式计算环境中,所有的资源都分布在多个系统中。在这种情况下事务管理需要同时在局部和全局范围内进行。分布式或全局事务跨多个系统执行,它的执行需要全局事务管理系统和所有相关系统的局部数据管理人员之间的协调。
编程式 vs. 声明式
Spring 支持两种类型的事务管理:
- 编程式事务管理 :这意味着你在编程的帮助下有管理事务。这给了你极大的灵活性,但却很难维护。
- 声明式事务管理 :这意味着你从业务代码中分离事务管理。你仅仅使用注释或 XML 配置来管理事务。
声明式事务管理比编程式事务管理更可取,尽管它不如编程式事务管理灵活,但它允许你通过代码控制事务。但作为一种横切关注点,声明式事务管理可以使用 AOP 方法进行模块化。Spring 支持使用 Spring AOP 框架的声明式事务管理。
2.Spring 事务管理 API 分析
Spring 框架中,涉及到事务管理的 API 大约有100个左右,其中最重要的有三个:
PlatformTransactionManager
事务管理器
TransactionDefinition
事务定义,事务的一些基础信息,如超时时间、隔离级别、传播属性等
TransactionStatus
事务状态,事务的一些状态信息,如是否是一个新的事务、是否已被标记为回滚
所谓事务管理,其实就是“按照给定的事务规则来执行提交或者回滚操作”。
“给定的事务规则”就是用 TransactionDefinition 表示的,
“按照……来执行提交或者回滚操作”便是用 PlatformTransactionManager 来表示,
TransactionStatus 用于表示一个运行着的事务的状态。
打一个不恰当的比喻,TransactionDefinition 与 TransactionStatus 的关系就像程序和进程的关系。
PlatformTransactionManager
PlatformTransactionManager 用于执行具体的事务操作。接口定义如下所示:
public interface PlatformTransactionManager
{
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status)throws TransactionException;
void rollback(TransactionStatus status)throws TransactionException;
}
方法 | 描述 |
---|---|
TransactionStatus getTransaction(TransactionDefinition definition) | 根据指定的事务定义TransactionDefinition,该方法返回当前活动事务或创建一个新的事务。 |
void commit(TransactionStatus status) | 该方法提交给定的事务和关于它的状态。 |
void rollback(TransactionStatus status) | 该方法执行一个给定事务的回滚。 |
根据底层所使用的不同的持久化 API 或框架,PlatformTransactionManager 的主要实现类大致如下:
- DataSourceTransactionManager:适用于使用JDBC和iBatis进行数据持久化操作的情况。
- HibernateTransactionManager:适用于使用Hibernate进行数据持久化操作的情况。
- JpaTransactionManager:适用于使用JPA进行数据持久化操作的情况。
另外还有JtaTransactionManager 、JdoTransactionManager、JmsTransactionManager等等。
如果我们使用JTA进行事务管理,我们可以通过 JNDI 和 Spring 的 JtaTransactionManager 来获取一个容器管理的 DataSource。JtaTransactionManager 不需要知道 DataSource 和其他特定的资源,因为它将使用容器提供的全局事务管理。而对于其他事务管理器,比如DataSourceTransactionManager,在定义时需要提供底层的数据源作为其属性,也就是 DataSource。与 HibernateTransactionManager 对应的是 SessionFactory,与 JpaTransactionManager 对应的是 EntityManagerFactory 等等。
TransactionDefinition
它用于定义一个事务。它包含了事务的静态属性,比如:事务传播行为、超时时间等等。Spring 为我们提供了一个默认的实现类:DefaultTransactionDefinition,该类适用于大多数情况。如果该类不能满足需求,可以通过实现 TransactionDefinition 接口来实现自己的事务定义。接口定义如下:
public interface TransactionDefinition
{
int getPropagationBehavior();
int getIsolationLevel();
String getName();
int getTimeout();
boolean isReadOnly();
}
方法 | 描述 |
---|---|
int getPropagationBehavior() | 该方法返回传播行为。Spring 提供了与 EJB CMT 类似的所有的事务传播选项。 |
int getIsolationLevel() | 该方法返回该事务独立于其他事务的工作的程度。 |
String getName() | 该方法返回该事务的名称。 |
int getTimeout() | 该方法返回以秒为单位的时间间隔,事务必须在该时间间隔内完成。 |
boolean isReadOnly() | 该方法返回该事务是否是只读的。 |
事务隔离级别
隔离 | 描述 |
---|---|
TransactionDefinition.ISOLATION_DEFAULT | 这是默认的隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。 |
TransactionDefinition.ISOLATION_READ_COMMITTED | 表明能够阻止误读;可以发生不可重复读和虚读。该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。 |
TransactionDefinition.ISOLATION_READ_UNCOMMITTED | 表明可以发生误读、不可重复读和虚读。该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。 |
TransactionDefinition.ISOLATION_REPEATABLE_READ | 表明能够阻止误读和不可重复读;可以发生虚读。该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。 |
TransactionDefinition.ISOLATION_SERIALIZABLE | 表明能够阻止误读、不可重复读和虚读。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。 |
事务传播类型
传播 | 描述 |
---|---|
TransactionDefinition.PROPAGATION_MANDATORY | 使用当前事务;如果不存在当前事务,则抛出一个异常。 |
TransactionDefinition.PROPAGATION_NESTED | 如果存在当前事务,则在一个嵌套的事务中执行。 |
TransactionDefinition.PROPAGATION_NEVER | 不使用当前事务;如果存在当前事务,则抛出一个异常。 |
TransactionDefinition.PROPAGATION_NOT_SUPPORTED | 不使用当前事务;而总是执行非事务性。 |
TransactionDefinition.PROPAGATION_REQUIRED | 使用当前事务;如果不存在事务,则创建一个新的事务。 |
TransactionDefinition.PROPAGATION_REQUIRES_NEW | 创建一个新事务,如果存在一个事务,则把当前事务挂起。 |
TransactionDefinition.PROPAGATION_SUPPORTS | 使用当前事务;如果不存在,则执行非事务性。 |
TransactionDefinition.TIMEOUT_DEFAULT | 使用默认超时的底层事务系统,或者如果不支持超时则没有。 |
举例
public class DefaultTransactionDefinition implements TransactionDefinition, Serializable
{
private int propagationBehavior = PROPAGATION_REQUIRED;
private int isolationLevel = ISOLATION_DEFAULT;
private int timeout = TIMEOUT_DEFAULT;
private boolean readOnly = false;
//略
}
- 事务的传播属性为PROPAGATION_REQUIRED,即当前没有事务的时候,创建一个,如果有则使用当前事务
- 事务的隔离级别采用底层数据库默认的隔离级别
- 超时时间采用底层数据库默认的超时时间
- 是否只读为false
TransactionStatus
PlatformTransactionManager.getTransaction(…) 方法返回一个 TransactionStatus 对象。返回的TransactionStatus 对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事务)。TransactionStatus 接口提供了一个简单的控制事务执行和查询事务状态的方法。该接口定义如下:
public interface TransactionStatus extends SavepointManager
{
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
boolean isCompleted();
}
方法 | 描述 |
---|---|
boolean hasSavepoint() | 该方法返回该事务内部是否有一个保存点,也就是说,基于一个保存点已经创建了嵌套事务。 |
boolean isCompleted() | 该方法返回该事务是否完成,也就是说,它是否已经提交或回滚。 |
boolean isNewTransaction() | 在当前事务时新的情况下,该方法返回 true。 |
boolean isRollbackOnly() | 该方法返回该事务是否已标记为 rollback-only。 |
void setRollbackOnly() | 该方法设置该事务为 rollback-only 标记。 |
TransactionStatus它继承了SavepointManager接口,SavepointManager是对事务中上述保存点功能的封装,如下:
public interface SavepointManager
{
//创建一个保存点
Object createSavepoint() throws TransactionException;
//回滚到某个保存点
void rollbackToSavepoint(Object savepoint) throws TransactionException;
//释放某个保存点
void releaseSavepoint(Object savepoint) throws TransactionException;
}
Spring利用保存点功能实现了事务的嵌套功能。后面会详细说明。