一、事物的传播属性
传播属性 | 当前不存在事物 | 当前存在事物 |
---|---|---|
REQUIRED | 新建事物 | 使用当前事物 |
REQUIRES_NEW | 新建事物 | 挂起当前事物,新建事物 |
SUPPORTS | 不使用事物 | 使用当前事物 |
NOT_SUPPORTED | 不使用事物 | 挂起当前事物 |
MANDATORY | 抛出异常 | 使用当前事物 |
NEVER | 不使用事物 | 抛出异常 |
NESTED | 新建事物 | 嵌套事物 |
二、代码演示
github地址:https://github.com/javaDeveloperZeng/example.git
1.建立测试类
@RunWith(SpringJUnit4ClassRunner.class)
//启动Spring
@SpringBootTest
public class TestTwoController {
Logger logger= LoggerFactory.getLogger(TestController.class);
@Autowired
UserService userService;
@Autowired
UserServiceTwo userServiceTwo;
@Test
public void testMethod(){
userService.test1();
logger.debug("----执行结束---");
}
}
2.建立userService
@Service
@Slf4j
public class UserService {
@Autowired
UserMapper userMapper;
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
UserServiceTwo userServiceTwo;
@Transactional(propagation=Propagation.REQUIRED)
public void test1(){
jdbcTemplate.execute("insert user (id,userName,passWord,realName) " +
"values ('1','a','b','c')");
System.out.println("userService执行完");
userServiceTwo.insetData2();
}
}
3.建立userServiceTwo.java
@Service
@Slf4j
public class UserServiceTwo {
@Autowired
JdbcTemplate jdbcTemplate;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insetData2(){
jdbcTemplate.execute("insert user (id,userName,passWord,realName) " +
"values ('2','abc','b','c')");
System.out.println("userServiceTwo执行完");
throw new RuntimeException("错误");
}
}
执行测试类查看数据,部分结果
三、有try 的时候案例
ServiceA {
void methodA() {
ServiceB.methodB();
}
}
ServiceB {
void methodB() {
}
}
案例1,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED,
1、如果ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,会共用同一个事务,如果出现异常,ServiceA.methodA和ServiceB.methodB作为一个整体都将一起回滚。
2、如果ServiceA.methodA没有事务,ServiceB.methodB就会为自己分配一个事务。ServiceA.methodA中是不受事务控制的。如果出现异常,ServiceB.methodB不会引起ServiceA.methodA的回滚。
案例2,ServiceA.methodA的事务级别PROPAGATION_REQUIRED,ServiceB.methodB的事务级别PROPAGATION_REQUIRES_NEW,
调用ServiceB.methodB,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务。
1、如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。
2、如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA的try…catch捕获并处理,ServiceA.methodA事务仍然可能提交;如果他抛出的异常未被ServiceA.methodA捕获处理,ServiceA.methodA事务将回滚。
案例3,ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_NESTED,
调用ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的子事务并设置savepoint
1、如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB也将回滚。
2、如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA的try…catch捕获并处理,ServiceA.methodA事务仍然可能提交;如果他抛出的异常未被ServiceA.methodA捕获处理,ServiceA.methodA事务将回滚。
四、事物不生效情况
修改UserService .java中test1调用本类方法test2。期望结果是test1中执行插入语句回滚,test2中隔离级别REQUIRES_NEW是新开的事物不应该回滚。但是真实结果test1和test2中执行插入都回滚。test2中事物失效。
@Service
@Slf4j
public class UserService {
@Autowired
UserMapper userMapper;
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
UserServiceTwo userServiceTwo;
@Transactional(propagation=Propagation.REQUIRED)
public void test1(){
jdbcTemplate.execute("insert user (id,userName,passWord,realName) " +
"values ('1','a','b','c')");
System.out.println("tes1执行完");
this.test2();
throw new RuntimeException("出错");
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void test2(){
jdbcTemplate.execute("insert user (id,userName,passWord,realName) " +
"values ('2','aaa','b','c')");
System.out.println("test2执行完");
}
}
解决方法1:在类内部通过@Autowired将本身bean引入,然后通过调用自身bean,从而实现使用AOP代理操作。
注入自身bean
@Service
@Slf4j
public class UserService {
@Autowired
@Lazy
private UserService service;
@Autowired
UserMapper userMapper;
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
UserServiceTwo userServiceTwo;
@Transactional(propagation=Propagation.REQUIRED)
public void test1(){
jdbcTemplate.execute("insert user (id,userName,passWord,realName) " +
"values ('1','a','b','c')");
System.out.println("tes1执行完");
service.test2();
throw new RuntimeException("出错");
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void test2(){
jdbcTemplate.execute("insert user (id,userName,passWord,realName) " +
"values ('2','aaa','b','c')");
System.out.println("test2执行完");
}
}
解决方法2:
通过ApplicationContext引入bean
通过ApplicationContext获取bean,通过bean调用内部方法,就使用了bean的代理类。
注入ApplicationContext
@Service
@Slf4j
public class UserService {
@Autowired
ApplicationContext applicationContext;
@Autowired
UserMapper userMapper;
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
UserServiceTwo userServiceTwo;
@Transactional(propagation=Propagation.REQUIRED)
public void test1(){
jdbcTemplate.execute("insert user (id,userName,passWord,realName) " +
"values ('1','a','b','c')");
System.out.println("tes1执行完");
((UserService)applicationContext.getBean("userService")).test2();
throw new RuntimeException("出错");
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void test2(){
jdbcTemplate.execute("insert user (id,userName,passWord,realName) " +
"values ('2','aaa','b','c')");
System.out.println("test2执行完");
}
}
解决方法3: 通过AopContext获取当前类的代理类
通过AopContext获取当前类的代理类,直接通过代理类调用方法
在引导类上添加@EnableAspectJAutoProxy(exposeProxy=true)注解
-
修改UserService方法
-
((UserService) AopContext.currentProxy()).test2();
以上就是内部方法调用时,事务不起作用的原因及解决办法。
事务失效 springmvc解决方法连接:https://blog.csdn.net/aosica321/article/details/58039299