Propagation.NESTED传播注解的用途使用

package com.test;

import java.math.BigDecimal;
import java.util.Optional;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import lombok.Data;

@SpringBootApplication
@EnableJpaRepositories(considerNestedRepositories = true)
@ComponentScan
@EntityScan
@EnableTransactionManagement
public class NetestTransactionTest {

    public static void main(String[] args) {
        SpringApplication.run(NetestTransactionTest.class, args);

    }

    @RestController
    public static class TestController {
        @Autowired
        private ServiceA serviceA;

        @RequestMapping("/test")
        public void test() {
            User u = new User();
            u.setUsername("张三");
            u.setAge(31);
            serviceA.recharge(u, BigDecimal.TEN);
        }
    }

    @Service
    public static class ServiceA {

        @Autowired
        private UserRepository rep;

        @Autowired
        private ServiceB serviceB;

        public User save(User user) {
            return rep.save(user);
        }

        /**
         * 1. 场景一 
         *  1,2,3,5,8,10 - 会抛出异常错误,`Transaction silently rolled back because it has been marked as rollback-only`,异常原因是因为8的事务传播特性为REQUIRED,和1在一个事务中,由于`serviceB.saveLog`的调用也被AOP,当10抛出异常,会导致当前事务的status设置为`rollback-only`。而在recharge方法中,异常被catch,AOP会尝试commit,但是status已经被设置为`rollback-only`,所以抛出异常
         * 2. 场景二(解决场景一报错,但是所有事物回滚) 
         *  1,2,3,4,5,8,10 - 不会抛出异常,所有的操作都会被回滚 
* 3. 场景三(解决场景一报错,但是外层事务不回滚) * 1,2,3,5,9,10 - 不会抛出异常,serviceB的事务会被回滚,serviceA的事务提交 * 4. 场景四(场景三种有一个bug) * 1,2,3,5,6,9 - 会抛出异常,serviceA事务会被回滚,但是serviceB事务因为是REQUIRES_NEW,所以被提交了,没有回滚 * 5. 场景五(解决场景四的bug) * 1,2,3,5,7,9 - 会抛出异常,serviceB的事务会被回滚,serviceA的事务被回滚 * 1,2,3,5,7,10 - 不会抛出异常,serviceB的事务会被回滚,serviceA的事务提交 * 1,2,3,4,5,7,10 - 不会抛异常, serviceB的事务会被回滚,serviceA的事务被回滚 * @param user * @param amount */ @Transactional(propagation = Propagation.REQUIRED) public void recharge(User user, BigDecimal amount) { user.setBalance(Optional.ofNullable(user.getBalance()).map(b -> b.add(amount)).orElse(amount));; rep.save(user); // 1 UserAccountLog log = new UserAccountLog(); log.setUserId(user.getId()); log.setEffectAmount(amount); try { serviceB.saveLog(log); // 2 } catch (Exception e) { serviceB.saveLogBackoff(log); // 3 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // 4 } user.setAge(user.getAge() + 1); rep.save(user); // 5 // int i = 1 / 0; // 6 } } @Service public static class ServiceB { @Autowired private UserAccountLogRepository rep; @Transactional(propagation = Propagation.NESTED) // 7 // @Transactional(propagation = Propagation.REQUIRED) // 8 // @Transactional(propagation = Propagation.REQUIRES_NEW) // 9 public void saveLog(UserAccountLog log) { rep.save(log); int i = 1 / 0; // 10 } @Transactional public void saveLogBackoff(UserAccountLog log) { // save log back off System.out.println("save log back off"); } } @Repository public interface UserRepository extends JpaRepository { } @Repository public interface UserAccountLogRepository extends JpaRepository { } @Data @Entity @Table(name = "user") public static class User { @Id @GeneratedValue private Long id; private String username; private Integer age; private BigDecimal balance; } @Data @Entity @Table(name = "user_account_log") private static class UserAccountLog { @Id @GeneratedValue private Long id; private Long userId; private BigDecimal effectAmount; } }
  1. 场景一
    1,2,3,5,8,10 - 会抛出异常错误,Transaction silently rolled back because it has been marked as rollback-only,异常原因是因为8的事务传播特性为REQUIRED,和1在一个事务中,由于serviceB.saveLog的调用也被AOP,当10抛出异常,会导致当前事务的status设置为rollback-only。而在recharge方法中,异常被catch,AOP会尝试commit,但是status已经被设置为rollback-only,所以抛出异常
  2. 场景二(解决场景一报错,但是所有事物回滚)
    1,2,3,4,5,8,10 - 不会抛出异常,所有的操作都会被回滚
  3. 场景三(解决场景一报错,但是外层事务不回滚)
    1,2,3,5,9,10 - 不会抛出异常,serviceB的事务会被回滚,serviceA的事务提交
  4. 场景四(场景三种有一个bug)
    1,2,3,5,6,9 - 会抛出异常,serviceA事务会被回滚,但是serviceB事务因为是REQUIRES_NEW,所以被提交了,没有回滚
  5. 场景五(解决场景四的bug)
    1,2,3,5,7,9 - 会抛出异常,serviceB的事务会被回滚,serviceA的事务被回滚
    1,2,3,5,7,10 - 不会抛出异常,serviceB的事务会被回滚,serviceA的事务提交
    1,2,3,4,5,7,10 - 不会抛异常, serviceB的事务会被回滚,serviceA的事务被回滚

你可能感兴趣的:(Propagation.NESTED传播注解的用途使用)