一、基本概念
1、@Transactional注解的注意点
- @Transactional 注解只能应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
- 在类内部调用(即this调用)时,被调用方法的事务声明(@Transactional 注解)将不起作用,注意是@Transactional 注解不起作用,不是没有事务,因为可能存在共享事务(有注解的方法调用没有注解的方法)。
- 只有在外部调用的情况下才能引起事务,这是由Spring Aop的本质决定的。比如Controller调用Service、一个Service调用另一个Service才会引起事务。
2、事务传播行为
事务传播行为,所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
- TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
- TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- 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注解没有起作用。
test2、内部调用,有注解调用无注解
UserServiceImpl类中的 test2 调用 insert2,test2方法上有 @Transactional 注解,insert2没有注解,insert2中抛出除零异常。
测试结果:测试结果:两条数据都没有插入,事务进行了回滚;说明insert2方法加入到了test2 的事务中,共享了事务。
test3、内部调用,有注解调用无注解
UserServiceImpl类中的 test3 调用 insert3,test3方法上有 @Transactional 注解,insert3没有注解,test3末尾抛出除零异常。
测试结果:测试结果:两条数据都没有插入,事务进行了回滚;说明insert3方法加入到了test3 的事务中,共享了事务。
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上的注解生效了。
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上的注解也生效了(事务回滚了,没有插入数据)。