Spring-test transaction automatic rollback and @Transactional detailed explanation

Spring-test transaction is automatically rolled back

During the test, the addUser() method was used to insert a record into the database. It turned out that the unit test was passed, but a record was not inserted into the database. Looking at the log, it was found that Spring-test rolled back the transaction.
In order to prevent the test data from polluting the database, when using Spring-test for unit testing, the transaction will be rolled back by default, that is, @Rollback is true by default . If you want the test data not to be rolled back, you can set @Rollback( value = false).

@Test
@Transactional
@Rollback(value = false)
public void consumerTest(){
    
    
    Consumer consumer = new Consumer();
    consumer.setUsername("栗子");
    consumer.setPassword("!@#");
    consumer.setSex(new Byte("0"));
    consumer.setPhoneNum("12222544441");
    consumer.setEmail("[email protected]");
    consumer.setBirth(new Date());
    consumer.setIntroduction("");
    consumer.setLocation("");
    consumer.setAvator("/img/user.jpg");
    consumer.setCreateTime(new Date());
    consumer.setUpdateTime(new Date());
    
    System.out.println(consumerService.addUser(consumer));
}

If you are using a MySQL database, after setting automatic rollback, if you find that the transaction is still not rolled back, you can check whether the database engine is Innodb, because other database engines such as MyISAM and Memory do not support transactions.

@Transactional Detailed

@Transactional is an annotation used in declarative transaction management programming

1. Add location

1) The interface implementation class or interface implementation method, not the interface class.
2) Access authority: The public method only works. The @Transactional annotation should only be applied to public methods, which is determined by the nature of Spring AOP .
System design: Place tags on methods that require transaction management, rather than on all interface implementation classes: read-only interfaces do not require transaction management, because @Transactional is configured , AOP interception and transaction processing are required . Affect system performance.

3) Wrong use:

1. There are two methods A and B in the interface. A has no @Transactional tag, and B has. The upper layer calls B indirectly through A, and the transaction does not take effect at this time.

2. The exception (runtime exception) in the interface is caught but not thrown. In the default configuration, Spring rolls back the transaction only when the thrown exception is a runtime unchecked exception, that is, the thrown exception is a subclass of RuntimeException (Errors will also cause the transaction to roll back), and a checked exception is thrown. Will not cause the transaction to roll back. It can be configured via @Transactional rollbackFor.

3. Transaction management under multi-threading. Because threads are not managed by spring, threads cannot use spring transactions by default, nor can they obtain spring-injected beans. Multi-threading is enabled in methods managed by spring declarative transaction, and methods in multi-threads are not controlled by transactions. A method that uses @Transactional, if the method contains the use of multiple threads, and an exception occurs inside the method, the transaction that calls the method in the thread will not be rolled back.

2. Declarative transaction management implementation:

@Transactional Annotation
@Transactional essentially uses JDBC transactions for transaction control
@Transactional Spring-based dynamic proxy mechanism

@Transactional implementation principle:

  1. When the transaction starts, through the AOP mechanism, a proxy connection object is generated and placed in a container of the DataSource instance related to the DataSourceTransactionManager. In the next whole transaction, the client code should use the connection to connect to the database and execute all database commands. [Database commands executed without using the connection to connect to the database will not be rolled back when the transaction is rolled back] (The physical connection connection is logically created to create a new session session; DataSource and TransactionManager are configured with the same data source)
  2. At the end of the transaction, roll back the database command executed on the proxy connection object obtained in step 1, and then close the proxy connection object. After the transaction ends, the rollback operation will not affect the executed SQL operation commands)

3. The essence of declarative transaction management:

There are two ways to
open the transaction : display start transaction | begin, end the transaction through commit | rollback,
close the autocommit in the database autocommit set autocommit = 0; MySQL turns on autocommit by default; end the transaction by manually committing or performing a rollback operation

Spring turns off automatic submission in the database: Turn off automatic submission before the method is executed, and then turn on automatic submission after the method is executed.
org.springframework.jdbc.datasource.DataSourceTransactionManager.java source code implementation:

@Override
	protected void doBegin(Object transaction, TransactionDefinition definition) {
    
    
	…………………………
		if (con.getAutoCommit()) {
    
    
				txObject.setMustRestoreAutoCommit(true);
				if (logger.isDebugEnabled()) {
    
    
					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
				}
				con.setAutoCommit(false);
			}
	…………………………
}
@Override
protected void doCleanupAfterCompletion(Object transaction) {
    
    
	DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    ……………………………
	// Reset connection.
	Connection con = txObject.getConnectionHolder().getConnection();
	try {
    
    
		if (txObject.isMustRestoreAutoCommit()) {
    
    
			con.setAutoCommit(true);
		}
		DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
	}
	catch (Throwable ex) {
    
    
		logger.debug("Could not reset JDBC Connection after transaction", ex);
	}
	…………………………
}

problem:

After the automatic commit is turned off, if the transaction has not been completed, that is, how to deal with the SQL operations that have been executed when the commit or rollback is not executed manually?

