- 背景知识
事务,其实就是一个或者一组不会中途失败的操作。简单理解就是,要么同时成功,要么同时失败。一开始我对这句话有点不解,因为这句话有点故意押韵的意思,但是结合一个实际场景,就豁然开朗了。在网购的时候都需要通过网络支付才能完成下单,如果“付钱”这个动作没有完成,就显示“已支付”,那么就没有人会去网上卖东西。
- 事务的特性
根据上边的背景,我们可以总结出来事务的四个特性,也是面试中经常被问到的“ACID”。
- A - atomcity 原子性:这个特性很容易理解,就是每一个事务都只有一种状态,要么成功,要么失败。
- C - Consistency 一致性:在事务完成之后,所有的状态都是一直的,也就是调用链上的方法,都是成功的,只要有一个失败,那么都不是事务。
- I - isolation 隔离性:事务与事务之间是独立的,不会相互影响。如果两个事务之间的操作有冲入,那么就必须是一个完成之后才会进行下一个。
- D - durability 持久性:这个特性也很好理解,可以持久的。也就是事务一旦发生之后,一切更改都是持久的影响。
- spring 中的事务实现
Java中最常用的实现事务的方式就是利用Spring 本身的事务。
众所周知,作为Java中最主要的一个框架——Spring本身的功能是非常完善的,虽然已经有点臃肿,利用Spring进行事务的管理是一种非常高效的方法。
这里先贴出一张网络上常见的图:
从这张图中我们可以看到三个非常重要的类:TransactionDefinition、PlatformTransactionManager以及TransactionStatus。下边是对他们的详解。
3.1 TransactionDefinition
这个是用来定义事务的传播行为的类,可以配置的项有下边6种。
- PROPAGATION_MANDATORY - 强制事务,没有事务就会报错
- PROPAGATION_NESTED - 嵌套事务,如果有事务,就会嵌套当前事务;如果没有,则会新增一个事务。
- PROPAGATION_NEVER - 永远不使用事务。
- PROPAGATION_NOT_SUPPORTED - 不使用事务,如果当前有事务,会被挂起(不生效)。
- PROPAGATION_REQUIRED - 最常见的操作,会用事务执行,如果没有事务,就会新建一个事务。
- PROPAGATION_REQUIRES_NEW - 每次都会新建事务
- PROPAGATION_SUPPORTS - 如果有事务,就会使用事务,如果没有,就会以非事务的形式执行。
上边的事务的传播行为,使用最多的是:PROPAGATION_REQUIRED ,也可以根据自己的实际使用情况使用其他的类型。
3.2 PlatformTransactionManager
这个是Spring 中的事务管理器,里边有三个方法:
- getTransaction - 根据上边的定义获取事务
- commit - 提交事务
- rollback - 回滚事务
可以看出这个是事务的基本使用方法。
3.3 TransactionStatus
故名思意,这个类是用来表示事务的各个状态的,这个接口有下边的方法:
- flush - 刷新所有的数据,但是这个主要是取决于底层的JPA或者Hibernate的机制。
- hasSavepoint - 检查是否有存档点,存档点是回滚事务的地方。
- isCompleted - 检查事务是否完成提交/回滚。
- isNewTransaction - 检查当前事务是否是新的事务。
- isRollbackOnly - 检查当前事务是否为“只回滚”状态。
- setRollbackOnly - 将事务设置成“只回滚”状态,被设置的线程只要出现异常就会回滚。
4.spring中事务的使用
4.1 代码实现
前边铺垫这么久,其实事务的使用非常简单,实际的代码如下:
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 自己的逻辑
} catch (Exception ex) {
LOG.error("Exception in executeSelect:",ex);
transactionManager.rollback(status);
throw ex;
}
transactionManager.commit(status);
这里的使用的流程就是:
- 定义一种事务的传播行为,当然这个事务的传播行为是可以共用的;
- 获取当前事务的状态,也就是上边的status;
- 进行自己的逻辑,一般会用try catch处理异常;
- 如果出现异常,就在catch中进行回滚;如果没有出现异常,就提交事务。
4.2 注解实现
虽然上边的代码比较简单,而且容易上手,但是更加常用的还是利用注解来标记事务。用注解来表示事务,其实只是需要在自己的业务代码中增加@Transactional 注解。
这个注解的常用配置属性有两个:isolation和propagation。
isolation是指事务的隔离级别,Spring 中,事务的隔离级别有五种:
- ISOLATION_DEFAULT - 默认事务(个人觉得只是占位符)
- ISOLATION_READ_COMMITTED - 读已提交
- ISOLATION_READ_UNCOMMITTED - 读未提交
- ISOLATION_REPEATABLE_READ - 可以重复读
- ISOLATION_SERIALIZABLE - 同步读取
这几种隔离级别中,一般只会用第二种,其他的除了最后一种,都会有不同的安全隐患,所以一般不会用。而最后一种,虽然是最安全的,但是其速度非常慢,所以也是比较少使用。
propagation属性对应的是上边的事务的传播行为,这里使用PROPAGATION_REQUIRED即可。综上,要实现和4.1一样的效果,只要用下边这行注释即可。
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)