声明式事务
1 编程式事务
由程序员编写事务控制代码
例子 OpenSessionInView就是编程式事务
2 声明式事务
事务控制代码已经由spring写好,程序员只需要声明出哪些方法需要进行事务控制和如何进行事务控制
3 声明式事务都是针对于ServiceImpl类下的方法
声明式事务都是针对于ServiceImpl类下方法的
事务管理器基于通知(advice)的
六 声明式事务中属性解释
1 name="" 哪些方法需要有事务控制,里面支持*通配符
2 readonly="boolean" 是否是只读事务
如果是true,告诉数据库此事务为只读事务,一般不会加入事务锁,会对性能有一定提升,对于查询方法,建议使用此属性
数据库对于提交事务和只读事务的处理会有所不同,对于提交事务,数据库会添加事务锁等一系列管理机制,而只读事务相反
如果是false,则该事务为需要提交的事务,建议在新增,删除,修改操作下使用,默认值为false
事务属性
在某些场合的声明事务时,用到了@Transactional(readOnly = false, propagation = Propagation.REQUIRED)。
中间的参数readOnly,propagation我们称为事务属性。它就是对事务的基本配置。事务属性有五个方面:传播行为,隔离级别,事务超时时间,回滚规则,是否只读。
由属性接口TransactionDefinition可以看到,可返回四个基本事务属性:
public interface TransactionDefinition { int getPropagationBehavior(); // 传播行为。 int getIsolationLevel(); // 隔离级别。事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。 int getTimeout(); // 事务必须在多少秒内完成。 boolean isReadOnly(); // 事务是否只读。事务管理器能够根据这个返回值进行优化,确保事务是只读的 }
这篇先谈事务的隔离级别(isolation level)
数据库是要被广大客户所共享访问的,那么在数据库操作过程中很可能出现以下几种不确定情况。
更新丢失(Lost update)
两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了。比如CMS系统中,两个同时打开一篇文章进行修改,一个人先保存,另一个人后保存,后保存的就覆盖了先保存的那个人的内容,这就造成更新丢失。
这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。在并发事务处理带来的问题中,“更新丢失”通常应该是完全避免的。但防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决,因此,防止更新丢失应该是应用的责任。
脏读(Dirty reads)
一个事务读取到了另一个事务未提交的数据操作结果。这是相当危险的,因为很可能所有的操作都被回滚。
不可重复读(Non-repeatable Reads)
一个事务对同一行数据重复读取两次,但是却得到了不同的结果。比如事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。又叫虚读。
幻读(Phantom Reads)
事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据(这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。
不可重复读的重点是修改某个记录字段,幻读的重点在于新增或者删除记录。
对于前者, 只需要锁住满足条件的记录。对于后者, 要锁住满足条件及其相近的记录。
“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。
事务隔离级别
为了避免上面出现的几种情况,在标准SQL规范中,定义了4个事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
未提交读取(Read Uncommitted)
Spring标识:ISOLATION_READ_UNCOMMITTED。允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
已提交读取(Read Committed)
Spring标识:ISOLATION_READ_COMMITTED。允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
可重复读取(Repeatable Read)
Spring标识:ISOLATION_REPEATABLE_READ。禁止不可重复读取和脏读取,但是有时可能出现幻读数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
序列化(Serializable)
Spring标识:ISOLATION_SERIALIZABLE。提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
Spring中同时提供一个标识:ISOLATION_DEFAULT。表示使用后端数据库默认的隔离级别。大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。MySQL的默认隔离级别是Repeatable read。
以上是isolation,现在我们介绍事务传播级别propagation
propagation取值如下:
REQUIRED: (默认值) 如果当前有事务,就在事务中执行,如果当前没有事务,新建事务
SUPPORTS: 如果当前有事务,就在事务中执行,如果当前没有事务,就在非事务状态下执行
MANDATORY: 必须在事务内部执行,如果当前有事务,就在事务中执行,如果没有事务,则就直接报错
Required_NEW: 必须在事务中执行,如果当前没有事务,新建事务,如果当前有事务,把当前事务挂起
NOT_SUPPORTED: 必须在非事务下执行,如果当前没有事务,就正常执行,如果当前有事务,会把当前事务挂起
NEVER: 必须在非事务状态下执行,如果当前没有事务,正常执行,如果当前有事务,则报错
NESTED: 必须事务状态下执行,如果没有事务,新建事务,如果当前有事务,创建一个嵌套事务