关于Java事务&spring 事务的详解

事务(ACID)

 

是在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元。

 

事务的4个特性(ACID):

 

原子性(atomicity):事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。

 

一致性(consistency):事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性

 

隔离性 (isolation):一个事务的执行不能被其他事务所影响。

 

和持久性(durability):一个事务一旦提交,事物的操作便永久性的保存在DB中。即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

 

五个隔离级别:

DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.

未提交读(read uncommited) :脏读,不可重复读,虚读都有可能发生

已提交读 (read commited):避免脏读。但是不可重复读和虚读有可能发生

可重复读 (repeatable read) :避免脏读和不可重复读.但是虚读有可能发生.

串行化的 (serializable) :避免以上所有读问题.

 

 

ISOLATION_DEFAULT:这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.

 

另外四个与JDBC的隔离级别相对应;

 

ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。脏读

场景:月底了财务给大家发工资,给小A账户发10000元,在事务还没有提交的时候,小A查了一下账户看到了有10000元,高兴坏了。但此时财务发现是自己手误打多了一个0,于是回滚了事务,重新发了1000元提交了事务,小A重新查了一下发现卡里的金额变成了1000元空欢喜了一场。

 

ISOLATION_READ_COMMITTED:允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行,读已提交(主要指数据值改变)。 oracle默认级别

场景:小A开启了事务查了一下账户金额有1000元,此时他的花呗到期,支付宝从账户了自动扣掉了100元(这个扣100元是其他事物的操作且已提交),此时小A又重新查余额变成900元了(发生了在一个事物里两次查询的结果值不一致)。

ISOLATION_REPEATABLE_READ:这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。幻读(主要指行数改变)。Mysql默认级别

场景:小A开启了事务查了一下账户金额有1000元,他查了一下自己的账户没有转账记录,此时他的花呗到期,支付宝从账户了自动扣掉了100元(这个扣100元是其他事物的操作且已提交),他这是查了一下余额还是1000元,然后给老婆转了800块钱,一查账户记录突然查到了两条,这时候就发生了幻读)

 

ISOLATION_SERIALIZABLE:这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。串行化,所有事物排队执行,不允许同时执行,效率极低。

 

 

 

事务的传播行为:

 

* 保证同一个事务中

PROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个(默认)

PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务

PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常

* 保证没有在同一个事务中

PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务

PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务

PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常

PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行

 

 

Spring 事务失效情况

 

@Transactional经常遇到的几个场景:

@Transactional 加于private方法, 无效 @Transactional 加于未加入接口的public方法, 再通过普通接口方法调用, 无效 @Transactional 加于接口方法, 无论下面调用的是private或public方法, 都有效 @Transactional 加于接口方法后, 被本类普通接口方法直接调用, 无效 @Transactional 加于接口方法后, 被本类普通接口方法通过接口调用, 有效 @Transactional 加于接口方法后, 被它类的接口方法调用, 有效 @Transactional 加于接口方法后, 被它类的私有方法调用后, 有效

 

Spring中一个没有事务的方法A调用一个默认事务(PROPAGATION_REQUIRED)的方法B时,如果使用this调用方法B,方法B抛出RuntimeException,此时方法B事务未生效,不会回滚。

@Service

public class EmployeeService {

@Autowired

private EmployeeDao employeeDao;

public void save(){

try {

this.saveEmployee(); //此处this调用不会开启事务,数据会被保存

}catch (Exception e){

e.printStackTrace();

}

}

@Transactional(propagation = Propagation.PROPAGATION_REQUIRED)

//此处无论是PROPAGATION_REQUIRED还是PROPAGATION_REQUIRES_NEW,事务均不生效

public void saveEmployee(){

Employee employee = new Employee();

employee.setName("zhangsan");

employee.setAge("26";

employeeDao.save(employee);

throw new RuntimeException();

}

}

JDK的动态代理。只有被动态代理直接调用时才会产生事务。在SpringIoC容器中返回的调用的对象是代理对象而不是真实的对象。而这里的this是EmployeeService真实对象而不是代理对象。

 

解决办法:

方法1、在方法save上开启事务,方法saveEmployee不用事务或默认事务,并在方法A的catch中throw new RuntimeException();(在没指定rollbackFor时,默认回滚的异常为RuntimeException),这样使用的就是方法save的事务。(一定要throw new RuntimeException();否则异常被捕捉处理,同样不会回滚。)如下:

@Transactional() //开启事务

public void save(){

try {

this.saveEmployee(); //这里this调用会使事务失效,数据会被保存

}catch (Exception e){

e.printStackTrace();

throw new RuntimeException();

}

}

 

上一篇:spring在加Transactional的方法中使用redis取值为空的问题


下一篇:Linux运行级别研究(转)