事务: Transaction, 是数据库中的一种能够保证多个写操作要么全部成功, 要么全部失败的机制
在基于Spring JDBC的数据库编程中, 在业务方法上添加@Transactional注解, 即可使得这个业务方法是事务性的
举例, 一个银行转账操作, 转账时需要执行的sql语句大致是:
UPDATE 存款表 SET 余额=余额-50000 WHERE 账号='李同学';
UPDATE 存款表 SET 余额=余额+50000 WHERE 账号='张同学';
以上的转账操作就涉及数据库的多次写操作, 如果由于某些意外原因(例如停电、服务器死机等), 导致第一条sql语句成功执行, 但是第二条sql语句未能成功执行, 就会出现数据不完整的问题! 使用事务就可以解决这个问题!
关于@Transactional注解, 可以添加在:
业务实现类的方法上:
仅作用于当前方法
业务实现类上:
将作用于当前类的所有方法
业务接口的抽象方法上:
仅作用于当前方法
无论是哪个类重写此方法, 都将是抽象的
业务接口上:
将作用于当前接口中所有抽象方法
无论是哪个类实现了此接口, 重写的所有方法都将是事务的
在执行数据访问操作时, 数据库有一个"自动提交"的机制
事务的本质是会先将"自动提交"关闭, 当业务方法执行完毕之后, 再一次性提交
再事务中, 涉及几个概念:
开启事务: BEGIN
提交事务: COMMIT
回滚事务: ROLLBACK
在基于Spring JDBC的程序设计中, 通过@Transactional注解即可使得业务方法是事务性的, 其实现过程大致是:
开启事务
try{
执行业务方法
提交事务
}catch(RuntimeException e){
回滚事务
}
可以看到, Spring JDBC框架再处理事务时, 默认将根据RuntimeException进行回滚
提示:可以配置@Transactional注解的rollbackFor或rollbackForClassName属性来指定回滚的异常类型,即根据其它类型的异常来回滚,例如:
@Transactional(rollbackFor = {IOException.class})
@Transactional(rollbackForClassName = {}"java.io.IOException"})
另外,还可以通过noRollbackFor或noRollbackForClassName属性用于指定不回滚的异常!
建议在业务方法中执行了任何增、删、改操作后,都获取受影响的行数,并判断此值是否符合预期,如果不符合,应该及时抛出RuntimeException或其子孙类异常!
补充
Spring JDBC框架再实现事务管理时, 使用到了Spring AOP技术及基于接口的代理模式, 由于使用了基于接口的代理模式, 故如果将@Transactional注解添加在实现类中自定义的方法(不是重写的接口中的抽象方法)上, 是错误的做法
事务的ACID属性
为了操持数据库的一致性, 在事务处理之前和之后, 都遵循某些属性, 也就是大家耳熟能详的ACID属性:
· 原子性(Atomicity): 即不可分割性, 事务中的操作要么全不做, 要么全做
· 一致性(Consistency): 一个事务在执行前后, 数据库必须处于正确的状态, 满足完整性约束
· 隔离性(Isolation): 多个事务并发执行时, 一个事务的执行不应影响其他事务的执行
· 持久性(Durability): 事务处理完成后, 对数据的修改就是永久的, 即时系统故障也不会丢失
并非任意的对数据库的操作序列都是数据库事务, ACID是一系列操作组成事务的必要条件。总而言之, ACID提供了一种机制, 使每个事务都"作为一个单元, 完成一组操作, 产生一致结果, 事务彼此隔离, 更新永久生效", 从而来确保数据库的正确性和一致性
事务的传播
事务的传播表现为: 某个数据访问过程中调用了另一个事务, 事务应该如何执行?
当需要管理事务的传播方式时, 配置@Transactional注解的propagation属性即可, 在绝大部分情况下, 没有必要刻意的设置事务的传播方式, 使用默认的REQUIRED即可, 它表现为: 如果当前无事务, 将创建新的事务, 如果当前已存在事务, 则使用当前事务
事务的隔离
隔离性是指, 并发执行的各个事务之间不能互相干扰, 即一个事务内部的操作及使用的数据, 对并发的其他事务是隔离的。此属性确保并发执行一系列事务的效果等同于以某种顺序串行地执行它们
也就是达到这么一种效果:
对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。这要求两件事:
在一个事务执行过程中,数据的中间的(可能不一致)状态不应该被暴露给所有的其他事务。
两个并发的事务应该不能操作同一项数据。数据库管理系统通常使用锁来实现这个特征。
事务隔离分为不同级别, 包括未提交读(Read Uncommitted), 提交读(Read Committed), 可重复读(Repeatable Read)和串行化(Serializable)
以上四个级别地隔离性以依次增强, 分别解决不同的问题。事务隔离级别越高, 就越能保证数据的完整性和一致性, 但同时对并发性能的影响也越大
总结
事务(Transaction)是由一系列对系统中数据进行访问或更新的操作所组成的一个程序执行逻辑单元(Unit)
在事务的ACID特性中, C即一致性是事务的根本追求, 而对数据一致性的破坏主要来自两个方面:
事务的并发执行
事务故障或系统故障
数据库系统是通过并发控制技术和日志恢复技术来避免这种情况发生的
并发控制技术保证了事务的隔离性, 使数据库的一致性状态不会因为并发执行的操作被破坏
日志恢复技术保证了事务的原子性, 使一致性状态不会因为事务或系统故障被破坏, 同时使已提交的数据库的修改不会因系统崩溃而丢失, 保证了事务的持久性