先想了解@Transaction注解,首先就要了解什么是事务
没有事务存在的问题:
首先有一段代码他有很多个操作对数据库进行更改,但是代码在运行中,执行到一半的时候一个操作数据库的一个操作报了一个错误,此时咱们知道Java中报了错误,就不会在往下执行代码了,那么还有一半的代码片段就不会执行,但是在这段代码中已经有一半的代码已经执行了,并且也对数据库进行操作了,但是可能没有被执行到的代码片段是一些非常重要的数据,此时可能就会导致项目数据异常,所以要解决这样的问题,就必须要有事务了
那么什么是事务:
Java中的事务,简单来说,我们可以把多个操作看成一个操作单元,那么这个操作单元在运行的时候要么一起成功要么一起失败,这样就是事务了
在我们数据库层面的事务有四大特性:
原子性:原子性就是我们一个操作单元操作数据库一个小操作失败了,那么他这个整个操作单元之前操作的数据库,都会滚回,恢复成数据库之前的数据.
一致性:一致性是指一个事务对数据库的一种状态改变,使数据库从一种一致状态转化成另一种一致状态
隔离性:多个事务进入数据进行操作,一个事务在操作的时候数据库会禁止其他事务对其进行更改,保证一个事务在操作的时候数据的隔离性
持久性:一个事务操作完成后对数据库的修改是永久性的,是不能在被回滚了
Spring中的事务
Spring中处理事务一个共有两种方法,通Spring aop通过面向切面的方式配置事务(xml),但是这种方式需要配置,并且可能有很多地方需要事务,那就需要在需要事务在配置 ,这样就会显得在xml配置比较繁琐,还有一种就是Spring @Transaction注解,Transaction注解就比xml这种配置方式好多了,哪里需要事务就直接贴上,可以贴在类上,也可以贴在方法上,贴上就拥有了事务
<!-- xml中配置事务 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">//事务管理的接口
<property name="dataSource" ref="dataSource" />
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>//表示调用dao层方法开头的方法拥有事务,这里可以用*表示,这样就是什么方法拥有事务
<tx:method name="list*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="select*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
//这里表示service层下所有带Service的类带有事务,且类的名字有Service
<aop:pointcut expression="execution(* cn.mor.crm.service.*Service.*(..))" id="txPoint" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint" />
</aop:config>
@Transactional直接贴上方法或者类上就可以了
@Transactionald的使用
@Transactional默认是回滚RuntimeException错误,就是运行时异常
@Transactional(readOnly=true)//表示只读,false表示读写
@Transactional(rollbackFor=Exception.class)//指定异常类型回滚
@Transactional(rollbackFor={Exception.class,RuntimeException.class})//指定异常类型回滚多个
@Transactional(noRollbackFor =RuntimeException.class)//指定不需要回滚的异常
@Transactional(timeout=30);//事务超时时间.默认30s
@Transactional事物传播行为介绍:
@Transactional(propagation=Propagation.REQUIRED) :如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事务,都创建一个新的事务,原来的挂起,新的执 行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) :必须在一个已有的事务中执行,否则抛出异常 @Transactional(propagation=Propagation.NEVER) :必须在一个没有的事务中执行,否则抛出异常(与 Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) :如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
@Transactional事务隔离级别设置
@Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE):串行化
@Transactional使用 try-catch问题:
我们在贴了@Transactional的方法下或者类下,使用 try-catch捕获异常,此时会有一个问题,就是我们代码在try里面运行中报错了,
然后被catch捕获到了,此时因为异常已经被catch捕获到异常,@Transactional注解是检测不到异常,这样报错的代码就不会进行回滚了
解决方案:
此时我们就需要把异常给抛出去,那我们就需要使用throw抛出异常,但是我们抛出的异常可能有很多种,比如是IOException,如果我们把IOException抛到方法上,此时可能有人会觉得可以了,其实还是不行的,如果你贴的@Transactional默认的,什么也没配置,那么你抛出的异常还是不会回滚,因为@Transactiona默认回滚错误是RuntimeException,所有也不会进行回滚,如果用@Transactiona(rollbackFor=Exception.class)指定所有异常的父类Exception进行回滚,就不会因为抛出的异常是IOException,Transactiona就不会进行回滚了,因为指定Exception回滚就表示所有Exception下的子类异常都能进行回滚
@Transactional(rollbackFor=Exception.class)//这样代码才会回滚,@Transactional不会回滚
public String test() throws IOException {
try {
System.out.println("对数据库进行操作");
System.out.println("报错啦啦啦啦啦");
System.out.println("没有执行到");
} catch (Exception e) {
throw new IOException("捕获到异常");//抛出异常
}
return null;
}