Spring事务的传播行为+隔离级别+回滚规则+实现方式,面向切面实现事务控制

文章目录

    • spring 事务的传播行为
    • spring 事务的隔离级别
    • spring 事务的回滚规则
    • spring 事务的实现方式
      • 1. 基于 XML 配置
      • 2. 基于注解配置
        • 1. 定义一个 TransactionManager 类,用于开启、提交或回滚事务:
        • 2. 定义一个 Service 接口及其实现类 UserServiceImpl :
        • 3. 定义一个切面类,用于添加事务管理功能:
        • 4. 在 Spring 的配置文件中配置切面:

事务是指数据库操作的一个执行单元,它是由一组有序的 SQL 语句组成的。

在执行过程中,要么都执行成功,要么都执行失败。如果其中一条 SQL 语句执行失败,整个事务就会被回滚,回到事务开始前的状态。

spring 事务管理器是基于 AOP 技术实现的,它可以管理一组相关的事务操作。在同一个事务中,多个数据库操作可以被视为一个整体,如果其中任何一个操作失败,整个事务都将被回滚。

spring 事务的传播行为

spring 事务的传播行为是指,当一个事务方法调用了其他方法时,这些方法应该如何处理事务。spring 事务一般采用以下传播行为:

  1. Propagation.REQUIRED(默认):如果当前存在事务,则加入该事务;否则创建一个新事务。

  2. Propagation.SUPPORTS:如果当前存在事务,则加入该事务;否则以非事务方式执行。

  3. Propagation.MANDATORY:必须在当前事务内运行;否则抛出异常。

  4. Propagation.REQUIRES_NEW:创建一个新事务,如果当前存在事务,则挂起当前事务。

  5. Propagation.NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则挂起当前事务。

  6. Propagation.NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

  7. Propagation.NESTED:如果当前存在事务,则在嵌套事务内运行;否则创建一个新事务。

spring 事务的隔离级别

spring 事务的隔离级别是指在并发环境下,不同的事务之间需要隔离的程度。spring 支持以下五种事务隔离级别:

  1. Isolation.DEFAULT(默认):使用数据库默认的隔离级别。

  2. Isolation.READ_UNCOMMITTED:允许读取未提交的数据。

  3. Isolation.READ_COMMITTED:只能读取已提交的数据。

  4. Isolation.REPEATABLE_READ:保证同一事务中多次读取同一数据时,得到的结果是一致的。

  5. Isolation.SERIALIZABLE:最高的事务隔离级别,保证事务之间的完全隔离。

spring 事务的回滚规则

spring 事务在什么情况下会回滚?通常情况下,spring 事务遵循以下两个原则:

  1. 默认情况下,当抛出 RuntimeException 和其子类异常时,spring 事务会进行回滚。

  2. 如果方法中主动抛出了 Exception 异常或者其他检查异常,则不会进行回滚。

可以通过 @Transactional 的 noRollbackFor 属性和 rollbackFor 属性来指定不回滚的异常和回滚的异常。

spring 事务的实现方式

spring 事务的实现方式通常有两种:基于 XML 配置和基于注解配置。

1. 基于 XML 配置

在 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:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    

    
    <context:component-scan base-package="com.example.demo"/>
    
    <context:annotation-config/>

    
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mya_test"/>
        <property name="user" value="root"/>
        <property name="password" value=""/>
    bean>

    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    bean>

    
    <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="transferAccounts" isolation="READ_COMMITTED" propagation="REQUIRED" read-only="false"/>
        tx:attributes>
    tx:advice>

    
    <aop:config>
        <aop:pointcut id="servicePointCut"
                      expression="execution(public void com.example.demo.service.UserServiceImplXML.transferAccounts(..))"/>
        <aop:advisor pointcut-ref="servicePointCut" advice-ref="txAdvice"/>
    aop:config>

beans>

2. 基于注解配置


<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:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    

    
    <context:component-scan base-package="com.example.demo"/>
    
    <context:annotation-config/>
    
    <aop:aspectj-autoproxy/>

    
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mya_test"/>
        <property name="user" value="root"/>
        <property name="password" value=""/>
    bean>
    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    bean>

    
    <bean name="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    bean>
    
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

beans>
import com.example.demo.dao.UserDao;
import com.example.demo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED, readOnly = true)
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    @Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED, readOnly = false)
    /**
     * isolation:隔离级别
     * propagation:传播行为
     * readOnly:是否只读,默认false
     */
    public boolean transferAccounts(User user1, User user2, double money) {
        // 1减钱
        user1.setMoney(user1.getMoney() - money);
        userDao.updateUser(user1, user1.getId());

//        System.out.println(10/0);

        // 2加钱
        user2.setMoney(user2.getMoney() + money);
        userDao.updateUser(user2, user2.getId());
        return true;
    }
}

1. 定义一个 TransactionManager 类,用于开启、提交或回滚事务:
// 定义一个事务管理类,用于开启、提交或回滚事务
public class TransactionManager {
    /**
     * 开始事务
     */
    public void begin() {
        System.out.println("开始事务...");
    }
    
    /**
     * 提交事务
     */
    public void commit() {
        System.out.println("提交事务...");
    }
    
    /**
     * 回滚事务
     */
    public void rollback() {
        System.out.println("回滚事务...");
    }
}

2. 定义一个 Service 接口及其实现类 UserServiceImpl :
public interface Service {
    void addUser();
}

public class UserServiceImpl implements Service {
    @Override
    public void addUser() {
        System.out.println("添加用户...");
    }
}
3. 定义一个切面类,用于添加事务管理功能:
// 定义一个切面类,用于添加事务管理功能
public class TransactionAspect {
    private TransactionManager transactionManager;
    
    /**
     * 设置事务管理器
     */
    public void setTransactionManager(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }
    
    /**
     * 开始事务
     */
    public void beginTransaction() {
        transactionManager.begin();
    }
    
    /**
     * 提交事务
     */
    public void commitTransaction() {
        transactionManager.commit();
    }
    
    /**
     * 回滚事务
     */
    public void rollbackTransaction() {
        transactionManager.rollback();
    }
    
    /**
     * 环绕通知,在目标方法周围织入通知
     */
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            beginTransaction();
            joinPoint.proceed(); // 执行目标方法
            commitTransaction();
        } catch (Throwable e) {
            rollbackTransaction();
            throw e;
        }
    }
}

注解说明:

  • @Around:表示在目标方法周围织入通知。
  • @Pointcut:表示定义一个切点,通过表达式匹配需要拦截的目标方法。
4. 在 Spring 的配置文件中配置切面:

<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"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
	        
	<bean id="transactionManager" class="com.example.TransactionManager"/>
	<bean id="userService" class="com.example.UserServiceImpl"/>
	<bean id="transactionAspect" class="com.example.TransactionAspect">
	    <property name="transactionManager" ref="transactionManager"/>
	bean>
	
	<aop:config>
		<aop:aspect ref="transactionAspect">
	        
	        <aop:pointcut id="serviceMethod" expression="execution(* com.example.Service.*(..))"/>
	        
	        <aop:around pointcut-ref="serviceMethod" method="around"/>
	    aop:aspect>
	aop:config>

beans>

注解说明:

  • :将 TransactionManagerUserServiceImplTransactionAspect 配置为 Spring 容器中的 Bean。
  • :配置 AOP。
  • :表示定义一个切面。
  • :定义切点,通过表达式匹配需要拦截的目标方法。
  • :定义环绕通知,在目标方法周围织入通知,注意 method 属性要指向切面类中的方法名。

你可能感兴趣的:(spring,java)