Spring-事务模型及实现资源同步
1、事务管理概述
事务是恢复和并发控制的基本单位。事务具有四个属性(ACID):原子性、一致性、隔离性、持久性。
- 原子性(Atomicity):一个事务是一个不可分割的工作单位,事务中包括的所有操作要么全部成功,要么全部失败。
- 一致性(Consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。
- 隔离性(Isolation):一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务间互不干扰。
- 持久性(Durability):一个事务一旦提交,它对数据库中数据的改变就应该是永久性的,后续的其他操作或故障不应对其有任何影响。
spring框架支持全面的事务管理,它为事务管理提供了一致的抽象,优势如下: - 跨越不同事务API的一致编程模型,如Java事务API(JTA)、JDBC、hibernate、Java持久性API(JPA)。
- 支持声明式事务管理。
- 用于编程式事务管理的简单API比负责事务API要简单。
- 与spring的数据访问抽象有极佳整合能力。
2、事务模型
1)全局事务
全局事务使用户可以使用多个事务资源,通常是关系数据库和消息队列。应用程序服务器通过JTA管理全局事务,JTA的UserTransaction通常需要来自JNDI,即需要使用JNDI才能使用JTA。
全局事务将限制应用程序代码的重用,因为JTA通常只在应用程序服务器环境中可用。
2)本地事务
本地事务是特定于资源的,如与JDBC连接关联的事务。本地事务更易使用,缺点是不能在多个事务资源上工作。如使用JDBC连接管理事务的代码无法在全局JTA事务中运行。由于应用程序服务器不参与事务管理,无法保证跨多个资源的正确性。
另外一个缺点是本地事务对编程模型是侵入式的。
3)spring框架的一致性编程模型
spring解决了全局事务和本地事务的缺点,它使开发人员可以在任何环境中使用一致的编程模型。
spring框架提供了声明式和编程式事务管理。通过编程式事务管理,开发人员可以使用spring框架事务抽象,它可以在任何事务基础设施上运行。使用声明式模型,通常会写很少与事务管理相关的代码,不依赖于spring框架事务API或任何其他事务API。
spring事务抽象的核心是事务策略,事务策略由org.springframework.transaction.PlatformTransactionManager接口定义。
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException;
void commit(TransactionStatus var1) throws TransactionException;
void rollback(TransactionStatus var1) throws TransactionException;
}
PlatformTransactionManager是一个接口,可以根据需要轻松进行Morck或Stub,它不受JNDI等查找策略的舒服。
PlatformTransactionManager接口是任何方法都可以抛出未检查的TransactionException,开发人员可以自行选择捕获和处理TransactionException。
getTransaction(TransactionDefinition var1)方法根据TransactionDefinition参数返回一个TransactionStatus对象。返回的TransactionStatus对象可能代表一个新的事务,或是一个已存在的事务。
TransactionDefinition接口定义:
- 隔离(Isolation):代表了事务与其他事务的分离程度,如该事务可以看到来自其他事务的未提交的写入数据等。
- 传播(Propagation):通常在事务范围内执行的所有代码都将在该事务中运行,如果在事务上下文已经存在得到情况下执行事务方法,则可以选择指定行为。如代码可以在现有的事务中继续运行,或现有事务可以被暂停并创建新的事务。
- 超时(Timeout):定义了事务超时前该事务能运行多久,并由事务基础设施自动回滚。
- 制度状态(Read-only status):当代码读取但不修改数据时,可以使用只读事务。在某些情况下,只读事务可以是一个有用的优化,如使用hibernate时。
public interface TransactionDefinition {
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;
int getPropagationBehavior();
int getIsolationLevel();
int getTimeout();
boolean isReadOnly();
@Nullable
String getName();
}
TransactionStatus接口为事务代码提供了一种简单的方法来控制事务执行和查询事务状态。
public interface TransactionStatus extends SavepointManager, Flushable {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}
示例:定义本地PlatformTransactionManager实现。
定义JDBC数据源:
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
如果使用JTA,通过JNDI获得的容器DataSource与spring的JtaTransactionManager结合使用,如下是使用JTA和JNDI查找的示例:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
<!-- 省略其他bean定义 -->
</beans>
JtaTransactionManager不需要了解DataSource或任何其他特定资源,它使用容器的全局事务管理基础设施。dataSource的定义使用jee命名空间中的<jee:jndi-lookup/>标签。
3、通过事务实现资源同步
- 高级别的同步方法
高级别的同步方法是首选,通常是使用spring基于模板的持久性集成API,或原生的ORM API来管理本地的资源工厂。这些事务感知型解决方案在内部处理资源创建和重用、清理、映射等,用户无需关注这些细节。用户可以专注于非模板话的持久性逻辑。通常可以使用原生的ORM API或使用JdbcTemplate模板方法来进行JDBC访问。 - 低级别的同步方法
低级别的同步方法包括DataSourceUtils(用于JDBC)、EntityManagerFactoryUtils(用于JPA)、SessionFactoryUtils(用于hibernate)等。当用户希望应用程序代码直接处理原生持久性API的资源类型时,可以使用这些类来确保获得正确的spring框架管理的实例和事务同步等。
在JDBC的情况下,使用spring的org.springframework.jdbc.datasource.DataSourceUtils示例:
Connection conn = DataSourceUtils.getConnection(dataSource);
如果现有的事务已经有一个同步到它的连接,则返回该实例。否则方法调用会触发创建一个新的连接,该连接与任何现有事务同步,并可以用于同一事务中的后续重用。
一旦使用了spring的JDBC、JPA或hibernate支持,通常不会使用DataSourceUtils或其他帮助类,因为通过spring抽象,比直接使用相关的API更简便。
- TransactionAwareDataSourceProxy
TransactionAwareDataSourceProxy类是最低级别的,一般情况下,无需使用这个类,而是使用上述更高级别的抽象来编写新的代码。它是目标dataSource的代理,封装了目标DataSource以增加对spring管理的事务的感知。
--参考文献《Srping5开发大全》