[spring] transaction propagation mechanism

Transactions in Spring

Spring's transaction is actually the transaction operation of the database. It conforms to the ACID standard and also has a standard transaction isolation level.

Transactions in Spring only encapsulate and extend JDBC transactions, and the underlying layer will eventually use this set of JDBC API. However, Spring transactions have their own characteristics, that is, transaction propagation mechanism.

The so-called transaction propagation mechanism is how the transaction is passed in the call of multiple methods. Is it to recreate the transaction or use the transaction of the parent method? Does the rollback of the parent method affect the transactions of the child method? These can be determined through the transaction propagation mechanism.

preparation

Entity class

Area

package com.morris.spring.entity;

import lombok.Data;

import java.io.Serializable;

@Data
public class Area implements Serializable {

	private Integer id;
	private String areaName;
	private Integer areaCode;
}

Good

package com.morris.spring.entity;

import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;

@Data
public class Good implements Serializable {

	private Integer id;
	private String goodName;
	private BigDecimal price;
}

DAO layer

AreaDao

package com.morris.spring.dao;

import com.morris.spring.entity.Area;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

public class AreaDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	public boolean insert(Area area) {
		String sql = "insert into t_area(area_name, area_code) values(?,?)";
		return jdbcTemplate.update(sql, area.getAreaName(), area.getAreaCode()) > 0;
	}
}

GoodDao

package com.morris.spring.dao;

import com.morris.spring.entity.Good;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

public class GoodDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	public boolean insert(Good good) {
		String sql = "insert into t_good(good_name, price) values(?,?)";
		return jdbcTemplate.update(sql, good.getGoodName(), good.getPrice()) > 0;
	}
}

Service layer

AreaServiceImpl

package com.morris.spring.service;


import com.morris.spring.dao.AreaDao;
import com.morris.spring.entity.Area;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public class AreaServiceImpl implements AreaService {

	@Autowired
	private AreaDao areaDao;

	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public boolean addArea(int i) {
		int y = 1000000 / i;
		Area area = new Area();
		area.setAreaCode(y);
		area.setAreaName("shenzhen");
		return areaDao.insert(area);
	}
}

GoodServiceImpl

package com.morris.spring.service;

import com.morris.spring.dao.GoodDao;
import com.morris.spring.entity.Good;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

public class GoodServiceImpl implements GoodService {

	@Autowired
	private GoodDao goodDao;

	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public boolean addGood() {
		Good good = new Good();
		good.setGoodName("iphone");
		good.setPrice(BigDecimal.valueOf(99999));
		return goodDao.insert(good);
	}
}

TransactionService

package com.morris.spring.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Component
public class TransactionService {

	@Autowired
	private GoodService goodService;

	@Autowired
	private AreaService areaService;

	@Transactional(propagation = Propagation.REQUIRED)
	public void addGoodAndArea() {
		System.out.println("------addGoodAndArea-------");
		areaService.addArea(10);
		goodService.addGood();
	}

}

Test class

TransactionPropagationDemo

package com.morris.spring.demo.jdbc;

import com.morris.spring.config.JdbcConfig;
import com.morris.spring.dao.AreaDao;
import com.morris.spring.dao.GoodDao;
import com.morris.spring.service.AreaServiceImpl;
import com.morris.spring.service.GoodServiceImpl;
import com.morris.spring.service.TransactionService;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * Communication mechanism of transaction
 */
public class TransactionPropagationDemo {

	@Test
	public void test() {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
		applicationContext.register(GoodDao.class);
		applicationContext.register(AreaDao.class);
		applicationContext.register(GoodServiceImpl.class);
		applicationContext.register(AreaServiceImpl.class);
		applicationContext.register(JdbcConfig.class);
		applicationContext.register(TransactionService.class);
		applicationContext.refresh();

		TransactionService transactionService = applicationContext.getBean(TransactionService.class);
		transactionService.addGoodAndArea();
	}

}

Communication mechanism

For specific options, refer to enumeration class org Springframework transaction. annotation. Propagation.

