事务 transaction : 全有或全无的操作
6.1 理解事务
6.1.1 事务的特性
这里可以参照数据库原理中对事务的描述
- 原子性 Atomic:事务是由一个或多个活动组成的一个工作单元,原子性确保事务中的所有操作全部发生或全部不发生
- 一致性 Consistent:一旦事务完成,无论成功或失败,系统必须确保他所建模的业务处于抑制的状态。
- 隔离性 Isolated:事务允许多个用户对相同的数据进行操作,每个用户的操作不会与其他用户纠缠到一起。因此,事务应该被彼此隔离,避免发生同步读写相同数据的事情
- 持久性 Durable:一旦事务完成,事务的结果应该持久化,这样就能从任何的系统崩溃中恢复过来。这一般会设计将结果存储到数据库或其他形式的持久化存储中
这里事务的概念与数据库理论中的事务是一致的。
6.1.2 Spring对事务管理的支持
Spring提供了对编码式和声明式事务管理的支持。
Spring通过回调机制将实际的事务实现从事务性的代码中抽象出来,Spring对事务的支持可以不需要JTA的实现。
JTA,即Java Transaction API,JTA允许应用程序执行分布式事务处理——在两个或多个网络计算机资源上访问并且更新数据 |
如果应用程序只使用一种持久化资源,Spring可以使用持久化机制本身所提供的事务性支持,包括JDBC、Hibernate、JPA。但是如果应用程序的事务跨多个资源,那么Spring会使用第三方的JTA实现来支持分布式(XA)事务。
编码式事务允许用户在代码中精确定义事务的边界,声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。
6.2 选择事务管理器
Spring并不直接管理事务,而是提供了多种事务管理器。他们将事务管理的职责委托给JTA或其他持久化机制所提供的平台相关的事务实现。
Spring的事务管理器:
事务管理器 org.framework.* |
使用场景 |
jca.cci.coonection.CciLocalTransactionManager |
使用Spring对JavaEE连接器架构(Java EE Connector Architecture,JCA)和通用客户端接口(Common Client Interface,CCI)提供支持 |
jdbc.datasource.DataSourceTransactionManager |
用于Spring对JDBC抽象的支持,也可用于使用iBATIS进行持久化的场景 |
jms.connection.JmsTransactionManger |
用于JMS1.1+ |
jms.connection.JmsTransactionManager102 |
用于JMS1.0.2 |
orm.hibernate3.HibernateTransactionManager |
用于Hibernate3进行持久化 |
orm.jdo.JdoTransactionManager |
用于JDO进行持久化 |
orm.jpa.JpaTransactionManager |
用于Java持久化API(Java Persistence API,JPA)进行持久化 |
transaction.jta.JtaTransactionManager |
需要分布式事务或没有其他的事务管理器满足需求 |
transaction.jta.OC4JJtaTransactionManager |
用于Oracle的OC4J JEE容器 |
transaction.jta.WebLogicJtaTransactionManager |
需要使用分布式事务并且应用程序运行于WebLogic中 |
transaction.jta.WebSphereUowTransactionManager |
需要WebSphere中UOWManager所管理的事务 |
要使用事务管理器,需要将其生命在应用程序上下文中。
6.2.1 JDBC事务
如果在应用程序中直接使用JDBC来进行持久化,DataSourceTransactionManager会处理事务边界。要使用DataSourceTransactionManager,需要如下装配:
这里dataSource配置成一个名为dataSource Bean的引用,而dataSource是定义在上下文文件中的javax.sql.DataSource Bean。
DataSourceTransactionManager通过调用java.sql.Connection来管理事务,后者是通过DataSource获取到的。通过调用连接的commit()提交事务,rollback()方法进行回滚。
6.2.2 Hibernate事务
如果使用Hibernate实现应用程序的持久化,那么需要使用HibernateTransactionManager。需如下配置:
sessionFactory属性需要装配一个HibernateSessionFactory。(无论是Spring3.0 还是Spring2.5都不包含对Hibernate2的支持,只有Spring2.0是支持的)
HibernateTransactionManager将事务管理委托给org.hibernate.Transaction对象,后者是从HibernateSession中获得的。事务成功完成时,HibernateTransactionManager将会调用Transaction对象commit()方法。如果事务失败,Transaction对象的rollback()方法将会被调用
6.2.3JPA事务
使用JPA 需要用Spring的JpaTransactionManager来处理事务,需要如下配置:
JpaTransactionManager只需要装配一个JPA实体管理工厂(javax.persistence.EntityManagerFactory接口的任意实现)。JpaTransactionManager将与由工厂所产生的JPA EntityManager合作来构建事务。。
JpaTransactionManager支持将事务应用于简单的JDBC操作之中,这些JDBC操作所使用的DataSource与EntityManagerFactory所使用的DataSource必须是相同的。为了做到这一点,JpaTransactionManager必须装配一个JpaDialect的实现。
eg:
EclipseLinkJpaDialect
将jpaDialect Bean装配到JpaTransactionManager中
JpaDialect实现必须同时支持JPA/JDBC访问。所有Spring所支持的特定厂商JpaDialect实现都提供了对JPA和JDBC混合的支持,而DefaultJpaDialect并不支持。
6.2.4 JTA事务
在其他事务管理器满足不了、或事务需要跨资源使用时,需要使用JtaTransactionManager:
JtaTransactionManager将事务管理委托给一个JTA的实现。JTa规定了应用程序与一个或多个数据源协调事务的标砖API。transactionManagerName属性指明了要在JNDI上查找的JTA事务管理器。
6.3 Spring编码事务
eg:
saveSpittle()
public void saveSpittle(Spittle spittle) {
spitterDao.saveSpittle(spittle);
}
添加编码事务
public void saveSpittle(final Spittle spittle) {
txTemplate.execute(new TransactionCallback() { //这里为什么是Void,不太理解
public Void doInTransaction(TransactionStatus txStatus) {
try {
spitterDao.saveSpittle(spittle);
} catch(RuntimeException e) {
txStatus.setRollbackOnly();
throw e;
}
return null;
}
});
}
为了使用TransactionTemplate,需要实现TransactionCallBack接口。因为TransactionCallback只有一个要实现的方法,通常会简单的将其实现为匿名内部类。将事务性的代码放在doInTransaction()方法中。
调用TransactionTemplate实例的execute()方法时,将会执行TransactionCallback实例中的代码。如果代码遇到了问题,调用TransactionStatus对象的setRollbackOnly()方法将回滚事务。否则,如果doInTransaction()成功返回,事务将会提交。
TransactionTemplate实例需要注入到SpitterServiceImpl中
TransactionTemplate需要注入一个transactionManager。实际上TransactionTemplate使用了PlatformTransactionManager实现来处理特定平台的事务管理细节。这里装配的transactionManager的Bean引用,可以是Spring提供的任意一个事务管理器。
编程式事务是具有侵入性的。因此,在不需要极小粒度的事务控制时,声明式事务是更好的选择。
6.4 声明式事务
通过AOP实现。
6.4.1 定义事务的属性
Spring中,声明式事务是通过 事务属性(transaction attribute)来定义的。是无属性描述了事务策略如何应用到方法上。事务属性包含了五个方面:传播行为、隔离级别、hi滚规则、事务超时、是否只读
Spring所有的声明式事务机制都依赖这5个参数来控制如何管理实务策略。
传播行为
propagation behavior。定义客户端与被调用方法之间的事务边界。Spring定义了7种不同的传播行为
传播行为 |
含义 |
PROPAGATION_ MANDATORY |
表示该方法必须在事务中运行,如果当前事务不存在,抛出一个异常 |
PROPAGATION_NESTED |
表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独的提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。歌唱声对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确定他们是否支持嵌套式事务 |
PROPAGATION_NEVER |
表示当前方法不应该运行在事务上下文中。如果当前争优一个事务在运行,抛出异常 |
PROPAGATION_NOT_SUPPORTED |
表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_REQUIRED |
表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务 |
PROPAGATION_REQUIRES_NEW |
表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionMangager,则需要访问TransactionManager |
PROPAGATION_SUPPORTS |
表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行。 |
传播规则规定了,新的事务应该被启动还是挂起、方法是否需要在事务环境中运行。
隔离级别
isolation level。定义了一个事务可能受其他并发事务影响的程度。理想情况下,事物之间是完全隔离的,但这会导致性能问题。
隔离级别 |
含义 |
ISOLATION_DEFAULT |
适用后端数据库默认的隔离级别 |
ISOLATION_READ_UNCOMMITTED |
允许读取尚未提交的数据变更。可能导致脏读、幻读、不可重复读 |
ISOLATION_READ_COMMITTED |
允许读取并发事务已经提交的数据。可能发生幻读、不可重复度 |
ISOLATION_REPEATABLE_READ |
对同一字段的多次读取结果是一只的,除非数据被本事务自己修改。可能发生幻读 |
ISOLATION_SERIALIZABLE |
完全服从ACID的隔离级别,确保阻止脏读、不可重复度、幻读。最慢的事务隔离级别,通过完全锁定事务相关的数据库表来实现。 |
隔离级别定义在org.springframework.transaction.TransactionDefinition接口中
并不是所有数据源都支持这些隔离级别
只读
如果食物支队后端的数据库进行读操作,数据库可以利用食物的之独特性来进行一些特定的优化。
制度优化是在事务启动的时候由数据库实时的,只有对那些具备启动一个新的事务的传播行为的方法,将事务声明为只读才有意义。包括:PROPAGATION_REQUIRED/PROPAGATION_REQUIRES_NEW/PROPAGATION_NESTED
事务超时
超时始终会在事务开始时启动,只有对那些具备启动一个新事物的传播性为的方法,设置事务超时才有意义。包括:PROPAGATION_REQUIRED/PROPAGATION_REQUIRES_NEW/PROPAGATION_NESTED
回滚规则
规定哪些异常会导致事务回滚,哪些不会。默认情况下,事务只有在遇到RuntimeException时才会回滚,检查性异常时不会回滚。
6.4.2 在XML中定义事务
spring提供了一个tx配置命名空间,用来配置Spring中的声明式事务。
xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop=" http://www.springframework.org/schema/aop"
xmlns:tx=" http://www.springframework.org/schema/tx"
xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xds">
注意,aop命名空间也应该包括在内
事务属性定义在元素中,元素为相应的属性指定参数
对事务属性的定义:
隔离级别 |
含义 |
isolation |
指定事务的隔离级别 |
propagation |
事务的传播规则 |
read-only |
指定事务为只读 |
rollback-for
no-rollback-for
|
指定事务对于哪些检查性异常应当回滚而不提交
指定事务对于哪些异常应当继续运行而不会滚
|
timeout |
超时时间 |
使用来声明事务时,需要一个事务管理器。根据
约定优于配置,嘉定事务管理器被声明为一个id为transactionManager的Bean。如果事务管理器配置了其他的id,例如txManager,则需要在transactionmanager属性中明确指定事务管理器的id:
只是定义了AOP通知,用于把事务边界通知给方法。但是这只是事务通知,而不是完整的事务性切面。需要一个切点来声明那些Bean应该被通知,因此为了完整定义事务性切面,需要定义一个通知器advisor。
eg:
point-cut="execution(* *..SpitterService.*(..))"
advice-ref="
txAdvice"/>
6.4.3 定义注解驱动的事务
//就可以了
可以通过transactionmanager属性(默认为transactionManager)来指定特定的事务管理器
元素告诉Spring检查上下文中所有的Bean并查找使用@Transactional注解的Bean,无论这个注解用于类级别还是方法级别。对于每一个使用@Transactional注解的Bean,会自动为它添加事务通知。通知的事务属性时通过@Transaction注解的参数来定义的。
eg:
@Transational(propagation=Propagation.SUPPORTS,readOnly=true)
public class SpitterServiceImpl implements SpitterService {
...
@Transactional(propagation=PROPGATION.REQUIRED, readOnly=false)
public void addSpitter(Spitter spitter) {
...
}
...
}
在类级别上使用@Transactional注解表示所有的方法都支持事务。
第6章小结
Spring同时支持编码式和声明式的事务管理。无论按一种方式,Spring都将事务管理平台抽象为通用的API,避免了直接与特定的事务管理器实现进行交互。