C3P0's default strategy is to roll back any uncommitted transactions.
C3P0 is an open source JDBC connection pool that implements data source and JNDI binding, and supports JDBC3 specification and JDBC2 standard extensions. Open source projects currently using it include Hibernate, Spring, etc.
JNDI (Java Naming and Directory Interface, Java Naming and Directory Interface) is a standard Java naming system interface provided by SUN. JNDI provides a unified client API, through different The access provider interface JNDI Service Supply Interface (SPI) is implemented. The manager maps the JNDI API to a specific naming service and directory system, so that Java applications can interact with these naming services and directory services.

4. Spring transaction features

All spring transaction management strategy classes inherit from the org.springframework.transaction.PlatformTransactionManager interface

public interface PlatformTransactionManager {
    
    
  TransactionStatus getTransaction(TransactionDefinition definition)
  throws TransactionException;
  void commit(TransactionStatus status) throws TransactionException;
  void rollback(TransactionStatus status) throws TransactionException;

Transaction isolation level: refers to the degree of isolation between several concurrent transactions

  1. @Transactional(isolation = Isolation.READ_UNCOMMITTED): Read uncommitted data (dirty reads will occur, non-repeatable reads) basically not used

  2. @Transactional(isolation = Isolation.READ_COMMITTED): Read the submitted data (non-repeatable reads and phantom reads will appear)

  3. @Transactional(isolation = Isolation.REPEATABLE_READ): Repeatable reading (phantom reading will appear)

  4. @Transactional(isolation = Isolation.SERIALIZABLE): serialization

Transaction propagation behavior: If a transaction context already exists before starting the current transaction, there are several options to specify the execution behavior of a transactional method

  1. TransactionDefinition.PROPAGATION_REQUIRED:
    If there is a transaction currently, join the transaction; if there is no transaction currently, create a new transaction. This is the default value.

  2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:
    Create a new transaction, if there is a transaction currently, suspend the current transaction.

  3. TransactionDefinition.PROPAGATION_SUPPORTS:
    If there is currently a transaction, then join the transaction; if there is no current transaction, continue to run in a non-transactional manner.

  4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
    Run in a non-transactional mode. If there is a transaction currently, the current transaction is suspended.

  5. TransactionDefinition.PROPAGATION_NEVER:
    Run in a non-transactional mode. If there is a transaction currently, an exception will be thrown.

  6. TransactionDefinition.PROPAGATION_MANDATORY:
    If there is a transaction currently, join the transaction; if there is no transaction currently, an exception is thrown.

  7. TransactionDefinition.PROPAGATION_NESTED:
    If there is currently a transaction, create a transaction to run as a nested transaction of the current transaction;
    if there is no current transaction, the value is equivalent to TransactionDefinition.PROPAGATION_REQUIRED.

Insert picture description here

Field description in the above table:

  1. value: Mainly used to specify different transaction managers; mainly used to satisfy that there are different transaction managers in the same system. For example, in Spring, two transaction managers txManager1 and txManager2 are declared. Then, users can specify a specific txManager according to their needs based on this parameter.

  2. Value Applicable scenario: In a system, if multiple data sources or multiple databases need to be accessed, multiple transaction managers must be configured.

  3. REQUIRED_NEW: Internal transactions run independently and can be rolled back or submitted independently in their respective scopes; external transactions will not be affected by the rollback status of internal transactions.

  4. ESTED transactions are managed based on a single transaction and provide multiple save points. This mechanism of multiple save points allows internal transaction changes to trigger the rollback of external transactions. After the external transaction is rolled, the transaction processing can still be continued, even if some operations have been rolled. Since this setting is based on the JDBC savepoint, it can only work in the JDBC mechanism.

  5. rollbackFor: Let the checked exception roll back; that is, let the rollback operation that should not be rolled back.

  6. noRollbackFor: Ignore non-checked exceptions; that is, no rollback operation should be performed that should be rolled back.

Insert picture description here
Insert picture description here
Insert picture description here

5. Other

  1. Nested invocations of transaction methods will produce transaction propagation.

  2. Spring's transaction management is thread-safe.

  3. The @Transactional declared by the parent class will perform transaction enhancement on all methods of the subclass; the subclass override overrides the parent class to override the declaration configuration in @Transactional.

  4. @Transactional is used above the class name, and the methods in the class can override the @Transactional configuration on the class through the attribute configuration; for example, the global configuration on the class is readable and writable, and a method can be changed to read-only.

Insert picture description here
If the runtime exception is not handled, then after the runtime exception occurs, either the thread terminates or the main program terminates.
If you do not want to terminate, you must catch all runtime exceptions and never let this processing thread exit. Abnormal data appears in the queue, the normal processing should be to discard the abnormal data, and then record the log. The processing of normal data below should not be affected by abnormal data.

Non-runtime exceptions are exceptions other than RuntimeException, all of which belong to the Exception class and its subclasses. Such as IOException, SQLException, etc. and user-defined Exception. For this kind of exception, the JAVA compiler forces us to catch and handle these exceptions, otherwise the program will not be compiled. Therefore, in the face of such an exception, whether we want it or not, we can only write a lot of catch blocks to deal with possible exceptions.

Insert picture description here

Guess you like

Origin blog.csdn.net/m0_46864744/article/details/112798388