一、基本概念

1、@Transactional注解的注意点

  1. @Transactional 注解只能应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
  2. 在类内部调用(即this调用)时,被调用方法的事务声明(@Transactional 注解)将不起作用,注意是@Transactional 注解不起作用,不是没有事务,因为可能存在共享事务(有注解的方法调用没有注解的方法)。
  3. 只有在外部调用的情况下才能引起事务,这是由Spring Aop的本质决定的。比如Controller调用Service、一个Service调用另一个Service才会引起事务。

2、事务传播行为

       事务传播行为,所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:

  1. TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  3. TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  5. TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  6. TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  7. TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价TransactionDefinition.PROPAGATION_REQUIRED。

二、上代码

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

实体类 User.java

package me.zhao.entity;

public class User {

    private Integer id;

    private String name;

    private Integer age;

    public User() {
    }

    public User(String name, Integer age) {
         = name;
        this.age = age;
    }
    
    // getter、setter 
}

UserDao.java

package me.zhao.dao;

import me.zhao.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 插入
     * @param user 用户
     * @return 插入条数
     */
    public int insert(User user) {
        String sql = "insert into user(name, age) value(?, ?)";
        return jdbcTemplate.update(sql, user.getName(), user.getAge());
    }
}

两个接口 UserService.java、UserService2.java

package me.zhao.service;

public interface UserService {

    void test1();

    void test2();

    void test3();

    void test4();

    void test5();
}
package me.zhao.service;

public interface UserService2 {

    void insert5();
}

两个实现 UserServiceImpl.java、UserServiceImpl2.java

package me.zhao.service.impl;

