Spring’s declarative transactions are easy to implement. You can bind Transactional controls to methods requiring Transactional control by adding the @Transactional annotation to them. But the configuration of the parameters is outlined today, and there is an AOP trap that needs your attention.

Propagation properties The characteristics of
REQUIRED The default propagation property, indicating that if a transaction exists in the current environment, the transaction is kept executing, otherwise a new transaction is opened and executed in a new transaction
REQUIRES_NEW Create a new transaction and execute it in the new transaction regardless of whether there are transactions in the current environment, suspending the original transaction
SUPPORTS Indicates that if a transaction exists in the current environment, it is executed in that transaction; otherwise, it is not executed in transactional mode
NOT_SUPPORTED Indicates that this method does not control transactions and suspends if there are transactions in the current environment
MANDATORY Indicates that this method must be executed in a transaction, throwing an exception if there is no transaction in the current environment
NEVER Indicates that this method can run without transaction control and throws an exception as soon as a transaction is propagated
NESTED Indicates that the transaction is run in a nested transaction if it exists, or executed on the REQUIRED attribute if no transaction exists

Common attributes are REQUIRED and REQUIRES_NEW. Let’s use code to verify these two common attributes:

1. Open transaction support @ EnableTransactionManagement

/ * * *@author wangzhi
 */
@SpringBootApplication
@EnableTransactionManagement
public class DemoApplication {
    public static void main(String[] args) {
        newSpringApplication(DemoApplication.class).run(args); }}Copy the code

2. Entity classes and databases

/ * * *@author wangzhi
 */
@Data
@TableName("course")
public class CourseEntity {
    
    @TableId(type = IdType.AUTO)
    private Integer id;
    
    private String courseName;
    
    private BigDecimal price;
}
Copy the code

3. Service layer business code

Let’s first check REQUIRED

/ * * *@author wangzhi
 */
@Service
public class TransactionService {

    @Autowired
    private CourseMapper courseMapper;

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public void save(a) {
        CourseEntity course = new CourseEntity();
        course.setCourseName("Chinese");
        course.setPrice(new BigDecimal(100));
        courseMapper.insert(course);
        // create an exception
        System.out.println(1/0);
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public void saveInit(a) {
        CourseEntity course = new CourseEntity();
        course.setCourseName("Mathematics");
        course.setPrice(new BigDecimal(100));
        courseMapper.insert(course);
        // Call the save method
        try{
            save();
        }catch(Exception e){ e.printStackTrace(); }}}Copy the code

4. How many records does the database have?

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = DemoApplication.class)
class DemoApplicationTests {

    @Autowired
    TransactionService transactionService;

    @Test
    public void transactionTest(a) throws Exception { transactionService.saveInit(); }}Copy the code

5. Look at the results

4. Calm down. REQUIRES_NEW

/ * * *@author wangzhi
 */
@Service
public class TransactionService {

    @Autowired
    private CourseMapper courseMapper;

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public void save(a) {
        CourseEntity course = new CourseEntity();
        course.setCourseName("Chinese");
        course.setPrice(new BigDecimal(100));
        courseMapper.insert(course);
        // create an exception
        System.out.println(1/0);
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public void saveInit(a) {
        CourseEntity course = new CourseEntity();
        course.setCourseName("Mathematics");
        course.setPrice(new BigDecimal(100));
        courseMapper.insert(course);
        // Call the save method
        try {
            save();
        }catch(Exception e){ e.printStackTrace(); }}}Copy the code

5. This is not a problem with transaction propagation, but with dynamic proxies. Call each other transaction methods in the same class, cutting into the method that initiated the transaction, and whatever transaction method is called internally will default to this object to invoke normal methods. These transaction annotations simply don’t work. Transaction is based on the dynamic objects generated by the dynamic proxy to perform the control of the transaction, so calling other transaction methods within the same method must obtain its corresponding proxy object to call, otherwise it must be placed in a different class. Ending method:

6. The introduction of AOP

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
Copy the code

7.@EnableAspectJAutoProxy(exposeProxy = true)

/ * * *@author wangzhi
 */
@SpringBootApplication
@EnableTransactionManagement
@MapperScan("com.example.mapper")
@EnableAspectJAutoProxy(exposeProxy = true)
public class DemoApplication {
    public static void main(String[] args) {
        newSpringApplication(DemoApplication.class).run(args); }}Copy the code

8. Revamp service

/ * * *@author wangzhi
 */
@Service
public class TransactionService {

    @Autowired
    private CourseMapper courseMapper;

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public void save(a) {
        CourseEntity course = new CourseEntity();
        course.setCourseName("Chinese");
        course.setPrice(new BigDecimal(100));
        courseMapper.insert(course);
        // create an exception
        System.out.println(1/0);
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public void saveInit(a) {
        CourseEntity course = new CourseEntity();
        course.setCourseName("Mathematics");
        course.setPrice(new BigDecimal(100));
        courseMapper.insert(course);
        // Call the save method
        try {
            TransactionService proxy = (TransactionService)AopContext.currentProxy();
            proxy.save();
        }catch(Exception e){ e.printStackTrace(); }}}Copy the code

Check out the results: