• 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:因作者能力有限,如有误还请谅解;

  • 相关阅读:
    PHP UPLOAD 实现附件上传
    迁移服务器小记
    php TP5.1 将session 存入Redis
    js 精度问题的处理方法
    Aspect 浏览器插件
    JavaScript 构建工具 的发展和变化
    js input 保留1位小数
    如何使用adb 安装 apk应用
    php精度计算问题
    [Typescript] 89. Hard Currying 1
  • 原文地址:https://www.cnblogs.com/WHL5/p/9076335.html
Copyright © 2020-2023  润新知