import me.zhao.dao.UserDao;
import me.zhao.entity.User;
import me.zhao.service.UserService;
import me.zhao.service.UserService2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private UserService2 userService2;

    /**
     * 测试一 (内部调用)<br/>
     * test1 调用 insert1<br/>
     * test1方法上没有 @Transactional 注解,insert1有注解<br/>
     * insert1中抛出除零异常<br/>
     * 测试结果:两条数据都插入了,事务没有回滚;
     * 说明insert1方法的@Transactional注解没有起作用
     */
    @Override
    public void test1() {
        userDao.insert(new User("test1", 10));
        insert1();
    }

    @Transactional(rollbackFor = Exception.class)
    public void insert1() {
        userDao.insert(new User("insert1", 10));
        int a = 1 /0;
    }

    /**
     * 测试二 (内部调用)<br/>
     * test2 调用 insert2<br/>
     * test2方法上有 @Transactional 注解,insert2没有注解<br/>
     * insert2中抛出除零异常<br/>
     *
     * 测试结果:两条数据都没有插入,事务进行了回滚;
     * 说明insert2方法加入到了test2 的事务中,共享了事务
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void test2() {
        userDao.insert(new User("test2", 10));
        insert2();
    }

    public void insert2() {
        userDao.insert(new User("insert2", 10));
        int a = 1 /0;
    }

    /**
     * 测试三 (内部调用)<br/>
     * test3 调用 insert3<br/>
     * test3方法上有 @Transactional 注解,insert3没有注解<br/>
     * test3的末尾抛出抛出除零异常<br/>
     *
     * 测试结果:两条数据都没有插入,事务进行了回滚;
     * 说明insert3方法加入到了test3 的事务中,共享了事务
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void test3() {
        User user = new User("test3", 10);
        userDao.insert(user);
        insert3();
        int a = 1 /0;
    }

    public void insert3() {
        User user = new User("insert3", 10);
        userDao.insert(user);
    }


    /**
     * 测试四 (内部调用) <br/>
     * test4 调用 insert4,两个方法都有 @Transactional 注解<br/>
     * test4方法上有的事务传播级别是 Propagation.REQUIRED(默认值)
     * insert4事务传播级别是 Propagation.NEVER 必须以非事务方式运行,如果当前存在事务,则抛出异常 IllegalTransactionStateException
     * 异常信息为 Existing transaction found for transaction marked with propagation 'never'
     *
     * insert4中抛出除零异常
     *
     * 测试结果:没有抛出 IllegalTransactionStateException 异常;
     * 说明insert4方法的 @Transactional 注解没有生效
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void test4() {
        userDao.insert(new User("test4", 10));
        insert4();
    }

    @Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
    public void insert4() {
        userDao.insert(new User("insert4", 10));
        int a = 1 /0;
    }

    /**
     * 测试四 (外部调用)<br/>
     * test5 调用 外部方法  userService2.insert5,两个方法都有 @Transactional 注解<br/>
     * test5方法上有的事务传播级别是 Propagation.REQUIRED(默认值)
     * userService2.insert5事务传播级别是 Propagation.NEVER 必须以非事务方式运行,如果当前存在事务,则抛出异常 IllegalTransactionStateException
     * 异常信息为 Existing transaction found for transaction marked with propagation 'never'
     *
     * userService2.insert5中抛出除零异常
     *
     * 测试结果:抛出了 IllegalTransactionStateException 异常;
     * 说明两个方法的 @Transactional 注解 都产生了 事务行为
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void test5() {
        userDao.insert(new User("test5", 10));
        userService2.insert5();
    }
}
package me.zhao.service.impl;

import me.zhao.dao.UserDao;
import me.zhao.entity.User;
import me.zhao.service.UserService;
import me.zhao.service.UserService2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl2 implements UserService2 {

    @Autowired
    private UserDao userDao;


    @Override
    @Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
    public void insert5() {
        User user = new User("insert4", 10);
        userDao.insert(user);
        int a = 1 /0;
    }
}

TestController.java

package me.zhao.controller;

import me.zhao.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Autowired
    private UserService userService;

    @GetMapping("test1")
    public String test1() {
        userService.test1();
        return "test1";
    }

    @GetMapping("test2")
    public String test2() {
        userService.test2();
        return "test2";
    }

    @GetMapping("test3")
    public String test3() {
        userService.test3();
        return "test3";
    }

    @GetMapping("test4")
    public String test4() {
        userService.test4();
        return "test4";
    }

    @GetMapping("test5")
    public String test5() {
        userService.test5();
        return "test5";
    }
}

三、验证

test1、内部调用,无注解调用有注解

            UserServiceImpl类中的 test1 调用 insert1,test1方法上没有 @Transactional 注解,insert1有注解,在insert1中抛出除零异常

测试结果:两条数据都插入了,事务没有回滚;说明 insert1方法的@Transactional注解没有起作用。

spring事务调用另外一个事务方法 spring事务方法内调用不生效_spring

                   

spring事务调用另外一个事务方法 spring事务方法内调用不生效_User_02

test2、内部调用,有注解调用无注解

           UserServiceImpl类中的 test2 调用 insert2,test2方法上有 @Transactional 注解,insert2没有注解,insert2中抛出除零异常

测试结果:测试结果:两条数据都没有插入,事务进行了回滚;说明insert2方法加入到了test2 的事务中,共享了事务。

spring事务调用另外一个事务方法 spring事务方法内调用不生效_java_03

                 

spring事务调用另外一个事务方法 spring事务方法内调用不生效_User_04

test3、内部调用,有注解调用无注解

           UserServiceImpl类中的 test3 调用 insert3,test3方法上有 @Transactional 注解,insert3没有注解,test3末尾抛出除零异常

测试结果:测试结果:两条数据都没有插入,事务进行了回滚;说明insert3方法加入到了test3 的事务中,共享了事务。

spring事务调用另外一个事务方法 spring事务方法内调用不生效_User_05

                   

spring事务调用另外一个事务方法 spring事务方法内调用不生效_User_06

 

test4、内部调用,都有注解Propagation.REQUIRED 调用 Propagation.NEVER

           UserServiceImpl类中的 test4 调用 insert4,两个方法都有 @Transactional 注解,test4方法上有的事务传播级别是 Propagation.REQUIRED,insert4事务传播级别是 Propagation.NEVER,必须以非事务方式运行,如果当前存在事务,则抛出异常 IllegalTransactionStateException :Existing transaction found for transaction marked with propagation 'never',在insert4中抛出除零异常

测试结果:没有抛出 IllegalTransactionStateException 异常,也没有插入数据;说明insert4方法的 @Transactional 注解没有生效,test4上的注解生效了。

spring事务调用另外一个事务方法 spring事务方法内调用不生效_spring_07

                    

spring事务调用另外一个事务方法 spring事务方法内调用不生效_User_08

test5、外部调用,都有注解Propagation.REQUIRED 调用 Propagation.NEVER

           UserServiceImpl.test5 调用UserServiceImpl2. insert5,两个方法都有 @Transactional 注解,test5方法上有的事务传播级别是 Propagation.REQUIRED,insert5事务传播级别是 Propagation.NEVER,必须以非事务方式运行,如果当前存在事务,则抛出异常 IllegalTransactionStateException :Existing transaction found for transaction marked with propagation 'never',在userServiceImpl2.insert5中抛出除零异常

测试结果:抛出了 IllegalTransactionStateException 异常,但没有插入数据;

说明UserServiceImpl2. insert5方法的 @Transactional 注解生效了(抛出了IllegalTransactionStateException 异常),UserServiceImpl.test5上的注解也生效了(事务回滚了,没有插入数据)。

spring事务调用另外一个事务方法 spring事务方法内调用不生效_User_09

  

spring事务调用另外一个事务方法 spring事务方法内调用不生效_spring事务调用另外一个事务方法_10