《spring学习》之事务、异常、传播机制

何为数据库事务?

将多步数据操作(增删改)组成一个整体,执行时要嘛整体成功,要嘛整体失败。
四个特性:原子性,一致性,隔离性,持久性【概念问题,直接百度】

数据库并发问题

一个事务包含了多步数据库操作,当多个客户端同时执行多个事务时如果没有采取必要的隔离措施可能会发生并发问题,常见的并发问题包括脏读,不可重复读,幻象读,第一类丢失更新,第二类丢失更新【概念,百度】

数据库如何解决并发问题

数据库通过锁机制的方式来解决并发问题,但是锁的DML操作过于繁琐,所以数据库为用户提供了自动的锁机制,就是事务的隔离级别,只要用户指定了合适的隔离级别,数据库就会分析事务中的SQL语句并自动添加合适的锁
常见的隔离级别如下:

image.png

spring对事务管理的支持

spring对事务管理都是通过调用数据库的提交回滚,设置数据库的隔离级别来达到解决问题的,这不是spring的能力,只是spring对jdbc的封装。除了spring提供的传播机制。
spring事务管理主要是通过三个接口来实现:PlatformTransactionManager,TransactionDefinition,TransactionStatus

PlatformTransactionManager

事务的管理器,spring为各种持久层框架提供了不同的事务管理器实现类。用的时候根据不同的持久层采用不同的事务管理器。例如使用Mybatis,则使用org.springframework.jdbc.datasource.DataSourceTransactionManager


image.png
TransactionDefinition

定义了事务的事务隔离级别、事务传播、事务超时、是否只读


image.png
TransactionStatus

定义了事务的具体运行状态


image.png

事务的配置(以mybatis为例)

XML事务配置

1.配置数据源

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
        <!-- 初始化连接大小 -->
        <property name="initialSize" value="${initialSize}"></property>
        <!-- 连接池最大数量 -->
        <property name="maxActive" value="${maxActive}"></property>
        <!-- 连接池最大空闲 -->
        <property name="maxIdle" value="${maxIdle}"></property>
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="${minIdle}"></property>
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="${maxWait}"></property>
    </bean>

2.配置事务管理器

<bean id="txManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

3.采用aop的方式为service层注入事务

<apo:config>
        <!--定义切面,哪些方法需要注入事务-->
        <aop:pointcut id="serviceMethod" expression="execution(* com.xzy.service.*(..))"></aop:pointcut>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"></aop:advisor>
    </apo:config>
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="*" read-only="false" propagation="REQUIRED" timeout="-1"/> <!--是否只读,事务传播机制,事务超时间,还可以配置其他选项自己百度-->
        </tx:attributes>
    </tx:advice>

注解方式

1.设置数据源(同xml方式)
2.配置事务管理器(同xml方式)
3.添加注解事务支持

<tx:annotation-driven transaction-manager="txManager"/>

4.在service实现类上加入注解@Transactional
@Transactional可以加在接口定义、接口方法、实现类、实现类方法上,但一般建议定义在实现类和实现类方法上

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class BookService {
    // do someing...
}
image.png

spring事务在发生异常时的回滚

准备Service类

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class StudentService {
    @Autowired
    private StudentDao studentDao;

    public List<Student> list(){
        List<Student> list = studentDao.getAllStudent();
        System.out.println(list);
        return list;
    }

    public void update(){
        Student student = new Student();
        student.setId(1);
        student.setName("test_" + System.currentTimeMillis());
        studentDao.update(student);
    }
}
@Service
@Transactional(propagation = Propagation.REQUIRED)
public class BookService {

    @Autowired
    private BookDAO bookDAO;

    @Autowired
    private StudentService studentService;

    public Book initBook(){
        Book book = new Book();
        book.setPrice(122);
        book.setPublishDate(new Date());
        book.setTitle("书");
        return book;
    }


    public Book getBookById1(){
        Book book = new Book();
        book.setId(1);
        book.setTitle("Java编程思想_" + System.currentTimeMillis());
        book.setPrice(199.9);
        book.setPublishDate(new Date());
        return book;
    }

    public void add(){
        bookDAO.add(initBook());
    }


    public void update(){
        bookDAO.update(getBookById1());
    }


