spring(六):事务

事务特性ACID

  • 原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做;
  • 一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态,即数据完整性约束没有被破坏。
  • 隔离性(Isolation):并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性;
  • 持久性(Durability):事务一旦执行成功,它对数据库的数据的改变必须是永久的,不会因比如遇到系统故障或断电造成数据不一致或丢失。

常见的问题

  • 丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的
  • 脏读:一个事务看到了另一个事务未提交的更新数据
  • 不可重复读:在同一事务中,多次读取同一数据却返回不同的结果(也就是有其他事务更改了这些数据)
  • 幻读:一个事务在执行过程中读取到了另一个事务已提交的插入数据(即在第一个事务开始时读取到一批数据,但此后另一个事务又插入了新数据并提交,此时第一个事务又读取这批数据但发现多了一条,即好像发生幻觉一样)

事务隔离级别

  • 未提交读(Read Uncommitted):最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读;
  • 提交读(Read Committed):一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不可能可能出现丢失更新、脏读,但可能出现不可重复读、幻读;
  • 可重复读(Repeatable Read):保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,可能可能出现丢失更新、脏读、不可重复读,但可能出现幻读;
  • 序列化(Serializable):最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读。

隔离级别越高,数据库事务并发执行性能越差,能处理的操作越少。在实际项目开发中为了考虑并发性能一般使用提交读隔离级别,它能避免丢失更新和脏读,尽管不可重复读和幻读不能避免,但可以在可能出现的场合使用悲观锁或乐观锁来解决这些问题。

事务管理器

Spring框架支持事务管理的核心是事务管理器,通过实现策略接口PlatformTransactionManager支持各种数据访问框架的事务管理。

Spring 支持两种类型的事务管理:

  • 编程式事务管理 :这意味着你在编程的帮助下有管理事务。这给了你极大的灵活性,但却很难维护。
  • 声明式事务管理 :这意味着你从业务代码中分离事务管理。你仅仅使用注释或 XML 配置来管理事务。
public interface PlatformTransactionManager {
    // 根据给定的TransactionDefinition类型参数获取一个已经激活的事务或创建一个新的事务
    // 返回值TransactionStatus对象代表了当前事务的状态
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    // 提交TransactionStatus参数代表的事务
    void commit(TransactionStatus var1) throws TransactionException;
    // 回滚TransactionStatus参数代表的事务
    void rollback(TransactionStatus var1) throws TransactionException;
}
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 = 1; // 表明可以发生误读、不可重复读和虚读。
    int ISOLATION_READ_COMMITTED = 2;   // 表明能够阻止误读;可以发生不可重复读和虚读。
    int ISOLATION_REPEATABLE_READ = 4;  // 表明能够阻止误读和不可重复读;可以发生虚读。
    int ISOLATION_SERIALIZABLE = 8;     // 表明能够阻止误读、不可重复读和虚读。
    
    int TIMEOUT_DEFAULT = -1;
    
    int getPropagationBehavior();   // 获取事务传播行为
    int getIsolationLevel();    // 获取事务隔离级别
    int getTimeout();   // 获取事务超时时间
    boolean isReadOnly();   // 获取事务是否是只读的
    @Nullable
    String getName();   // 获取事务名字
}  
public interface TransactionStatus extends SavepointManager, Flushable {
    boolean isNewTransaction(); // 当前事务状态是否是新事务
    boolean hasSavepoint(); // 当前事务是否有保存点
    void setRollbackOnly(); // 设置当前事务应该回滚
    boolean isRollbackOnly();   // 当前事务是否应该回滚
    void flush();   // 刷新底层会话中的修改到数据库
    boolean isCompleted();  // 当前事务否已经完成
}

声明式事务处理的实现分析

1499639-20190722215300512-466282376.png

声明式事务的实现就是通过环绕增强的方式,在目标方法执行之前开启事务,在目标方法执行之后提交或者回滚事务。

  1. 读取和处理在IoC容器中配置的事务处理属性,并转化为事务处理幼的数据结构(TransactionAttribute)(使用通知器TransactionAttributeSourceAdvisor来完成事务处理属性的处理,和TransactionProxyFactoryBean拦截下来的事务方法的处理结合起来)
  2. 实现统一的事务处理过程,包含处理事务配置属性和与线程绑定完成事务处理的过程。(TransactionInfo和TransactionStatus记录和传递相关执行场景)
  3. 底层的事务处理实现。即PlatformTransactionManager接口的具体实现。
