Spring框架深入--事务

Posted IVEGOTNOIDEA

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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:因作者能力有限,如有误还请谅解;

以上是关于Spring框架深入--事务的主要内容,如果未能解决你的问题,请参考以下文章

阿里大牛:深入分析spring事务传播行为

8 -- 深入使用Spring -- 6...2 Spring支持的事务策略

深入理解 Spring 事务:入门使用原理

深入理解java:4.2. 框架编程之Spring框架的设计理念

8 -- 深入使用Spring -- 0...

数据库事务深入分析,最全的BAT大厂面试题整理