optionexplain
REQUIREDDefault options. If there is no current transaction, create a new transaction. If there is already a transaction, join it.
SUPPORTSThe current transaction is supported. If there is no current transaction, it will be executed in a non transaction manner.
MANDATORYUse the current transaction. If there is no current transaction, throw an exception.
REQUIRES_NEWCreate a new transaction. If there is a current transaction, suspend the current transaction.
NOT_SUPPORTEDPerform operations in a non transactional manner. If there is a transaction currently, suspend the current transaction.
NEVERExecute in a non transactional manner. If there is a transaction currently, an exception will be thrown.
NESTEDIf a transaction currently exists, it is executed within a nested transaction. If there is currently no transaction, perform an operation similar to REQUIRED.

REQUIRED

Default options. If there is no current transaction, create a new transaction. If there is already a transaction, join it.

The configuration is as follows:

  • TransactionService: REQUIRED
  • AreaServiceImpl: REQUIRED
  • GoodServiceImpl: REQUIRED

Run the above Demo and the results are as follows:

// Create first transaction
DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
// Create first connection
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction
DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit
------addGoodAndArea-------																				   
// Execute in the first transaction
DEBUG DataSourceTransactionManager:487 - Participating in existing transaction
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
// Execute in the first transaction
DEBUG DataSourceTransactionManager:487 - Participating in existing transaction
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)]
// Commit the first transaction
DEBUG DataSourceTransactionManager:763 - Initiating transaction commit
DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162]
DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction

Summary:

  • When there is no transaction in the outer layer, transactionservice The addgoodandarea () method found that no transaction was available, so it created a new transaction by itself.
  • goodService.addGood() and areaservice If an existing transaction is found during the execution of addarea(), the current transaction will be used for execution.

REQUIRES_NEW

Create a new transaction. If there is a current transaction, suspend the current transaction.

The configuration is as follows:

  • TransactionService: REQUIRED
  • GoodServiceImpl: REQUIRES
  • AreaServiceImpl: REQUIRES_NEW

The operation results are as follows:

// Create first transaction
DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
// Create first connection
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] for JDBC transaction
DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] to manual commit
------addGoodAndArea-------																			   
// Suspend the first transaction and create a second transaction
DEBUG DataSourceTransactionManager:446 - Suspending current transaction, creating new transaction with name [com.morris.spring.service.AreaServiceImpl.addArea]
// Create a second connection
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@116fc68] for JDBC transaction
DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@116fc68] to manual commit
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)]
// Commit the second transaction
DEBUG DataSourceTransactionManager:763 - Initiating transaction commit
DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@116fc68]
DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@116fc68] after transaction
// Resume first transaction
DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction
// Execute in the first transaction
DEBUG DataSourceTransactionManager:487 - Participating in existing transaction
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
DEBUG DataSourceTransactionManager:763 - Initiating transaction commit
DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66]
DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] after transaction

Summary: areaserviceimpl When addarea() finds an existing transaction during execution, it suspends the current transaction and resumes after execution.

SUPPORTS

The current transaction is supported. If there is no current transaction, it will be executed in a non transaction manner.

The configuration is as follows:

  • TransactionService: SUPPORTS
  • AreaServiceImpl: REQUIRED
  • GoodServiceImpl: SUPPORTS

The operation results are as follows:

// TransactionService.addGoodAndArea runs in a non transactional manner
------addGoodAndArea-------
// Start the first transaction
DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.AreaServiceImpl.addArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
// Create first connection
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@1691f3d] for JDBC transaction
DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1691f3d] to manual commit
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)]
// Commit the first transaction
DEBUG DataSourceTransactionManager:763 - Initiating transaction commit
DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@1691f3d]
DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1691f3d] after transaction
DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction
// AreaServiceImpl.addArea runs in a non transactional manner
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]

Summary: Currently there is no transaction, transactionservice Addgoodandarea() and areaserviceimpl Addarea() runs in a non transactional manner.

Modify the configuration as follows:

  • TransactionService: REQUIRED
  • AreaServiceImpl: SUPPORTS
  • GoodServiceImpl: SUPPORTS

The operation results are as follows:

// Start the first transaction
DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
// Create first connection
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@11158fb] for JDBC transaction
DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@11158fb] to manual commit
------addGoodAndArea-------																		  
// Execute in the first transaction
DEBUG DataSourceTransactionManager:487 - Participating in existing transaction
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)]
// Execute in the first transaction
DEBUG DataSourceTransactionManager:487 - Participating in existing transaction
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
// Commit the first transaction
DEBUG DataSourceTransactionManager:763 - Initiating transaction commit
DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@11158fb]
DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@11158fb] after transaction

