Spring 学习(五)--- 事务

问题 :

  • Spring 事务传播机制是怎么样的,在什么应用场景使用
  • 事务是什么
  • 我们使用的框架可能是Hibernate/JPA或者是Mybatis,都知道的底层是需要一个session/connection对象来帮我们执行操作的。要保证事务的完整性,我们需要多组数据库操作要使用同一个session/connection对象,而我们又知道Spring IOC所管理的对象默认都是单例的,这为啥我们在使用的时候不会引发线程安全问题呢?内部Spring到底干了什么?

概述

          事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。 使用Spring 事务的优势有 :

  • 一致的事务编程接口对应不同的事务API
  • 声明式事务
  • 提供简单的编程式事务API
  • 与 Spring DATA 对接

          Spring 事务可以通过两种方式实现事务,一种是  declarative transaction management(声明式事务) ,一种是program-matic transaction management (编程式事务),前者又可以通过两种形式来实现 XML 或是 javaConfig。

           本文将会使用 @Transctional 注解的形式来解释Spring 事务。

Spring 事务

声明式事务

         declarative transctional 事务主要运用到了AOP 的知识。

       The most important concepts to grasp with regard to the Spring Framework’s declarative transaction support are that this support is enabled via AOP proxies and that the transactional advice is driven by metadata (currently XML- or annotation-based). The combination of AOP with transactional metadata yields an AOP proxy that uses a TransactionInterceptor in conjunction with an appropriate PlatformTransactionManager implementation to drive transactions around method invocations.

         关于Spring Framework的声明式事务支持,最重要的概念是通过AOP代理启用此支持,并且事务性建议由元数据(当前基于XML或基于注释)驱动。 AOP与事务元数据的组合产生一个AOP代理,该代理使用TransactionInterceptor和适当的PlatformTransactionManager实现来驱动围绕方法调用的事务。

          这里所说的与AOP相关,可以通过官方文档的这张图来理解 :

tx 

         平时要是我们在使用spring事务的时候也可以通过Log看到代理的影子。

   PlatformTransactionManager 又是什么呢?为什么它可以驱动底层的数据完成调用事务的动作。我们可以从下图来了解Spring 事务的内部结构。

utf-8' '20160324011156424

            可以看到Spring 事务管理接口提供了多种底层实现,由 PlatformTransactionManager  平台事务管理者来管理。下面讲一下使用,再通过使用来介绍知识点。

使用

           例子中使用的是Spring MVC框架 ,以下是一个Service 的方法实现,mStudentDao 进行的操作都是与数据库相关的。

@Transactional(propagation= Propagation.REQUIRED,isolation= Isolation.DEFAULT,readOnly = true)
@Service
public class BeanTestServiceImpl implements BeanTestService {
    @Autowired
    private StudentDao mStudentDao;

    @Transactional(propagation= Propagation.REQUIRED,isolation= Isolation.DEFAULT,readOnly = false)
    @Override
    public void updateStuName(int id, String name) throws Exception {
        mStudentDao.updateStuName(id,"pig");
        int i = 5/0;
        mStudentDao.updateStuName(id,"dog");

    }
}

         方法中的第二句明显会抛出异常,查看我们的数据库会发现第一句的更新语句发生了回退(rollback),示例很简单。我们看一下@Transcational 中的三个属性 : propagation , isolation , readOnly .

          使用了AOP ,那么注解应该就要在应用在public 的方法上上,假如注解不是运用在public(例如protected或是private)上,

        When you use proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactionalannotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. If you need to annotate non-public methods, consider using AspectJ (described later).

          默认的@Transaction 设置如下 :

  • The propagation setting is PROPAGATION_REQUIRED.

  • The isolation level is ISOLATION_DEFAULT.

  • The transaction is read-write.

  • The transaction timeout defaults to the default timeout of the underlying transaction system, or to none if timeouts are not supported.

  • Any RuntimeException triggers rollback, and any checked Exception does not.

           其中propagation setting  和 isolation level  后面会进一步解释。我们看一下这个还有哪些属性可以设置。

ta

         后面几个属性和rollback有关,指定在什么情况下回退。

       

事务的隔离级别

           这是 propagation 的选项。

  • PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
  • PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
  • PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
  • PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

        下面我们学习这几个选项。

Understanding PROPAGATION_REQUIRED(默认)

         需要注意的地方:默认情况下,一个新的事务加入外部作用域,默认忽略会本地隔离级别,超时值或只读标志(如果有)。这样的场景就是当前的事务是read-only,新来了一个外部的事务是 read-write 的。

       By default, a participating transaction joins the characteristics of the outer scope, silently ignoring the local isolation level, timeout value, or read-only flag (if any). Consider switching the validateExistingTransactions flag to true on your transaction manager if you want isolation level declarations to be rejected when participating in an existing transaction with a different isolation level. This non-lenient mode also rejects read-only mismatches (that is, an inner read-write transaction that tries to participate in a read-only outer scope).

   出处

        它的使用,引用官方文档中的一段叙述来说明 :

     PROPAGATION_REQUIRED enforces a physical transaction, either locally for the current scope if no transaction exists yet or participating in an existing 'outer' transaction defined for a larger scope. This is a fine default in common call stack arrangements within the same thread (for example, a service facade that delegates to several repository methods where all the underlying resources have to participate in the service-level transaction).

 

Understanding PROPAGATION_REQUIRES_NEW

          这是创建一个新的事务。完全独立于上一个事务的操作。

PROPAGATION_NESTED

         PROPAGATION_NESTED uses a single physical transaction with multiple savepoints that it can roll back to. Such partial rollbacks let an inner transaction scope trigger a rollback for its scope, with the outer transaction being able to continue the physical transaction despite some operations having been rolled back. This setting is typically mapped onto JDBC savepoints, so it works only with JDBC resource transactions.

        PROPAGATION_NESTED设置了多个保存位置,让它遇到异常时只回滚一小部分,而不是全部,它只能在  JDBC resource transactions中使用。

        关于Understanding PROPAGATION_REQUIRED 和 Understanding PROPAGATION_REQUIRES_NEW的应用场景,可以看到下面的例子 :

public class FooService {
    private Repository repo1;
    private Repository repo2;

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void provideService() {
        repo1.retrieveFoo();
        repo2.retrieveFoo();
    }
}

       我们可以写一个测试来对比它们两者表现的差异。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests {

    private @Autowired TransactionManager transactionManager;
    private @Autowired FooService fooService;

    @Test
    public void testProvideService() {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        fooService.provideService();
        transactionManager.rollback(status);
        // assert repository values are unchanged ... 
}
  • Requires new we would expect fooService.provideService() was NOT rolled back since it created it's own sub-transaction.

  • Required we would expect everything was rolled back and backing store unchanged.

      例子来自于 Stack

isolation 隔离级别

       隔离级别看这篇文章 :Mysql学习

参考资料

猜你喜欢

转载自www.cnblogs.com/Benjious/p/10437823.html