Spring声明式事务

Spring 声明式事务

1. 回顾事务

  • 把一组业务当成一个业务,要么都成功,要么都失败
  • 事务在项目开发中,十分的重要,涉及到数据一致性问题,不能马虎!
  • 确保完整性和一致性

事务的ACID原则:

  1. 原子性(Atomicity):原子性意味着事务是一个不可分割的工作单位,事务中的操作要么全部完成,要么全部不完成。如果一个事务被标记为失败,那么系统将自动回滚到事务开始前的状态,就像这个事务从未被执行过一样。

  2. 一致性(Consistency):一致性意味着事务必须将数据库从一个一致性状态转换到另一个一致性状态。一致性状态通常由数据库的一组完整性约束来定义。如果一个事务在执行过程中发生错误,所有的数据修改都会被回滚,数据库状态将回到事务开始前的状态。

  3. 隔离性(Isolation):隔离性意味着在并发环境中,一个事务的执行不应该被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,其他事务无法看到这个事务内部的操作。

  4. 持久性(Durability):持久性意味着一旦事务被提交,它对数据库中数据的改变就是永久性的。接下来的其他操作或故障不应对其有任何影响。

首先!官方文档👈

然后!假如我们把UserMapperImpl类写成了这个吊样

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
    @Override
    public List<User> selectUser() {
        User user = new User(6,"小王","123457");
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        mapper.addUser(user);
        int i = 1/0;

        return mapper.selectUser();
    }

    @Override
    public int addUser(User user) {
        return 0;
    }

    @Override
    public int deleteUser(int id) {
        return 0;
    }
}

selectUser这个方法中有个int i = 1/0;,会提示错误,造成方法执行失败

假如!我们不开启事务的话,selectUser方法会报错,但是上面的mapper.addUser(user);已经执行成功,数据库中已经被添加了用户,但是我们不想在没有执行成功的情况下添加用户,怎么办?

我们就引入 事务!!!


  1. 首先,通过查看官方文档,我们发现,应该先开启事务(声明式事务)

    注意,这些配置文件应该写在配置数据源的那个xml文件中,也就是spring-dao.xml

    <!--    配置声明式事务-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <constructor-arg ref="dataSource" />
        </bean>
    
  2. 然后!就不要看官方文档了,我们用 @狂神说 的方法用AOP来实现事务的织入

    同样,在上面的xml文件中写入

    <!--    结合AOP实现事务的织入-->
    <!--    配置事务通知-->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--        给哪些方法配置事务-->
    <!--        配置事务的传播特性-->
            <tx:attributes>
                <tx:method name="add" propagation="REQUIRED"/>
                <tx:method name="delete" propagation="REQUIRED"/>
                <tx:method name="update" propagation="REQUIRED"/>
                <tx:method name="query" read-only="true"/>
                <tx:method name="*" propagation="REQUIRED"/>
            </tx:attributes>
        </tx:advice>
    

AI小提醒:

这些adddelete等是匹配的方法名,而不是SQL语句。

在这个配置中,<tx:method name="add" propagation="REQUIRED"/>表示的是当调用名为add的方法时,事务的传播特性将被设置为REQUIRED。同样,deleteupdatequery也是对应到方法名。

这个配置的目的是,当你在应用中调用这些方法时,Spring会根据这个配置自动为你管理事务。所以,你需要确保你的方法名和这个配置中的方法名一致,否则这个配置将不会产生任何效果。

请注意,这个配置并不关心你的方法内部执行了什么SQL语句,它只关心方法名和事务属性的映射关系。

  1. 然后!配置事务切入

    同样,在上面的xml文件中写入

    <!--    配置事务切入-->
        <aop:config>
    <!--        mapper下所有类,所有方法,都设置为切入点-->
            <aop:pointcut id="txPointCut" expression="execution(* top.qwwq.mapper.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
        </aop:config>
    

AI小提醒:

这是一个AOP(面向切面编程)的切入点表达式,用于定义哪些方法会被AOP通知(如事务管理)所影响。

execution(* top.qwwq.mapper.*.*(..))这个表达式的含义是:

  • execution():这是最常见的切入点匹配符,用于匹配方法执行。
  • *:表示返回任何类型的方法。
  • top.qwwq.mapper.*.*:表示在top.qwwq.mapper包下的所有类的所有方法。第一个*表示所有类,第二个 *表示所有方法。
  • (..):表示方法参数为任意,无论是无参还是有任意多个参数,都会被匹配。

所以,这个表达式的含义是:匹配top.qwwq.mapper包下所有类的所有方法。

在你的配置中,<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>这一行表示,对于匹配txPointCut(即满足上述表达式的方法)的方法,应用名为txAdvice的通知。

简单来说,这个配置的含义是:对于top.qwwq.mapper包下所有类的所有方法,当这些方法被调用时,Spring会自动应用txAdvice中定义的事务管理策略。

  1. 最后,执行方法就可以了。

最简实现Spring 声明式事务

如果你想用最简单的方式实现Spring 声明式事务,那么GPT4给出了一下两种方式

Spring 提供了两种主要的方式来实现声明式事务管理:注解方式和 XML 配置方式。下面我将为你介绍这两种方式。

1. 注解方式

在 Spring 中,你可以使用 @Transactional 注解来实现声明式事务管理。首先,你需要在你的配置中启用注解驱动的事务管理,然后在你需要进行事务管理的方法上添加 @Transactional 注解。以下是一个简单的例子:

@Configuration
@EnableTransactionManagement  // 启用注解驱动的事务管理
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        // 创建并返回数据源
    }

    @Bean
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

@Service
public class MyService {

    @Autowired
    private MyRepository repository;

    @Transactional  // 该方法将被事务管理
    public void doSomething() {
        // 你的业务逻辑
        repository.save(someObject);
    }
}

2. XML 配置方式

如果你不想在你的代码中添加注解,你可以使用 XML 配置方式来实现声明式事务管理。以下是一个简单的例子:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

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

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" />
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="serviceOperation" expression="execution(* com.example.myapp.service.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" />
    </aop:config>

</beans>

在这个配置中,所有在 com.example.myapp.service 包下的方法都将被事务管理。

这两种方式都可以实现声明式事务管理,你可以根据你的需求选择合适的方式。


Spring声明式事务
http://localhost:8090//archives/G5EDGR0i
作者
EnderKC
发布于
2024年12月23日
许可协议