    public void test1(){
        // 1 / 0 发生异常 这里不做处理 aop会自动回滚
        studentService.update();
        bookDAO.add(initBook());
        int i = 1 / 0 ; // 1/0会发生报错
    }


    public void test2(){
        //发生异常时手动catch后事务不能回滚
        try{
            studentService.update();
            bookDAO.add(initBook());
            int i = 1 / 0 ;
        }catch (Exception e){
            System.out.println("手动处理异常");
        }
    }

    public void test3(){
        //捕获异常后但是又抛出这样事务能回滚
        try{
            studentService.update();
            bookDAO.add(initBook());
            int i = 1 / 0 ;
        }catch (Exception e){
            System.out.println("手动处理异常");
            throw new RuntimeException("抛出"); //默认情况下对Error和RuntimeException及其子类进行回滚
        }
    }

    public void test4(){
        //手动回滚事务,这样即使做了异常处理我们手动可以进行回滚
        try{
            studentService.update();
            bookDAO.add(initBook());
            int i = 1 / 0 ;
        }catch (Exception e){
            System.out.println("手动处理异常");
            System.out.println("手动回滚事务");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //手动回滚事务
        }
    }
出现异常且不catch

当service抛出异常时自动回滚

 public void test1(){
        // 1 / 0 发生异常 这里不做处理 aop会自动回滚
        studentService.update();
        bookDAO.add(initBook());
        int i = 1 / 0 ; // 1/0会发生报错
    }
出现异常并catch

如果手动捕获异常处理,则spring不能回滚

 public void test2(){
        //发生异常时手动catch后事务不能回滚
        try{
            studentService.update();
            bookDAO.add(initBook());
            int i = 1 / 0 ;
        }catch (Exception e){
            System.out.println("手动处理异常");
        }
    }
出现异常catch且throw

出现异常时手动处理但抛出Error和RuntimeException及其子类进行回滚

public void test3(){
        //捕获异常后但是又抛出这样事务能回滚
        try{
            studentService.update();
            bookDAO.add(initBook());
            int i = 1 / 0 ;
        }catch (Exception e){
            System.out.println("手动处理异常");
            throw new RuntimeException("抛出"); //默认情况下对Error和RuntimeException及其子类进行回滚
        }
    }
手动回滚

可以使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()进行手动回滚

public void test4(){
        //手动回滚事务,这样即使做了异常处理我们手动可以进行回滚
        try{
            studentService.update();
            bookDAO.add(initBook());
            int i = 1 / 0 ;
        }catch (Exception e){
            System.out.println("手动处理异常");
            System.out.println("手动回滚事务");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //手动回滚事务
        }
    }

spring的事务传播行为

什么是spring的事务传播行为?
spring事务传播机制是用来控制当多个事务嵌套的时候的情况如何处理。例如上例子在bookService中方法使用了StudentService的方法,本身StudentService的方法也有自己的事务,当两个事务在一个方法里面时如何处理.

image.png

如何在service中方法中启用了线程调用其他service方法时事务如何嵌套?

    public void test5(){
        bookDAO.add(initBook());
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                studentService.update();
            }
        };
        new Thread(runnable).start();
    }

这种情况下,在子线程中会独立启动一个事务,与主线程无关

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,548评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,497评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,990评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,618评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,618评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,246评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,819评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,725评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,268评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,356评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,488评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,181评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,862评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,331评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,445评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,897评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,500评论 2 359

推荐阅读更多精彩内容

  • 原文链接:https://docs.spring.io/spring-boot/docs/1.4.x/refere...
    pseudo_niaonao阅读 4,709评论 0 9
  • 1.数据库事务基础知识 1.1.何为数据库事务 数据库事务的4个特性 原子性:组成一个事务的多个数据库操作是一个不...
    小螺钉12138阅读 1,581评论 1 18
  • Spring 事务属性分析 事务管理对于企业应用而言至关重要。它保证了用户的每一次操作都是可靠的,即便出现了异常的...
    壹点零阅读 1,306评论 0 2
  • 做视觉设计的同学,每天都要面对形形色色的需求,我始终觉得能 hold 得住多种设计风格的设计师才更受欢迎哦。我做过...
    安安设计阅读 9,238评论 0 46
  • 这个月我做到了 01 坚持每天读书20分钟,并写读书感悟,这个月坚持写了29天。 02 坚持写育儿日记,并记录反思...
    令芳老师阅读 326评论 2 7