Spring中几种事务失效的场景

说事务失效,要先知道什么是事务?

    访问并可能更新数据库中各种数据项的一个程序执行单员。
    事务有四个特性,即我们耳熟闻详的:
        1、原子性:即操作这些指令时(如更新、插入、删除等),要么全部执行成功,要么全部不执行,只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令之前的数据状态。
        2、一致性:事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
        3、隔离性:在该事务执行的过程中,无论发生的任何数据的改变都应该只存在于该事务之中,对外界不存在任何影响。只有在事务确定正确提交之后,才会显示该事务对数据的改变。其他事务才能获取到这些改变后的数据。
        4、持久性:当事务正确完成后,它对于数据的改变是永久性的。

        通俗的说的话就是,我们在一个方法里面,对数据进行插入、删除等操作的时候,只要有一个地方操作失败,那整个方法里面的操作,都应该回到未操作之前状态。在给大家举个例子(_):就像小明同学暗恋他的们班上的班花,终于在有一天鼓起勇气去给他的女神表白,但是女神不喜欢小明同学,并狠狠的羞辱了他(你个癞蛤蟆,还想吃天鹅肉 -_-),导致小明名气大失,这时小明开启了时光机,回到表白之前,不给他的女神表白(这就是我们说的事务回滚),便不会被女神羞辱,并告诉自己,别去表白了,她会羞辱你。但是由于小明操作失误,导致穿越到正在表白的时候,最后还是被羞辱了(这就是我们说得事务失效)。

下面来说说事务失效的几种场景
1、确认创建的mysql数据库表引擎是InnoDB,MyISAM不支持事务

    在mysql数据库中,我的数据库表引擎一定要是InnoDB,因为Innodb才支持事务,而MyISAM是不支持事务的,即使我们添加了 @Transactional注解也是无用的

2、注解到protected,private 方法上,事务不生效,它也不会报错,不过事务设置不会起作用。
@Transaction 可以用在类上、接口上、public方法上,如果将@Trasaction用在了非public方法上,事务将无效。

3、 在业务层捕捉异常后未向上抛出,事务不生效。
并且由于我们只是catch了异常而并没有抛出,程序也是不会报错的。
                直接看演示:

一开始并没有数据
Spring中几种事务失效的场景_第1张图片

@Override
@Transactional
public Long addTask(Task task) throws ScheduleSystemException {
    boolean flag =  addTaskToDb (task);
    if (flag) {
        addTaskToCache(task);
    }
    return task.getTaskId();
}
private void addTaskToCache(Task task) {
    cacheService.zAdd(Constants.DBCACHE, JSON.toJSONString(task),task.getExecuteTime());
}
@Transactional
private boolean addTaskToDb(Task task) {
    boolean flag = false;
    try {
        //save taskInfo
        .......................
		
		// 手动添加异常
        int i = 1 / 0;
        //save taskInfo Logs
       ...............................
        flag = true;
    } catch (Exception e) {
        log.warn("add task exception, taskId = {}", task.getTaskId());
        //注意此处,我们catch了异常,但是我们并没有向外抛出
//            throw new ScheduleSystemException(e.getMessage());
    }
    return flag;
}


//开始测试
@Test
public void testAddTask() {
   Task task = new Task();
   task.setTaskType(1011);
   task.setExecuteTime(new Date().getTime());
   task.setParameters(("taskServiceTest".getBytes()));
   task.setPriority(102);
   System.out.println(taskService.addTask(task));
}

执行结果:
Spring中几种事务失效的场景_第2张图片
可以看出,虽然我们添加了事务,但是由于我们将异常给catch了,而未抛出,这就到导致我们的事务失效了

3.1、为啥一般我自定义的异常都是继承的RuntimeException呢?因为Spring默认回滚的是:RuntimeException。这就导致如果我们catch了异常之后,如果抛的异常不是RuntimeException,事务也是会失效的。
Tips:推荐在业务层统一抛出异常,在控制层处理异常。

4、**遇到非检测异常时,事务不开启,也无法回滚。**下方图片是异常体系,可以手动指定遇到某些异常也会滚
@Transactional(rollbackFor = {异常类型列表})

Spring中几种事务失效的场景_第3张图片

5、不要写到接口上,spring 是采用 aop 针对具体实现类做的代理实现。
需要spring管理事务的bean生成了代理对象,然后通过代理对象拦截了目标方法的执行,在方法前后添加了事务的功能,所以必须通过代理对象调用目标方法的时候,事务才会起效

6、Spring的事务传播策略在内部方法调用时将不起作用,事务注解加到要调用方法上。

@Override
public Long addTask(Task task) throws ScheduleSystemException {
    boolean flag =  addTaskToDb (task);
    if (flag) {
        addTaskToCache(task);
    }
    return task.getTaskId();
}
private void addTaskToCache(Task task) {
    cacheService.zAdd(Constants.DBCACHE, JSON.toJSONString(task),task.getExecuteTime());
}

@Transactional
private boolean addTaskToDb(Task task) {
    boolean flag = false;
    try {
        //save taskInfo
        .......................
		
		// 手动添加异常
        int i = 1 / 0;
        //save taskInfo Logs
       ...............................
        flag = true;
    } catch (Exception e) {
        log.warn("add task exception, taskId = {}", task.getTaskId());
        throw new ScheduleSystemException(e.getMessage());
    }
    return flag;
}


//开始测试
@Test
public void testAddTask() {
   Task task = new Task();
   task.setTaskType(1011);
   task.setExecuteTime(new Date().getTime());
   task.setParameters(("taskServiceTest".getBytes()));
   task.setPriority(102);
   System.out.println(taskService.addTask(task));
}

Spring中几种事务失效的场景_第4张图片
可以看见,这样事务也会失效,导致我们一张表有了数据,另一张却没有。

你可能感兴趣的:(小技巧,笔记,java,spring,后端)