Summary: there are currently transactions, goodserviceimpl Addgood() and areaserviceimpl Addarea() runs in transactional mode.

MANDATORY

Use the current transaction. If there is no current transaction, throw an exception.

The configuration is as follows:

  • TransactionService: REQUIRED
  • AreaServiceImpl: REQUIRED
  • GoodServiceImpl: MANDATORY

The operation results are as follows:

// Create first transaction
EBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
// Create first connection
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@11c4a3f] for JDBC transaction
DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@11c4a3f] to manual commit
------addGoodAndArea-------																  
// Execute in the first transaction
DEBUG DataSourceTransactionManager:487 - Participating in existing transaction
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)]
// Execute in the first transaction
DEBUG DataSourceTransactionManager:487 - Participating in existing transaction
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
// Commit the first transaction
DEBUG DataSourceTransactionManager:763 - Initiating transaction commit
DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@11c4a3f]
DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@11c4a3f] after transaction

Summary: there are currently transactions, goodserviceimpl Addgood () runs in transactional mode.

Modify the configuration as follows:

  • TransactionService: SUPPORTS
  • AreaServiceImpl: MANDATORY
  • GoodServiceImpl: SUPPORTS

The operation results are as follows:

------addGoodAndArea-------
DEBUG DataSourceTransactionManager:888 - Should roll back transaction but cannot - no transaction available

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

	at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:372)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:374)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:205)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:219)
	at com.sun.proxy.$Proxy23.addArea(Unknown Source)
	at com.morris.spring.service.TransactionService.addGoodAndArea(TransactionService.java:20)

Summary: Currently there is no transaction, areaserviceimpl Addarea() throws an exception.

NOT_SUPPORTED

Perform operations in a non transactional manner. If there is a transaction currently, suspend the current transaction.

The configuration is as follows:

  • TransactionService: REQUIRED
  • AreaServiceImpl: NOT_SUPPORTED
  • GoodServiceImpl: NOT_SUPPORTED

The operation results are as follows:

// Start the first transaction
DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction
DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit
------addGoodAndArea-------
// Suspend first transaction
DEBUG DataSourceTransactionManager:436 - Suspending current transaction
// Run as non transactional
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)]
DEBUG DataSourceUtils:115 - Fetching JDBC Connection from DataSource
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
// Resume first transaction
DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction
// Suspend first transaction
DEBUG DataSourceTransactionManager:436 - Suspending current transaction
// Run as non transactional
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
DEBUG DataSourceUtils:115 - Fetching JDBC Connection from DataSource
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
// Resume first transaction
DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction
// Commit the first transaction
DEBUG DataSourceTransactionManager:763 - Initiating transaction commit
DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162]
DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction

Summary: execute goodserviceimpl Addgood() and areaserviceimpl If there is a current transaction when addarea(), suspend the current transaction.

NEVER

Execute in a non transactional manner. If there is a transaction currently, an exception will be thrown.

The configuration is as follows:

  • TransactionService: NEVER
  • AreaServiceImpl: NEVER
  • GoodServiceImpl: NEVER

The operation results are as follows:

// No transactions run in a non transactional manner
------addGoodAndArea-------
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)]
DEBUG DataSourceUtils:115 - Fetching JDBC Connection from DataSource
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]

Summary: Currently, no transactions are executed in non transaction mode.

Modify the configuration as follows:

  • TransactionService: REQUIRED
  • AreaServiceImpl: NEVER
  • GoodServiceImpl: NEVER

The operation results are as follows:

// Start the first transaction
DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction
DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit
------addGoodAndArea-------
DEBUG DataSourceTransactionManager:864 - Initiating transaction rollback
DEBUG DataSourceTransactionManager:345 - Rolling back JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162]
DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction

org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'

	at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:430)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:362)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:374)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:205)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:219)
	at com.sun.proxy.$Proxy23.addArea(Unknown Source)
	at com.morris.spring.service.TransactionService.addGoodAndArea(TransactionService.java:20)

Summary: areaserviceimpl An exception will be thrown if there is a transaction when addarea() is executed.

NESTED

