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. 場(chǎng)景一
* 1,2,3,5,8,10 - 會(huì)拋出異常錯(cuò)誤,`Transaction silently rolled back because it has been marked as rollback-only`,異常原因是因?yàn)?的事務(wù)傳播特性為REQUIRED,和1在一個(gè)事務(wù)中狱从,由于`serviceB.saveLog`的調(diào)用也被AOP破花,當(dāng)10拋出異常,會(huì)導(dǎo)致當(dāng)前事務(wù)的status設(shè)置為`rollback-only`玉控。而在recharge方法中飞主,異常被catch,AOP會(huì)嘗試commit,但是status已經(jīng)被設(shè)置為`rollback-only`碌识,所以拋出異常
* 2. 場(chǎng)景二(解決場(chǎng)景一報(bào)錯(cuò),但是所有事物回滾)
* 1,2,3,4,5,8,10 - 不會(huì)拋出異常碾篡,所有的操作都會(huì)被回滾 <br>
* 3. 場(chǎng)景三(解決場(chǎng)景一報(bào)錯(cuò),但是外層事務(wù)不回滾)
* 1,2,3,5,9,10 - 不會(huì)拋出異常,serviceB的事務(wù)會(huì)被回滾筏餐,serviceA的事務(wù)提交
* 4. 場(chǎng)景四(場(chǎng)景三種有一個(gè)bug)
* 1,2,3,5,6,9 - 會(huì)拋出異常开泽,serviceA事務(wù)會(huì)被回滾,但是serviceB事務(wù)因?yàn)槭荝EQUIRES_NEW魁瞪,所以被提交了穆律,沒有回滾
* 5. 場(chǎng)景五(解決場(chǎng)景四的bug)
* 1,2,3,5,7,9 - 會(huì)拋出異常,serviceB的事務(wù)會(huì)被回滾导俘,serviceA的事務(wù)被回滾
* 1,2,3,5,7,10 - 不會(huì)拋出異常,serviceB的事務(wù)會(huì)被回滾,serviceA的事務(wù)提交
* 1,2,3,4,5,7,10 - 不會(huì)拋異常, serviceB的事務(wù)會(huì)被回滾峦耘,serviceA的事務(wù)被回滾
* @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<User, Long> {
}
@Repository
public interface UserAccountLogRepository extends JpaRepository<UserAccountLog, Long> {
}
@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;
}
}
- 場(chǎng)景一
1,2,3,5,8,10 - 會(huì)拋出異常錯(cuò)誤,Transaction silently rolled back because it has been marked as rollback-only
,異常原因是因?yàn)?的事務(wù)傳播特性為REQUIRED趟畏,和1在一個(gè)事務(wù)中贡歧,由于serviceB.saveLog
的調(diào)用也被AOP,當(dāng)10拋出異常赋秀,會(huì)導(dǎo)致當(dāng)前事務(wù)的status設(shè)置為rollback-only
利朵。而在recharge方法中,異常被catch猎莲,AOP會(huì)嘗試commit绍弟,但是status已經(jīng)被設(shè)置為rollback-only
,所以拋出異常
- 場(chǎng)景二(解決場(chǎng)景一報(bào)錯(cuò),但是所有事物回滾)
1,2,3,4,5,8,10 - 不會(huì)拋出異常著洼,所有的操作都會(huì)被回滾
- 場(chǎng)景三(解決場(chǎng)景一報(bào)錯(cuò),但是外層事務(wù)不回滾)
1,2,3,5,9,10 - 不會(huì)拋出異常樟遣,serviceB的事務(wù)會(huì)被回滾,serviceA的事務(wù)提交
- 場(chǎng)景四(場(chǎng)景三種有一個(gè)bug)
1,2,3,5,6,9 - 會(huì)拋出異常身笤,serviceA事務(wù)會(huì)被回滾豹悬,但是serviceB事務(wù)因?yàn)槭荝EQUIRES_NEW,所以被提交了液荸,沒有回滾
- 場(chǎng)景五(解決場(chǎng)景四的bug)
1,2,3,5,7,9 - 會(huì)拋出異常瞻佛,serviceB的事務(wù)會(huì)被回滾,serviceA的事務(wù)被回滾
1,2,3,5,7,10 - 不會(huì)拋出異常,serviceB的事務(wù)會(huì)被回滾,serviceA的事務(wù)提交
1,2,3,4,5,7,10 - 不會(huì)拋異常, serviceB的事務(wù)會(huì)被回滾娇钱,serviceA的事務(wù)被回滾