Spring事务相关记录
一、注解事务的使用:
<!-- 数据源 --> <bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close"> <property name="driverClassName" value="${db.driverClassName}" /> <property name="url" value="${db.url}" /> <!-- 相关配置 --> </bean> <!-- 事务配置 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
<!-- 没有指定 proxy-target-class="true" 则默认spring会自动挑选jdk代理或cglib代理。-->
<tx:annotation-driven transaction-manager="transactionManager" /> <!-- 扫描注解 --> <context:component-scan base-package="com.thunder.practice" />
关于@Transactional的添加位置:
当使用jdk代理的时候,由于是基于接口的代理,此时可以将@Transactional放到接口定义,或接口方法上,所有继承该接口的类、方法,都将继承事务。也可以放到实现类的类定义或者某个方法上。
当使用cglib代理的时候,由于是基于类的代理,此时如果将@Transactional放到接口定义,或接口的方法上,则此时事务将失效,但不会抛异常。
因此建议将@Transactional的注解放在实现类的类定义或者具体方法之上。另外在方法上的@Transactional注解会覆盖掉类上的注解。
ps.还有一种常见的事务失效的错误:
sprign mvc+spring 的项目结构时,如果分成两个配置文件,一个由spring mvc的配置文件(由dispatcher servlet调用的配置文件)和spring的配置文件(由listener调用的配置文件)是不同的上下文对象(父子上下文,父子容器)。spring配置文件属于父上下文,里面定义了事务管理,事务管理是有这个上下文控制的,mvc配置文件属于子上下文,一般会把扫描注解的配置放到mvc配置文件,此时mvc的上下文扫描到的Service是没有经过事务加强的service,(ps.事务管理的本质是AOP增强处理),所以事务没有生效,也不会报错。
解决办法是将事务管理和扫描@Transactional注解进行增强的配置放在一个上下文就行了。
二、事务的隔离级别(isolation)和传播行为(propagation)
以下记录为猜测,待验证。
1、事务是aop增强,加到对service方法开始前添加开始事务的代码,结束时添加关闭事物的代码。
2、因此在一个方法中通过this直接调用service方法,而不是通过增强过的代理对象来调用service方法是没有相应的事务管理效果的。
以上两点也就能引发这种状况:没有收到事务管理的A方法,通过this调用了同一对象中经过事务加强的方法B。此时方法B是没有事务效果的。
而如果启用了事务管理的方法A,通过this调用了同一对象中的方法B(无论是否添加了注解啊、声明什么的)。其效果等同于在一个事务中添加了一堆代码,方法B上进行的事务属性声明是不会起作用的。
3.隔离级别的意义在于两个事务对数据增删改查的隔离级别。
未提交读(read-uncommited)、提交度(read-commited)、可重复读(read-repeatable)、顺序读(serializable-read)
隔离级别依次递增.越高的隔离级别,能解决的数据一致性问题越多,理论上性能损耗更大,可并发性越低。
隔离级别是为了解决以下三个问题:脏读、不可重复读、幻读。
脏读:在两个事务同事进行时,A事务读取了B事务未提交的修改记录。如果之后B事务回滚,则A事务读取到的记录就是无效的记录。
不可重复读:在A事务中读取了一条记录,B事务修改该记录之后进行了提交,此时A事务再次读取该记录,则此时数据与第一次读取时不同。
幻读:A事务根据某些条件读取一个记录集,B事务添加或删除一条数据提交后,A事务读取到的记录集变多了或变少了。
mysql默认的隔离级别是repeatable read;
4.事务传播方法,其实说的是本方法被上一个方法调用时的执行模式。
默认的设置是REQUIRED:如果已经在一个事务中时,使用该事务,否则新建一个事务。
REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则将当前事务挂起。
SUPPORTS:如果处于一个事务中,则使用该事务,否则不使用事务。
NOT_SUPPORTED:以非事务的方式执行该方法,如果当前存在一个事务,则将该事务挂起。
NEVER:以非事务模式执行方法,如果存在事务,则抛出一个异常。
MANDATORY:必须处在一个事务中执行,如果当前没有事务,则抛出一个异常。
NESTED:如果处在一个事务当中,则以嵌套事务的模式执行方法。如果没有处于一个事务当中,则与REQUIRED的设置相同。
嵌套事务的重点是savepoint,报错回滚时不会将整个事务都回滚 ,只会回滚到savepoint那里。也就是说调这个嵌套事务的那个方法中进行的数据库操作不会被回滚。