原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。
Spring并 不直接管理事务,只有当数据库支持事务时,Spring才支持事务
,Spring只不过简化了开发人员实现事务的步骤,Spring提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等 持久化机制所提供的相关平台框架的事务
来实现。
Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。此接口的内容如下:
三个方法分别是:
PlatformTransactionManager中有个getTransaction(TransactionDefinition var1)方法,这个方法返回的是TransactionStatus对象,然后程序根据返回的对象来获取事务状态,然后进行相应的操作。
几个方法分别是:
事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面,如图所示:
方法分别是:
传播行为: 事务的第一个方面是传播行为(propagation behavior),当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
这里以A业务和B业务之间如何传播事务为例说明:
①、PROPAGATION_REQUIRED : 默认值
,A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务。
②、PROPAGATION_SUPPORTS:A如果有事务,B将使用该事务;如果A没有事务,B将以非事务执行。
③、PROPAGATION_MANDATORY:A如果有事务,B将使用该事务;如果A没有事务,B将抛异常。
④、PROPAGATION_REQUIRES_NEW :如果A有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务。
⑤、PROPAGATION_NOT_SUPPORTED :如果A有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行。
⑥、PROPAGATION_NEVER :如果A有事务,B将抛异常;如果A没有事务,B将以非事务执行。
⑦、PROPAGATION_NESTED :A和B底层采用保存点机制,形成嵌套事务。
传播行为: 事务的第一个方面是传播行为(propagation behavior),当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
并发事务引起的问题:
①、脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
②、不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
③、幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
注意:不可重复读重点是修改,而幻读重点是新增或删除。
在 Spring 事务管理中,为我们定义了如下的隔离级别:
后端数据库默认
Spring 提供了两种方式实现事务: ①声明式,②编程式。
编程式事务: 允许用户在代码中精确定义事务的边界。
声明式事务: (基于AOP)有助于用户将操作与事务规则进行解耦。简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。
声明式事务管理只需要用到@Transactional 注解和@EnableTransactionManagement(开启事务管理功能)。它是基于 Spring AOP
实现的,并且通过注解实现,实现起来简单,对原有代码没有入侵性。
事务的切面有两个主要职责:
在’before’时,切面提供一个调用点,来决定被调用业务方法应该在正在进行事务的范围内运行,还是开始一个新的独立事务。
在’after’时,切面需要确定事务被提交,回滚或者继续运行。
@Transactional注解不起作用的集中情况:
①Transactional注解标注方法修饰符为非public时,@Transactional注解将会不起作用。
②在类内部调用调用类内部@Transactional标注的方法。这种情况下也会导致事务不开启。
③事务方法内部捕捉了异常,没有抛出新的异常,导致事务操作不会进行回滚。
④ 异常类型是不是unchecked异常。如果我想check异常也想回滚怎么办,注解上面写明异常类型即可。@Transactional(rollbackFor=Exception.class)