If a transaction currently exists, it is executed within a nested transaction. If there are currently no transactions, perform an operation similar to REQUIRED.

The configuration is as follows:

  • TransactionService: REQUIRED
  • GoodServiceImpl: NESTED
  • AreaServiceImpl: NESTED

The operation results are as follows:

// Create first transaction
EBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
// Create first connection
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction
DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit
------addGoodAndArea-------
// Create rollback point
DEBUG DataSourceTransactionManager:466 - Creating nested transaction with name [com.morris.spring.service.AreaServiceImpl.addArea]
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)]
// Release rollback point
DEBUG DataSourceTransactionManager:754 - Releasing transaction savepoint
// Create rollback point
DEBUG DataSourceTransactionManager:466 - Creating nested transaction with name [com.morris.spring.service.GoodServiceImpl.addGood]
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
// Release rollback point
DEBUG DataSourceTransactionManager:754 - Releasing transaction savepoint
DEBUG DataSourceTransactionManager:763 - Initiating transaction commit
DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162]
DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction

If you don't throw an exception, both REQUIRED and needed are used. The difference is that an exception occurs. The following shows the difference between REQUIRED and needed when an exception occurs:

The configuration is as follows:

  • TransactionService: REQUIRED
  • AreaServiceImpl: REQUIRED
  • GoodServiceImpl: REQUIRED

TransactionService.addGoodAndArea is modified as follows:

@Transactional(propagation = Propagation.REQUIRED)
public void addGoodAndArea() {
	System.out.println("------addGoodAndArea-------");
	try {
		areaService.addArea(0);
	} catch (Exception e) {
		e.printStackTrace();
	}
	goodService.addGood();
}

The operation results are as follows:

DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] for JDBC transaction
DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] to manual commit
------addGoodAndArea-------
DEBUG DataSourceTransactionManager:487 - Participating in existing transaction
DEBUG DataSourceTransactionManager:877 - Participating transaction failed - marking existing transaction as rollback-only
DEBUG DataSourceTransactionManager:360 - Setting JDBC transaction [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] rollback-only
java.lang.ArithmeticException: / by zero
	at com.morris.spring.service.AreaServiceImpl.addArea(AreaServiceImpl.java:18)
... ...
DEBUG DataSourceTransactionManager:487 - Participating in existing transaction
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
DEBUG DataSourceTransactionManager:723 - Global transaction is marked as rollback-only but transactional code requested commit
DEBUG DataSourceTransactionManager:877 - Participating transaction failed - marking existing transaction as rollback-only
DEBUG DataSourceTransactionManager:360 - Setting JDBC transaction [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] rollback-only
DEBUG DataSourceTransactionManager:723 - Global transaction is marked as rollback-only but transactional code requested commit
DEBUG DataSourceTransactionManager:864 - Initiating transaction rollback
DEBUG DataSourceTransactionManager:345 - Rolling back JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66]
DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] after transaction

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

	at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:905)
... ...

From the running results, it can be found that all transactions have been rolled back.

Modify the above configuration as follows:

  • TransactionService: REQUIRED
  • AreaServiceImpl: NESTED
  • GoodServiceImpl: NESTED

The operation results are as follows:

DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction
DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit
------addGoodAndArea-------
DEBUG DataSourceTransactionManager:466 - Creating nested transaction with name [com.morris.spring.service.AreaServiceImpl.addArea]
DEBUG DataSourceTransactionManager:857 - Rolling back transaction to savepoint
java.lang.ArithmeticException: / by zero
	at com.morris.spring.service.AreaServiceImpl.addArea(AreaServiceImpl.java:18)
... ...
DEBUG DataSourceTransactionManager:466 - Creating nested transaction with name [com.morris.spring.service.GoodServiceImpl.addGood]
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
DEBUG DataSourceTransactionManager:754 - Releasing transaction savepoint
DEBUG DataSourceTransactionManager:763 - Initiating transaction commit
DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162]
DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction

From the running results, we can find areaservice Addarea () rolled back (no content was submitted), goodservice The content of addgood () is submitted.

Note that only runtime exceptions and exceptions specified by rollbakcFor will be rolled back.

Tags: Spring AOP Dynamic Proxy

Posted by vargadanis on Sat, 16 Apr 2022 12:37:16 +0930