1 事务的ACID四大特性
原子性(Atomicity):事务是一个原子操作,由一系列的动作组成。原子性确保动作要么都做,要么都不做。
一致性(Consistency):一致性确保系统从一个一致状态转换为另一个一致状态。不管操作成功或失败,数据都不应该被破坏。
隔离性(Isolation):事务与事务之间相互隔离,互不干扰。
持久性(Durability):事务提交后,对系统的影响是持久不变的。无论系统发生什么错误,事务的结果是不变的。
2 spring事务隔离级别
2.1 并发事务可能引发的问题
(1)脏读(dirty read)
脏读发生在一个A事务读取了B事物改写但尚未提交的数据,一旦B事务发生异常回滚了数据,A事务读取的数据就是无效的。
(2)不可重复读(Nonrepeatable read)
不可重复读是在A事务执行相同的查询两次或两次以上时,每次查询的数据都不一致。这通常是因为在A事务进行查询期间,B事务对相同的数据进行了修改造成的。
(3)幻读(Phantom read)
幻读与不可重复读类似。幻读发生在A事务查询了几条数据,接着另一个并发事务B插入了几条数据,A事务再查询数据时发现和刚刚相比多了几条不存在的数据。
2.2 Spring事务的隔离级别
名称 | 值 | 解释 |
ISOLATION_DEFAULT | -1 | 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应 |
ISOLATION_READ_UNCOMMITTED | 1 | 这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。 |
ISOLATION_READ_COMMITTED | 2 | 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生 |
ISOLATION_REPEATABLE_READ | 4 | 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生 |
ISOLATION_SERIALIZABLE | 8 | 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的 |
3 spring事务的传播行为
名称 | 值 | 解释 |
PROPAGATION_REQUIRED | 0 | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是Spring默认的事务的传播。 |
PROPAGATION_SUPPORTS | 1 | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 2 | 支持当前事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 3 | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 4 | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 5 | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 6 | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 |
4 spring事务管理核心方法
图片(1)
图片(2)
Spring不直接管理事务,而是提供多种事务管理器,将具体的事务管理职责委托给持久化框架提供的事务来实现,例如通过HibernateTransactionManager将事务委托给hibernate的事务来实现。
其中有几个重要的核心类(主要位于spring-orm-3.1.2.RELEASE.jar和spring-tx-3.1.2.RELEASE.jar):
4.1 org.springframework.transaction.PlatformTransactionManager
此类是spring事务管理的核心接口,几乎所有相关的类直接或者间接的继承或者实现此接口。
4.2 org.springframework.transaction.TransactionDefinition
此类是定义spring事务属性的接口,其中包括事务的隔离级别、传播行为、超时时间、事务名称、是否只读等。
4.3 org.springframework.transaction.TransactionStatus
此接口主要用于保存事务运行的一些具体状态
4.4 org.springframework.transaction.support.ResourceTransactionManager
继承PlatformTransactionManager接口,指示本地资源事务管理器,操作单个目标资源。所有的orm框架事务管理器都要实现这个接口。这个接口只有一个方法:
Object getResourceFactory();
返回事务管理器操作的资源工厂,例如JDBC DataSource或JMS ConnectionFactory或hibernate的SessionFactory。
4.5 org.springframework.transaction.support.AbstractPlatformTransactionManager
此类是一个抽象基类,实现PlatformTransactionManager接口,实现spring标准事务工作流程,担任基础事务管理器。所有的orm框架事务管理器都要继承扩展此类。这个类的状态是可序列化的,实现了java.io.Serializable接口,允许与携带事务拦截器的代理一起序列化事务策略。
此类类提供了以下工作流处理:
1)确定是否存在现有事务;
2)应用适当的传播行为;
3)如果必要,暂停和恢复事务;
4)在提交时检查回滚标志;
5)在回滚上应用适当的修改(实际上就是回滚或只设置rollback-only标记);
6)触发注册同步回调(如果事务同步是活动的)。
创建事务的主要方法是(详情请参考API):
org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(TransactionDefinition)此方法是一个final方法,只处理了一些通用性的检查和设置,实质性的创建事务和开启事务操作都是通过分别调用抽象方法:
protected abstract Object doGetTransaction() throws TransactionException;
和
protected abstract void doBegin(Object transaction, TransactionDefinition definition)throws TransactionException;
来完成的,也就是说这些关键性的工作必须由各具体事务管理器来实现。
4.6 org.springframework.orm.hibernate3.HibernateTransactionManager
此类继承AbstractPlatformTransactionManager类,提供了自己的实现。其中最主要重要的方法为(详细请参考API):
doGetTransaction()//获取一个事务对象
doBegin(Object, TransactionDefinition)//开始一个事务
doCommit(DefaultTransactionStatus)//提交事务
doRollback(DefaultTransactionStatus)//回滚事务
5 spring编程式事务
Spring提供了两种编程式事务管理,一种是通过PlatformTransactionManager实现,另一种通过TransactionTemplate实现,推荐使用后者。编程式事务与声明式事务相比,前者能够提供更加细腻的事务管理,能够在代码中精确定义事务的边界,但是编程式事务每次都需要单独实现,事务管理的代码侵入业务代码当中,不利于解耦,对于业务逻辑复杂的功能是相当费脑的。
5.1 PlatformTransactionManager方式
// 定义一个某个框架平台的TransactionManager,如JDBC、Hibernate
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
// 设置数据源
dataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource());
// 定义事务属性
DefaultTransactionDefinition transDef = new DefaultTransactionDefinition();
// 设置传播行为属性
transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
// 获得事务状态
TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef);try
{
// 数据库操作
dataSourceTransactionManager.commit(status);// 提交
}catch(
Exception e)
{
dataSourceTransactionManager.rollback(status);// 回滚
}
5.2 TransactionTemplate方式
采用TransactionTemplate和采用其他Spring模板,如JdbcTempalte和HibernateTemplate是一样的方法。它使用回调方法,把应用程序从处理取得和释放资源中解脱出来。如同其他模板,TransactionTemplate是线程安全的。
相关代码如下
TransactionTemplate tt = new TransactionTemplate();
// 新建一个TransactionTemplate
tt.execute(
new TransactionCallback(){
public void doInTransactionWithoutResult(TransactionStatus status){
updateOperation();
}
});
// 执行execute方法进行事务管理
6 spring声明式事务
声明式事务主要有两种实现方式(最常用的两种):一种是基于<tx:advice>和aop命名空间的xml配置方式;一种是基于@Transactional注解的方式。
6.1 使用tx标签配置的拦截器方式
相关xml配置代码如下:
<!--<tx:advice>定义事务通知,用于指定事务属性,其中“transaction-manager”属性指定事务管理器,并通过<tx:attributes>指定具体需要拦截的方法
<tx:method>拦截方法,其中参数有: name:方法名称,将匹配的方法注入事务管理,可用通配符
propagation:事务传播行为;
isolation:事务隔离级别定义,默认为“DEFAULT” ;
timeout:事务超时时间设置,单位为秒,默认-1,表示事务超时将依赖于底层事务系统;
read-only:事务只读设置,默认为false,表示不是只读;
rollback-for:需要触发回滚的异常定义,可定义多个,以“,”分割,默认任何RuntimeException都将导致事务回滚,而任何Checked Exception将不导致事务回滚;
no-rollback-for:不被触发进行回滚的 Exception(s);可定义多个,以“,”分割;
-->
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 拦截save开头的方法,事务传播行为为:REQUIRED:必须要有事务, 如果没有就在上下文创建一个 -->
<tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED"
timeout="" read-only="false" no-rollback-for="" rollback-for="" />
<!-- 支持,如果有就有,没有就没有 -->
<tx:method name="*" propagation="SUPPORTS" />
</tx:attributes>
</tx:advice>
<!-- 定义切入点,expression为切人点表达式,如下是指定impl包下的所有方法,具体以自身实际要求自定义 -->
<aop:config>
<aop:pointcut expression="execution(* com.*.service.impl.*.*(..))"
id="pointcut" />
<!--<aop:advisor>定义切入点,与通知,把tx与aop的配置关联,才是完整的声明事务配置 -->
<aop:advisor advice-ref="advice" pointcut-ref="pointcut" />
</aop:config>
6.2 @Transactional注解方式
(1)此种方式是最常用也是推荐使用的一种方式,因为此种方式是基于注解的,只需要使用少量配置代码。不会像变成式注解那样入侵业务代码,方便业务代码解耦。
Xml配置代码如下:
<!-- spring事务管理:使用@Transactional注解 -->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
<property name="nestedTransactionAllowed" value="true" />
</bean>
<!-- 声明式事务管理 配置事物的注解方式注入 -->
<tx:annotation-driven transaction-manager="txManager"
proxy-target-class="true" />
使用的时候只需要在需要事务的方法上添加@Transactional就可以了。
(2)@Transactional的参数详解
属性 | 类型 | 描述 |
value | String | 可选的限定描述符,指定使用的事务管理器 |
propagation | enum: Propagation | 可选的事务传播行为设置 |
isolation | enum: Isolation | 可选的事务隔离级别设置 |
readOnly | boolean | 读写或只读事务,默认读写 |
timeout | int (in seconds granularity) | 事务超时时间设置 |
rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
注意:
1)@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
2)虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
3) 默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。
4)在拥有事务的前提下,任何的RuntimeExcetipn及其子类 将触发回滚,任何的checked Exception不触发回滚。不管@Transactional中的参数readOnly等于true或者false。checked Exception主要包括除了RuntimeExcetipn及其子类和Error的Exception异常(注意:将派生于Error或者RuntimeException的异常称为unchecked异常,所有其他的异常成为checked异常)。
5)@Transactional(readOnly=true)仍然会开启事务,但是这个会告诉底层是只读事务,一般用在多查询的场景,在此期间,此事务不会读取其他事务提交的数据。如果在此事务中更新数据,遇到unchecked exception时还是会回滚数据。
“只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。 但是你非要在“只读事务”里面修改数据,也并非不可以,只不过对于数据一致性的保护不像“读写事务”那样保险而已。 因此,“只读事务”仅仅是一个性能优化的推荐配置而已,并非强制你要这样做不可
7 springMVC中使用@Transactional经验总结
(1)把@Transactional加在controller上,被调用的service层方法任然在controller的事务中,出错任然会回滚数据。
(2)在service中有@Transactional的A方法调用没有@Transactional的B方法,B方法中进行业务逻辑判断和更新操作,并在B中抛出unchecked exception时还是会回滚数据。
(3)在service中有@Transactional的A方法调用没有@Transactional的B方法,B方法中进行业务逻辑判断和更新操作,在A中抛出unchecked exception时不会回滚数据。
(4)在service中有@Transactional的A方法调用没有@Transactional的B方法,A方法中进行业务逻辑判断和更新操作,在B中抛出unchecked exception时会回滚数据。
(5)在service中没有@Transactional的A方法调用有@Transactional的B方法,不会触发B方法的事务,相当于此次操作运行在没有事务的环境中。
(6)service没有@Transactional的方法调用有@Transactional的dao方法,如果service方法中抛出任何异常都不会回滚数据,dao中抛出unchecked exception时会回滚当前调用过程中的数据。
(7)如果把@Transactional添加到类上面,这个类的所有方法都会启动事务。最好将@Transactional添加在需要使用事务的方法上面。
(8)在不添加事务和添加@Transactional(readOnly=true)的情况下,修改实体(此处的实体指的是hibernate管理的实体)不会触发hibernate更新数据。但是只要加了@Transactional写事务,在方法结束的时候,只要实体属性值被修改了,hibernate会自动将更改的内容保存到数据库。注意,此处的实体是通过session.createSQLQuery(sql).addEntity(Class class)产生的受hibernate管理的实体,属于持久态。通过session.createSQLQuery(sql).setResultTransformer(Transformers.aliasToBean(Clazz.class))查询的实体是不受hibernate管理的实体,属于脱管态。在@Transactional事务下,如果是修改的脱管态的实体,hibernate不会update实体数据。
(9)在@Transactional标注的方法内捕获了异常,出错不会回滚数据。只有当throws异常,才会回滚数据。
(10)注意:方法上的@Transactional会覆盖类上面声明的@Transactional事务
(11)@Transactional 注解只能应用到 public 可见度的方法上。 如果应用在protected、private或者 package可见度的方法上,也不会报错,不过事务设置不会起作用。
【四川乐山程序员联盟,欢迎大家加群相互交流学习5 7 1 8 1 4 7 4 3】