zoukankan      html  css  js  c++  java
  • Spring框架深入(三)--事务

    一、事务的概念

      1、事务是什么

       (1)、作为单个逻辑工作单元执行的一系列操作(一组SQL会成为一个事务),是并发控制的单位,要么全部成功,要么全部失败

       (2)、如银行转账(需要两个update)/网上商城购物

      2、事务的特征

       (1)、原子性:所有的操作会被看成一个逻辑单元,要么全部成功,要么全部失败

       (2)、一致性:事务在完成时,必须使所有的数据都保持一致状态,保证数据的完整性

       (3)、隔离性:与并发有关,事务之间的相互影响—隔离级别

       (4)、持久性:事务结束后,结果是可以固化的

    二、事务隔离

      

      1、事务隔离用于处理事务之间的并发问题

      2、事务的隔离级别

       (1)、未授权读取

       (2)、授权读取

       (3)、可重复读取

       (4)、序列化:隔离级别最高的

      3、事务隔离的实现:

       (1)、悲观锁:基于数据库的锁,不能操作数据

        a、悲观锁是读取的时候为后面的更新加锁,之后再来的读操作都会等待。这种是数据库锁。

        b、悲观锁是数据库实现,他阻止一切数据库操作。

        c、在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;

       (2)、乐观锁:不同的事务可以看到同一对象的不同历史版本

        a、乐观锁是一种思想,是基于版本号机制的,具体实现是,表中有一个版本字段,第一次读的时候,获取到这个字段。处理完业务逻辑开始更新的时候,需要再次查看该字段的值是否和第一次的一样。如果一样更新,反之拒绝。之所以叫乐观,因为这个模式没有从数据库加锁。

        b、乐观锁适用于多读的应用类型,这样可以提高吞吐量;

        c、在实际生产环境中,如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择使用乐观锁。

    三、B/S中的事务

      1、一个请求对应一个业务,一个业务其实就应该是一个事务

      2、一个请求对应一个事务

      3、一个事务----MyBatis中的事务与sqlSession相关

      4、一个请求对应着启动一个线程,一个线程对应一个事务

       (1)、当前请求线程所执行的所有操作都是属于同一个事务的,使用的是同一个sqlSession

       (2)、dao的所有操作应该是基于同一个sqlSession,这些操作才构成一个事务;

      5、一个线程对应着同一个sqlSession:

        如何让一个线程中得到的sqlSession对象是同一个呢?使用ThreadLocal

      6、ThreadLocal

       (1)、当前线程变量:key/value

       (2)、将sqlSession放入线程上下文空间,线程会执行请求要做的所有方法(很多个dao操作),每次的dao操作所使用的sqlSession都从当前线程上下文取得;

       (3)、使用原理/步骤:

        a、每个dao在执行的时候,会使用getSqlSession来获得会话

        b、判断当前线程中是否有session,如果有的话,就用当前线程的session。如果没有的话,就会创建session放入当前线程,同时返回session;

        c、继续执行下一个dao操作的时候,因为是属于同一个请求线程的,所以可以从当前线程里拿到同一个session,从而形成事务的概念。

    /*
     * 当前线程上下文空间
     * 将session放入当前线程变量中
     * 如果当前线程有该对象,就直接拿来用
     * 如果没有,才去新建对象
     * */
    public class SessionFactoryUtil {
        private static ThreadLocal threadLocal = new ThreadLocal();    
        public static SqlSession getSqlSession() {    
            SqlSession session = (SqlSession) threadLocal.get();
            if(session!=null) {
                return session;
            }
            else{
                session=new SqlSession();
                threadLocal.set(session);
                return session;
            }
        }
    }
    public void run() {
        //这两个dao操作时属于同一个事务的,也就是dao操作所使用的sqlSession是同一个
        userDao.buy();
        productDao.updatePruduct();
    }

      7、B/S中要实现事务,需要将sqlSession放入ThreadLocal(当前线程上下文)中

       通过servletFilter实现请求到达的时候,创建session放入ThreadLocal

    四、Spring中的事务

      1、事务其实是一个切面的存在,只需要在Spring中配置AOP就可以实现事务了

      2、AOP

       (1)、核心业务:自己编写

       (2)、切面:事务这个切面Spring已经提供了实现:不需要自己编写

        a、Spring已经提供了实现事务的通知,配置为bean

        b、事务管理平台:确定事务切面用在哪个平台

        c、事务策略定义:事务的属性(隔离级别、传播性)

        d、事务状态

      3、在Spring中实现事务:通过配置AOP实现

       (1)、将Spring提供的通知类配置到核心业务线

       (2)、基于注解进行配置

       (3)、基于AOP的配置文件进行配置

      

      4、PlatformTransactionManager

       确定要做的事务是基于哪个平台(JDBC、Hibernate、MyBatis、JPA)

      

    五、Spring+MyBatis事务管理

      1、Spring+MyBatis集成后,默认每个操作都是产生一个新的SqlSession,不构成事务概念,每个操作就是一个独立的事务

      2、事务都是基于service层

      3、Spring中事务AOP的配置

       (1)、首先,我们这儿有一个Author类(实现类get,set方法),以及AuthorMapper接口

    public class Author {
        private int id;
        private String username;
        private String password;
        private String email;
        private String bio;
    }
    public interface AuthorMapper {
        public Author findById(int id);
    
        public void insertAuthor(Author aut);
    }

        接口中有两个方法,分别是查找所有和插入

        如果我们就这样运行,他就会创建两个SqlSession分别处理两个方法;

       (3)、在核心配置文件中进行事务AOP的配置

    <!-- 
        AOP 事务配置 
            核心业务:service——已完成
            切面bean:Spring提供支持类,进行配置
            建立切入点aop关联,进行配置
    -->
        
    <!-- 配置事务管理器:切面的一部分 -->
    <bean id="transManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
        
    <!-- 
        配置事务策略状态:切面的一部分 
        事务通知的配置
    -->
    <tx:advice id="txAdvice" transaction-manager="transManager">
        <tx:attributes>
            <!-- 
                对应的方法与事务的使用 
                REQUIRED:在事务中执行,如果事务不存在,则会重新创建一个
                SUPPORTS:使用当前的环境执行,如果当前存在事务,则会使用这个事务,如果当前没有这个事务,则不使用事务
            -->
            <tx:method name="add*" propagation="REQUIRED"/>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="del*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>
        
    <!-- 核心业务与事务关联起来 -->
    <aop:config>
        <!-- 切入点 -->
        <aop:pointcut expression="execution(* service.*.*.*(..))" id="transPointCut"/>
        <!-- 关联操作 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="transPointCut"/>
    </aop:config>    

        如果是对于hibernate而言,到这儿就已经可以实现事务了。但是,对于MyBatis而言,还需要继续进行配置,有两种配置方法;

        a、基于注解实现事务

    <!-- 配置注解驱动,标注@Transactional的类和方法都具有事务性 -->
    <tx:annotation-driven transaction-manager="transManager"/>

        只需要在信心配置文件中配置注解驱动,并且在实现类上加上@Transactional就可以了;

    @Transactional
    @Override
    public void doFindAndInsert() {
        Author aut=new Author();
        aut.setBio("test");
        aut.setEmail("test");
        aut.setPassword("test");
        aut.setUsername("test");
        authorMapper.insertAuthor(aut);
                    
        //报错的时候,回滚
        int i=5/0;
                    
        Author author = authorMapper.findById(1);
        System.out.println(author);
        return null;    
    }

        运行结果:

        

        b、基于事务管理模板实现事务

         配置一个支持事务的模板bean

    <!-- 
        基于事务模板
        用于支持事务的模板bean
     -->
    <bean id="txTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <constructor-arg type="org.springframework.transaction.PlatformTransactionManager" ref="transManager"></constructor-arg>
    </bean>

         实现类:

    public void doFindAndInsert() {
        txTemplate.execute(new  TransactionCallback<Integer>() {
    
            @Override
            public Integer doInTransaction(TransactionStatus arg0) {
                // TODO Auto-generated method stub
                Author aut=new Author();
                aut.setBio("test");
                aut.setEmail("test");
                aut.setPassword("test");
                aut.setUsername("test");
                authorMapper.insertAuthor(aut);
                    
                //报错的时候,回滚
                int i=5/0;
                    
                Author author = authorMapper.findById(1);
                System.out.println(author);
                return null;
            }
        });
    }

         运行结果:

        

    PS:因作者能力有限,如有误还请谅解;

  • 相关阅读:
    无限维
    黎曼流形
    why we need virtual key word
    TOJ 4119 Split Equally
    TOJ 4003 Next Permutation
    TOJ 4002 Palindrome Generator
    TOJ 2749 Absent Substrings
    TOJ 2641 Gene
    TOJ 2861 Octal Fractions
    TOJ 4394 Rebuild Road
  • 原文地址:https://www.cnblogs.com/WHL5/p/9076335.html
Copyright © 2011-2022 走看看