Spring基础事务

Posted 烟锁迷城

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring基础事务相关的知识,希望对你有一定的参考价值。

1、事务

事务是仅对数据库的一种操作定义,一个或多个增加,删除,修改语句都可以算作一个事务。事务具有四种基本特性,原子性,一致性,隔离性,持久性。

在spring中,事务是一种重要的机制,当一个请求中包含多个数据的修改操作时,就需要保证全部成功或全部失败,否则就会导致数据不一致。在不做管理的情况下,每一个修改数据库数据的SQL语句执行完成后都会自动提交,这让每一个语句都是独立的,想要将多个语句的成功与失败达成一致,就需要在所有的sql执行完成后统一提交。

在spring中,事务使用代理的方式实现,将需要的方法代理起来,然后在执行之前取消自动提交,执行之后进行统一提交,如果有错误就会导致整个方法回滚,这样就达成了所需要的事务一致性。

2、使用自定义注解实现事务

本案例使用自定义注解,AOP切面和JDBC数据库连接,实现事务。

数据库连接工具,建立数据库连接。

public class DBUtil {

    private final static String DRIVER_NAME = "com.mysql.cj.jdbc.Driver";
    private final static String URL = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf-8";
    private final static String USERNAME = "root";
    private final static String PASSWORD = "root";

    public static Connection getConnection() {
        Connection connection = null;
        try {
            Class.forName(DRIVER_NAME);
            connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }
}

使用自定义注解,设定四种切点。

事前切点,开启JDBC手动提交。

事后切点,手动提交事务。

抛错切点,事务回滚。

最终切点,事务关闭。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTransaction {
}
@Aspect
@Component
public class MyAspectJ {

    @Before("@annotation(MyTransaction)")
    public void transactionBefore() throws SQLException {
        Connection connection = DBUtil.getConnection();
        connection.setAutoCommit(false);
    }

    @AfterReturning("@annotation(MyTransaction)")
    public void transactionAfterReturn() throws SQLException {
        Connection connection = DBUtil.getConnection();
        connection.commit();
    }

    @AfterThrowing("@annotation(MyTransaction)")
    public void transactionAfterThrow() throws SQLException {
        Connection connection = DBUtil.getConnection();
        connection.rollback();
    }

    @After("@annotation(MyTransaction)")
    public void transactionAfter() throws SQLException {
        Connection connection = DBUtil.getConnection();
        connection.close();
    }
}

Dao层代码,插入数据方法。

public interface IUserDao {

    void addUser(User user) throws SQLException;
}
@Repository
public class UserDaoImpl implements IUserDao {

    public void addUser(User user) throws SQLException {
        PreparedStatement preparedStatement = null;
        String sql = "insert into user (name,age) values (?,?)";
        preparedStatement = DBUtil.getConnection().prepareStatement(sql);
        preparedStatement.setString(1, user.getName());
        preparedStatement.setInt(2, user.getAge());
        preparedStatement.executeUpdate();
    }
}

service层,建立两个数据插入

public interface IUserService {

    void business(User user1, User user2) throws SQLException;
}
@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private IUserDao userDao;
    
    @MyTransaction
    public void business(User user1, User user2) throws SQLException {
        userDao.addUser(user1);
        userDao.addUser(user2);
    }
}

javaconfig,执行插入指令,检验注解切面是否生效。

@Configuration
@EnableAspectJAutoProxy
@ComponentScan({"com.lichong.service", "com.lichong.dao","com.lichong.aspect"})
public class JavaConfig {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
        IUserService bean = context.getBean(IUserService.class);
        User user1 = new User();
        user1.setName("lili");
        user1.setAge(18);
        User user2 = new User();
        user2.setAge(20);
        user2.setName("lilie");
        try {
            bean.business(user1, user2);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

3、事务的传播行为

事务行为

说明

propagation_required支持当前事务,假设当前没有事务,就新建一个事务
propagation_supports支持当前事务,假设当前没有事务,就以非事务方式运行
propagation_mandatory支持当前事务,假设当前没有事务,就抛出异常
propagation_requires_new新建事务,假设当前存在事务,则把当前事务挂起
propagation_not_supported以非事务方式运行操作,假设当前存在事务,就把当前事务挂起
propagation_never以非事务方式运行操作,假设当前存在事务,就抛出异常
propagation_nested如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行与propagation_required一致的操作

  1.  propagation_required:最常见的行为,事务A和事务B一起成功一起失败。
  2. propagation_supports:如果有两个事务,A和B将保持一致。如果没有父事务,那么下一个操作将没有事务
  3. propagation_mandatory:如果有两个事务,A和B将保持一致。如果没有父事务,那么下一个操作将抛出异常
  4. propagation_requires_new:如果有两个事务,A和B将互不影响,各自抛错和提交
  5. propagation_not_supported:非事务执行
  6. propagation_never:非事务执行,有事务则抛错
  7. propagation_nested:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行与propagation_required一致的操作

4、事务的隔离级别

事务隔离级别是指在不同的事务访问同一批数据时的隔离程度。如果不采用数据隔离措施,会出现一些问题。

  1. 脏读:当事务A读取到事务B未提交的数据,就会发生脏读。假设B修改了某数据,还未提交,A此时读取到的数据就是错误的,这就是脏读。
  2. 幻读:当事务A批量修改某表数据时,事务B插入了一条新数据并提交,导致事务A未修改这条数据,就像发生了幻觉。
  3. 不可重复读:在一个事务中执行两次查询,查询间隔中事务B插入了一条数据并提交,导致两次结果不相同,这就是不可重复读。

为了解决这些问题,数据库提供了一些隔离级别

隔离级别描述
default

使用数据库本身使用的隔离级别

ORACLE:读已提交

MYSQL:可重复度

read_uncommited读未提交:避免不了任何风险
read_commitd读已提交:有幻读和不可重复读风险
repeatable_read可重复度:解决不可重复读的问题,有幻读风险
serlalizable串行化:解决幻读风险,保证所有事物串行执行。

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

Web基础之Spring AOP与事务

Spring基础之AOP

Spring基础之AOP

Spring基础(十四):Spring的事务回顾

[Spring框架]Spring 事务管理基础入门总结.

Spring基础(十五):Spring事务环境搭建