【Java】Spring事务相关笔记

Spring事务相关笔记

事务的四大特征

  • 原子性:事务中的全部操作在数据库中是不可分割的,要么全部执行,要么均不执行
  • 一致性:几个并行的事务,其执行结果必须与按某一顺序串行执行的结果相一致
  • 隔离性:事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须透明
  • 持久性:对于已提交的事务,系统必须保证该事务对数据库的改变不被丢失

事务使用

表结构示例:

drop table student;
create table `student`(
    `id` int unsigned auto_increment,
    `name` varchar(64),
    `age` int unsigned,
    primary key (`id`)
) engine=InnoDB default charset=utf8mb4;

实体类:

package com.alone.community.transaction.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

/**
 * @author Alone
 */
@Data
@Entity
public class Student {

    @Id
    @GeneratedValue
    private Integer id;

    private String name;

    private Integer age;

    public Student() {
    }

    public Student(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

dao:

@Repository
public interface StudentRepository extends JpaRepository<Student, Integer> {
}

service:

public interface StudentService {

    Student saveStudent(Student student);
}
@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentRepository studentRepository;

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Student saveStudent(Student student) {
        Student save = studentRepository.save(student);
        if("Alone".equals(save.getName())) {
            throw new RuntimeException("Alone已存在!");
        }
        return save;
    }
}

controller:

@RestController
@RequestMapping("/student")
public class StudentController {

    @Autowired
    private StudentService studentService;

    @RequestMapping("/savestudent")
    public Student saveStudent(@RequestBody Student student) {
        return studentService.saveStudent(student);
    }
}

事务注解@Transactional的常见参数

参数作用
value当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器
transactionManager当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器
propagation事务的传播级别,默认值为Proppagation.REQUIRED
isolation事务的隔离级别,默认值为Isolation.DEFAULT
timeout事务超时时间,默认值为-1,如果超过该时间限制但事务还没有完成,则自动回滚事务
rollbackFor用于指定能够触发事务回滚的异常类型,可以指定多个异常类型

事务隔离级别

参数事务隔离级别脏读不可重复读幻读
READ_UNCOMMITTED读未提交(read-uncommitted)
READ_COMMITTED不可重复读(read-committed)
REPEATABLE_READ可重复读(repeatable-read)
SERIALIZABLE串行化(serializable))

事务传播级别

传播行为解释
REQUIRED如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务
SUPPORTS如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行
MANDATORY如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常
REQUIRES_NEW重新创建一个新的事务,如果当前存在事务,暂停当前的事务
NOT_SUPPORTED以非事务的方式运行,如果当前存在事务,暂停当前的事务
NEVER以非事务的方式运行,如果当前存在事务,则抛出异常
NESTED和REQUIRED效果一样

事务失效的场景

  1. 访问权限问题

spring要求被代理的方法必须是public的。
在源码的AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute 方法中判断了,如果目标方法不是public,则TransactionAttribute 返回null。

@Nullable
    protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
        if (this.allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
            return null;
        } else {
            Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
            TransactionAttribute txAttr = this.findTransactionAttribute(specificMethod);
            if (txAttr != null) {
                return txAttr;
            } else {
                txAttr = this.findTransactionAttribute(specificMethod.getDeclaringClass());
                if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
                    return txAttr;
                } else {
                    if (specificMethod != method) {
                        txAttr = this.findTransactionAttribute(method);
                        if (txAttr != null) {
                            return txAttr;
                        }

                        txAttr = this.findTransactionAttribute(method.getDeclaringClass());
                        if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
                            return txAttr;
                        }
                    }

                    return null;
                }
            }
        }
    }
  1. 方法被final或static修饰
    spring内部是通过aop生成代理类来实现事务功能,如果某个方法用final或static修饰了,在代理类中就无法重写该方法,从而添加事务功能。

  2. 方法内部调用
    这篇文章中存在方法内部调用事务成功的情况,有待研究

方法内部调用不会生成代理对象,所以事务失效。如果想要调用,则需注入一下自己。

  1. 未被Spring管理
  2. 多线程调用

spring的事务是通过数据库连接来实现的,数据库连接又是通过ThreadLocal来保存的,多个线程同时调用时产生了多个ThreadLocal,则会有多个数据库连接。

  1. 表不支持事务
  2. 未开启事务
  3. 错误的事务传播级别
  4. 自己吞了异常
  5. 异常类型不正确

参考资料

  1. SpringBoot基础之声明式事务和切面事务和编程式事务
  2. SpringBoot 实战 (十) | 声明式事务
  3. 阿里P6都不一定全都答对,spring事务失效12种场景
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值