事务传播行为

事务传播行为是指多个拥有事务的方法在嵌套调用时的事务控制方式。
XML:<tx:method name=“…” propagation=“REQUIRED”/>
注解:@Transactional(propagaion=Propagation.REQUIRED)

事务传播行为七种类型

java事物传递机制 java事务传播_java

PROPAGATION_REQUIRED

java事物传递机制 java事务传播_java_02


在com.ql.spring.jdbc.service包下创建BatchService类,编写两个批量处理方法

package com.ql.spring.jdbc.service;

import com.ql.spring.jdbc.dao.EmployeeDao;
import com.ql.spring.jdbc.entity.Employee;

import java.util.Date;

public class BatchService {
    private EmployeeDao employeeDao;
    public void importJob1(){
        for (int i = 1; i <=10 ; i++) {
            Employee employee = new Employee();
            employee.setEno(8000+i);
            employee.setEname("研发部员工"+i);
            employee.setSalary(4000f);
            employee.setDname("研发部");
            employee.setHiredate(new Date());
            employeeDao.insert(employee);
        }
    }

    public void importJob2(){
        for (int i = 1; i <=10 ; i++) {
            Employee employee = new Employee();
            employee.setEno(9000+i);
            employee.setEname("市场部员工"+i);
            employee.setSalary(3000f);
            employee.setDname("市场部");
            employee.setHiredate(new Date());
            employeeDao.insert(employee);
        }
    }

    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }

    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }
}

然后在EmployeeService中创建一个方法startImportJob引用上面的两个方法

public void startImportJob(){
        batchService.importJob1();
        if(1==1){
            throw new RuntimeException("意料之外的异常");
        }
        batchService.importJob2();
        System.out.println("批量导入成功");
    }

在applicationContext.xml中配置BatchService类的两个方法事务传播行为类型

<tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--目标方法名为batchImport时,启用声明式事务,成功提交,运行时异常回滚-->
            <tx:method name="batchImport" propagation="REQUIRED"/>
            <tx:method name="batch*" propagation="REQUIRED"/>
            <!--设置所有findXXXX方法不需要使用事务-->
            <tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/>
            <tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/>

            <tx:method name="importJob1" propagation="REQUIRED"/>
            <tx:method name="importJob2" propagation="REQUIRED"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

在测试类JdbcTemplateTestor.java中添加测试方法

@Test
    public void testStartImportJob(){
        employeeService.startImportJob();
    }

运行后数据库并没有插入任何数据。

java事物传递机制 java事务传播_spring_03

PROPAGATION_REQUIRED_NEW

java事物传递机制 java事务传播_spring_04


在applicationContext.xml中修改BatchService类的两个方法事务传播行为类型

<tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--目标方法名为batchImport时,启用声明式事务,成功提交,运行时异常回滚-->
            <tx:method name="batchImport" propagation="REQUIRED"/>
            <tx:method name="batch*" propagation="REQUIRED"/>
            <!--设置所有findXXXX方法不需要使用事务-->
            <tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/>
            <tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/>

            <tx:method name="importJob1" propagation="REQUIRES_NEW"/>
            <tx:method name="importJob2" propagation="REQUIRES_NEW"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

再次运行后数据库中成功插入了第一个方法的新增数据,第二个方法执行之前因为抛出异常而没被插入数据。

java事物传递机制 java事务传播_java事物传递机制_05

注解配置声明式事务

@Transactional - 事务注解

代码演示

修改配置文件applicationContext.xml为

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd">
    <context:component-scan base-package="com.ql"/>
    <!--数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/ql-spring-jdbc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!--JdbcTemplate提供数据CRUD的API-->
    <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:annotation-driven transaction-manager="transactionManager"/>
</beans>

然后在EmployeeDao加上@Repository注解,最后在两个Service类加上Bean注解和事务注解,如下:

package com.ql.spring.jdbc.service;

import com.ql.spring.jdbc.dao.EmployeeDao;
import com.ql.spring.jdbc.entity.Employee;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
@Service
@Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
public class BatchService {
    @Resource
    private EmployeeDao employeeDao;
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void importJob1(){
        for (int i = 1; i <=10 ; i++) {
            Employee employee = new Employee();
            employee.setEno(8000+i);
            employee.setEname("研发部员工"+i);
            employee.setSalary(4000f);
            employee.setDname("研发部");
            employee.setHiredate(new Date());
            employeeDao.insert(employee);
        }
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void importJob2(){
        for (int i = 1; i <=10 ; i++) {
            Employee employee = new Employee();
            employee.setEno(9000+i);
            employee.setEname("市场部员工"+i);
            employee.setSalary(3000f);
            employee.setDname("市场部");
            employee.setHiredate(new Date());
            employeeDao.insert(employee);
        }
    }

    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }

    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }
}
package com.ql.spring.jdbc.service;

import com.ql.spring.jdbc.dao.EmployeeDao;
import com.ql.spring.jdbc.entity.Employee;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
@Service
//声明式事务核心注解
//放在类上,将声明式事务配置应用于当前类所有方法,默认事务传播为 REQUIRED
@Transactional
public class EmployeeService {
    @Resource
    private EmployeeDao employeeDao;
    @Resource
    private BatchService batchService;
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public Employee findById(Integer eno){
        return employeeDao.findById(eno);
    }

    public void batchImport(){
       for (int i = 1; i <=10 ; i++) {
           if(i==3){
               throw new RuntimeException("意料之外的异常");
           }
           Employee employee = new Employee();
           employee.setEno(8000+i);
           employee.setEname("员工"+i);
           employee.setSalary(4000f);
           employee.setDname("市场部");
           employee.setHiredate(new Date());
           employeeDao.insert(employee);
       }
    }

    public void startImportJob(){
        batchService.importJob1();
        if(1==1){
            throw new RuntimeException("意料之外的异常");
        }
        batchService.importJob2();
        System.out.println("批量导入成功");
    }
    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }

    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }

    public BatchService getBatchService() {
        return batchService;
    }

    public void setBatchService(BatchService batchService) {
        this.batchService = batchService;
    }
}

运行后效果跟XML配置一致。