事务处理拦截器的配置
// TransactionProxyFactoryBean  
    @Override
    // 在IoC容器完成bean的依赖注入时,通过bean的初始化方法被调用
    // 自此,transactionInterceptor配置被启动并成为通知器Advisor的一部分
    protected Object createMainInterceptor() {
        ...
        if (this.pointcut != null) {
            return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
        }
        else {
            // Rely on default pointcut.
            return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
        }
    }
// AbstractSingletonProxyFactoryBean
    // 是事务处理完成aop配置的地方
    public void afterPropertiesSet() {
        // 检测bean
        ...else {
            ...
            // 创建代理工厂对象
            ProxyFactory proxyFactory = new ProxyFactory();
            // 为ProxyFactory生成代理对象、配置通知器、设置代理接口
            // 自此,拦截器被配置进代理对象中
            proxyFactory.addAdvisor(...);
            ...
            proxyFactory.setInterfaces(...);
            this.postProcessProxyFactory(proxyFactory);
            this.proxy = proxyFactory.getProxy(this.proxyClassLoader);
        }
    }
事务处理配置的读入

在TransactionInterceptor进行依赖注入时,其父类TransactionAspectSupport设置TransactionAttribute。

    public void setTransactionAttributes(Properties transactionAttributes) {
        NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
        tas.setProperties(transactionAttributes);
        this.transactionAttributeSource = tas;
    }
    public void setProperties(Properties transactionAttributes) {
        TransactionAttributeEditor tae = new TransactionAttributeEditor();
        Enumeration<?> propNames = transactionAttributes.propertyNames();
        while (propNames.hasMoreElements()) {
            String methodName = (String) propNames.nextElement();
            String value = transactionAttributes.getProperty(methodName);
            tae.setAsText(value);
            TransactionAttribute attr = (TransactionAttribute) tae.getValue();
            addTransactionalMethod(methodName, attr);
        }
    }
事务处理拦截器的设计与实现
// TransactionInterceptor   
    @Override
    @Nullable
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // Work out the target class: may be {@code null}.
        // The TransactionAttributeSource should be passed the target class
        // as well as the method, which may be from an interface.
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

        // Adapt to TransactionAspectSupport's invokeWithinTransaction...
        return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
    }
// TransactionAspectSupport 
    @Nullable
    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
            final InvocationCallback invocation) throws Throwable {

        // If the transaction attribute is null, the method is non-transactional.
        TransactionAttributeSource tas = getTransactionAttributeSource();
        final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
        // 根据事务属性决定采用的事务处理器
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
            // Standard transaction demarcation with getTransaction and commit/rollback calls.
            // 创建事务 ★
            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

            Object retVal;
            try {
                // This is an around advice: Invoke the next interceptor in the chain.
                // This will normally result in a target object being invoked.
                // 沿着拦截器链进行
                retVal = invocation.proceedWithInvocation();
            }...
            finally {
                // 事务信息更新
                cleanupTransactionInfo(txInfo);
            }
            // 提交事务<<PlatformTransactionManager>>
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }

        else {
            final ThrowableHolder throwableHolder = new ThrowableHolder();

            // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
            try {
                Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
                    TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                    try {
                        return invocation.proceedWithInvocation();
                    }
                    catch (Throwable ex) {
                        if (txAttr.rollbackOn(ex)) {
                            // A RuntimeException: will lead to a rollback.
                            ...
                        }
                        else {
                            // A normal return value: will lead to a commit.
                            ...
                        }
                    }
                    finally {
                        cleanupTransactionInfo(txInfo);
                    }
                });

                // Check result state: It might indicate a Throwable to rethrow.
                ...
                return result;
            }
            ...
        }
    }

    protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
            @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

        // If no name specified, apply method identification as transaction name.
        if (txAttr != null && txAttr.getName() == null) {
            txAttr = new DelegatingTransactionAttribute(txAttr) {
                @Override
                public String getName() {
                    return joinpointIdentification;
                }
            };
        }

        TransactionStatus status = null;
        if (txAttr != null) {
            if (tm != null) {
                // 获取事务对象,创建TransactionStatus对象
                status = tm.getTransaction(txAttr);
            }
            ...
        }
        // 把TransactionStatus设置到TransactionInfo中,并绑定到当前线程
        return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
    }

转载于:https://www.cnblogs.com/angelica-duhurica/p/11195133.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值