Spring事务管理(基础概念)

一般我们说事务,都是用一个术语来描述事务:ACID。也就事务的四个特性:原子性(Atomic),一致性(Consistent),隔离性(Isolated),持久性(Durable).
原子性:事务是由一个或多个活动所组成的一个工作单元(比如一个方法里有增删改)。原子性保证这个事务中所有操作要么全部完成,要么全部不发生。也就是说如果所有活动都完成了,事务就成功了,如果有一个失败了,事务就失败了并且进行回滚。
一致性:一旦事务完成(不管成功还是失败),系统就必须确保所建模的业务必须处于一致的状态(比如,取钱100成功,银行卡就少100。失败,银行卡里钱就不少)。现实的数据不应该被损坏。
隔离性:多个用户同时操作相同数据,但是用户之间的操作不应该相互影响。因此事务应该被彼此隔离,避免发生同步读写相同数据的情况,造成用户读写数据混乱,造成数据损坏。
持久性:事务一旦完成,数据就应该持久化,一般会存储到数据库或者其他形式的持久化存储中。

Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给JTA或者其他持久化机制所提供的平台相关的事务实现。Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager(在spring-tx.jar包中)。通过这个接口,spring为各个平台提供了对应的事务管理器。如:org.springframework.jdbc.datasource.DataSourceTransactionManager用于对JDBC抽象的支持,也可用于使用Ibtais进行持久化。
PlatformTransactionManager接口代码如下:

package org.springframework.transaction;
public interface PlatformTransactionManager {
    //由TransactionDefinition得到TransactionStatus对象
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    //事务提交
    void commit(TransactionStatus status) throws TransactionException;
    //事务回滚
    void rollback(TransactionStatus status) throws TransactionException;
}

在spring中有两种事务形式,编码式和声明式。一般我们采用声明式,因为编码式对代码有侵入,声明式解耦性比较好。
声明式事务是通过事务属性(transaction attribute)来定义的。事务属性包含了5个方面,传播行为,隔离级别,事务超时,回滚规则,是否只读?
这里写图片描述
上面讲到的事务管理器接口PlatformTransactionManager通过getTransaction(TransactionDefinition definition)方法来得到事务,这个方法里面的参数是TransactionDefinition类,这个类就定义了一些基本的事务属性。

package org.springframework.transaction;

import java.sql.Connection;

public interface TransactionDefinition {

    int PROPAGATION_REQUIRED = 0;

    int PROPAGATION_SUPPORTS = 1;

    int PROPAGATION_MANDATORY = 2;

    int PROPAGATION_REQUIRES_NEW = 3;

    int PROPAGATION_NOT_SUPPORTED = 4;

    int PROPAGATION_NEVER = 5;

    int PROPAGATION_NESTED = 6;

    int ISOLATION_DEFAULT = -1;

    int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;

    int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;

    int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;

    int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;

    int TIMEOUT_DEFAULT = -1;
     // 获取事务的传播行为
    int getPropagationBehavior();
    // 获取事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
    int getIsolationLevel();
    // 获取事务的超时时间
    int getTimeout();
     // 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的
    boolean isReadOnly();

    String getName();

}

事务的传播行为

事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:

PROPAGATION_REQUIRED 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务

PROPAGATION_SUPPORTS 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行

PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常

PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager

PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager

PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常

PROPAGATION_NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务

事务的隔离级别:
事务的第二个维度就是隔离级别(isolation level)。隔离级别定义了一个事务可能受其他并发事务影响的程度。
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致一下的问题。

脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。

在理想情况下,事务之间是完全隔离的,从而防止问题的发生,但是完全的隔离会导致性能下降。因为这会涉及到锁定数据库的记录,侵占性的锁会阻碍并发性,要求事务必须等待来完成各自的工作。所以考虑的性能问题,所以程序有时需要在事务上有一定的灵活性。因此有多个事务隔离级别。
ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的

事务的第三个特性是它是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。

为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。

事务五边形的最后一个方面是一组规则,这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的)
但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。

上面讲到的调用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一个实现,源码如下:

package org.springframework.transaction;

public interface TransactionStatus extends SavepointManager {

    //是否是新事务
    boolean isNewTransaction();
     // 是否有恢复点
    boolean hasSavepoint();
    // 设置为只回滚
    void setRollbackOnly();
    // 是否为只回滚
    boolean isRollbackOnly();
    //清楚session
    void flush();
    // 是否已完成
    boolean isCompleted();

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值