Spring事务管理
今天学了Spring事务管理,事务管理这一章节大部分都是理论的知识。
事务(transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元,这些操作应该要么完整的执行,要么完全不执行,以确保数据的完整性和一致性。
事务的4个关键特点:
1、原子性(atom):事务应该当做一个单独单元的操作,这意味着整个序列操作要 么是成功,要么是失败的。
2、一致性(consistency):这表示数据库的引用完整性的一致性,表中的唯一主键 等。
3、隔离性(isolation):可能同时处理很多有相同数据集的事务,每个事务应该与其 他事务隔离,以防止数据损坏。
4、持久性(durable):一个事务一旦完成全部操作,这个事务的结果必须是永久性 的,不能因系统故障而从数据库中删除。
使用SQL发布事务到数据库系统的过程:
1、使用begin transaction命令开始事务;
2、使用SQL查询语句执行各种删除、更新或插入操作;
3、如果所有的操作都成功,则执行提交操作,否则回滚所有操作。
Spring事务管理类型:
1、按作用范围分为局部事务和全局事务;
2、按实现方式分为声明式事务和编程式事务。
局部事务式特定于一个单一的事务资源,如一个jdbc连接,局部事务管理在一个 集中的计算环境中是有用的,该计算环境中的应用程序组件和资源位于一个单 位点,而事务管理只涉及到一个运行在一个单一机器中的本地数据管理器。局 部事务更容易实现
全局事务可以跨多个事务资源,如在一个分布式系统中的事务。全局事务管需要在 分布式计算环境中,所有的资源都分布在多个系统中,在这种情况下事务管理 需要同时在局部和全局范围内进行。分布式或全局事务跨多个系统执行,它的 执行需要全局事务管理系统和所有相关系统的局部数据管理人员之间的协调。
编程式事务:
灵活但难以维护。
声明式事务:
从业务代码中分离出事务,仅关注xml配置来管理事务。
补充:声明式事务管理比编程式事务管理更可取,尽管他不如编程式事务管理灵活,但他允许你通过代码控制事务。但作为一种横切关注点,声明式事务管理可以使用AOP方法进行模块化。Spring支持使用Spring AOP框架的声明式事务管理。
事务操作的重要方法:
名称 |
说明 |
TransactionStatusgetTransaction(TransactionDefinition definition) |
根据指定的传播行为,该方法返回当前活动事务或创建一个新的事务。 |
void commit(TransactionStatus status) |
该方法提交给定的事务和关于它的状态。 |
void rollback(TransactionStatus status) |
该方法执行一个给定事务的回滚。 |
int getPropagationBehavior() |
该方法返回传播行为。Spring 提供了与 EJB CMT 类似的所有的事务传播选项。 |
int getIsolationLevel() |
该方法返回该事务独立于其他事务的工作的程度。 |
String getName() |
该方法返回该事务的名称。 |
int getTimeout() |
该方法返回以秒为单位的时间间隔,事务必须在该时间间隔内完成。 |
boolean isReadOnly() |
该方法返回该事务是否是只读的。 |
|
该方法返回该事务内部是否有一个保存点,也就是说,基于一个保存点已经创建了嵌套事务。 |
|
该方法返回该事务是否完成,也就是说,它是否已经提交或回滚。 |
boolean isNewTransaction() |
在当前事务时新的情况下,该方法返回 true。 |
boolean isRollbackOnly() |
该方法返回该事务是否已标记为 rollback-only。 |
void setRollbackOnly() |
该方法设置该事务为 rollback-only 标记。 |
并发下事务可能会产生的问题:
脏读:所谓脏读,就是指事务A读到了事务B还没有提交的数据,比如银行取钱,事务A开启事务,此时切换到事务B,事务B开启事务--取走100元,此时切换回事务A,事务A读 取的肯定是数据库里面的原始数据,因为事务B取走了100元并没有提交,数据库里面的账务余额肯定还是原始余额,这就是脏读。
不可重复读:是指在一个事务里面读取了两次某个数据,但读出来的数据不一致。还是以银行取钱为例,事务A开启事务--查出银行卡余额为1000元,此时切换到事务B,事务B 开启事务---取走100元---提交,数据库里面的余额变为900元,此时切换回事务A,事务A再查一次查出账户余额为900元,这样对事务A而言,在同一个事务内两次读取账户余 额数据不一致,这就是不可重复读。
幻读:是指在一个事务里面的操作中发现了未被操作的数据,比如学生信息。事务A开启事务--修改所有学生当天签到状况为false,此时切换到事务B,事务B开启事务--事务B 插入一条学生数据,此时切换回事务A,事务A提交的时候发现了一条自己没有修改过的数据,这就是幻读,就好像发生了幻觉一样,幻读出现的前提是并发的事务中有事 务发生了插入、删除操作。
事务的隔离级别:
事务隔离级别就是为了解决并发情况下产生的脏读、幻读、不可重复读的问题而诞生的。为什么要有事务隔离级别,因为事务隔离级别越高,在并发下产生的问题就越少,但同时付出的性能消耗也将越大,因此很多时候必须在并发性和性能之间做一个权衡。所以设立了集中事务隔离级别以便于让不同的项目可以根据自己项目的并发情况选择合适的事务隔离级别,对于在事务隔离级别之外会产生的并发问题,在代码中补偿。
5种隔离级别:
1、TeansactionDefinition.ISOLATION_DEFAULT:这是默认的隔离级别。
2、TeansactionDefinition.ISOLATION_READ_COMMITTED:表明能够阻止脏读,可以发生不可重复读和幻读。
3、TeansactionDefinition.ISOLATION_REDA_UNCOMMITTED:表明可以发生脏读、不可重复读和幻读。
4、TeansactionDefinition.ISOLATION_REPEATABLE_REDA:表明能阻止脏读和不可重复读,可以发生幻读。
5、TeansactionDefinition.ISOLATION_SERIALIZABLE:表明能阻止脏读、不可重复读和幻读。
补充:不是事务隔离级别设置的越高越好,事务隔离级别设置的越高,意味着是必要花手段去加锁用以保证事务的正确性,那么效率就要降低,因此实际开发中往往要在效
率和并发正确性之间做一个取舍,一般情况下会设置为READ_COMMITTED,此时避免了脏读,并发性也还不错。
事务的传播行为:
事务的传播行为规定了事务方法和事务方法发生嵌套调用时事务如何进行传播。
8种类型:
1、TransactionDefinition.PROPAGATION_MANDATORY:支持当前事务,如果不存在当前事务,则抛出一个异常。
2、TransactionDefinition.PROPAGATION_NESTED:如果存在当前事务,则在一个嵌套的事务中执行。
3、TransactionDefinition.PROPAGATION_NEVER:不支持当前事务,如果存在当前事务,则抛出一个异常。
4、TransactionDefinition.PROPAGATION_NOT_SUPPORTED:不支持当前事务,而总是执行非事务性。
5、TransactionDefinition.PROPAGATION_REQUIRED:支持当前事务,如果不存在事务,则创建一个新的事务。
6、TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新事务,如果存在一个事务,则把当前事务挂起。
7、TransactionDefinition.PROPAGATION_SUPPORTS:支持当前事务,如果不存在,则执行非事务性。
8、TransactionDefinition.PROPAGATION_DEFAULT:使用默认超时的底层事务系统,或者如果不支持超时则没有。