@Detailed explanation and use of Transactional

@Transactional

package org.springframework.transaction.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.core.annotation.AliasFor;
import org.springframework.transaction.TransactionDefinition;

/**
 * Describes a transaction attribute on an individual method or on a class.
 *
 * <p>At the class level, this annotation applies as a default to all methods of
 * the declaring class and its subclasses. Note that it does not apply to ancestor
 * classes up the class hierarchy; methods need to be locally redeclared in order
 * to participate in a subclass-level annotation.
 *
 * <p>This annotation type is generally directly comparable to Spring's
 * {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
 * class, and in fact {@link AnnotationTransactionAttributeSource} will directly
 * convert the data to the latter class, so that Spring's transaction support code
 * does not have to know about annotations. If no rules are relevant to the exception,
 * it will be treated like
 * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute}
 * (rolling back on {@link RuntimeException} and {@link Error} but not on checked
 * exceptions).
 *
 * <p>For specific information about the semantics of this annotation's attributes,
 * consult the {@link org.springframework.transaction.TransactionDefinition} and
 * {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
 *
 * @author Colin Sampaleanu
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 1.2
 * @see org.springframework.transaction.interceptor.TransactionAttribute
 * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute
 * @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

	/**
	 * Alias for {@link #transactionManager}.
	 * @see #transactionManager
	 */
	@AliasFor("transactionManager")
	String value() default "";

	/**
	 * A <em>qualifier</em> value for the specified transaction.
	 * <p>May be used to determine the target transaction manager,
	 * matching the qualifier value (or the bean name) of a specific
	 * {@link org.springframework.transaction.PlatformTransactionManager}
	 * bean definition.
	 * @since 4.2
	 * @see #value
	 */
	@AliasFor("value")
	String transactionManager() default "";

	/**
	 * The transaction propagation type.
	 * <p>Defaults to {@link Propagation#REQUIRED}.
	 * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()
	 */
	Propagation propagation() default Propagation.REQUIRED;

	/**
	 * The transaction isolation level.
	 * <p>Defaults to {@link Isolation#DEFAULT}.
	 * <p>Exclusively designed for use with {@link Propagation#REQUIRED} or
	 * {@link Propagation#REQUIRES_NEW} since it only applies to newly started
	 * transactions. Consider switching the "validateExistingTransactions" flag to
	 * "true" on your transaction manager if you'd like isolation level declarations
	 * to get rejected when participating in an existing transaction with a different
	 * isolation level.
	 * @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel()
	 * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction
	 */
	Isolation isolation() default Isolation.DEFAULT;

	/**
	 * The timeout for this transaction (in seconds).
	 * <p>Defaults to the default timeout of the underlying transaction system.
	 * <p>Exclusively designed for use with {@link Propagation#REQUIRED} or
	 * {@link Propagation#REQUIRES_NEW} since it only applies to newly started
	 * transactions.
	 * @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()
	 */
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

	/**
	 * A boolean flag that can be set to {@code true} if the transaction is
	 * effectively read-only, allowing for corresponding optimizations at runtime.
	 * <p>Defaults to {@code false}.
	 * <p>This just serves as a hint for the actual transaction subsystem;
	 * it will <i>not necessarily</i> cause failure of write access attempts.
	 * A transaction manager which cannot interpret the read-only hint will
	 * <i>not</i> throw an exception when asked for a read-only transaction
	 * but rather silently ignore the hint.
	 * @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly()
	 * @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly()
	 */
	boolean readOnly() default false;

	/**
	 * Defines zero (0) or more exception {@link Class classes}, which must be
	 * subclasses of {@link Throwable}, indicating which exception types must cause
	 * a transaction rollback.
	 * <p>By default, a transaction will be rolling back on {@link RuntimeException}
	 * and {@link Error} but not on checked exceptions (business exceptions). See
	 * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)}
	 * for a detailed explanation.
	 * <p>This is the preferred way to construct a rollback rule (in contrast to
	 * {@link #rollbackForClassName}), matching the exception class and its subclasses.
	 * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}.
	 * @see #rollbackForClassName
	 * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
	 */
	Class<? extends Throwable>[] rollbackFor() default {};

	/**
	 * Defines zero (0) or more exception names (for exceptions which must be a
	 * subclass of {@link Throwable}), indicating which exception types must cause
	 * a transaction rollback.
	 * <p>This can be a substring of a fully qualified class name, with no wildcard
	 * support at present. For example, a value of {@code "ServletException"} would
	 * match {@code javax.servlet.ServletException} and its subclasses.
	 * <p><b>NB:</b> Consider carefully how specific the pattern is and whether
	 * to include package information (which isn't mandatory). For example,
	 * {@code "Exception"} will match nearly anything and will probably hide other
	 * rules. {@code "java.lang.Exception"} would be correct if {@code "Exception"}
	 * were meant to define a rule for all checked exceptions. With more unusual
	 * {@link Exception} names such as {@code "BaseBusinessException"} there is no
	 * need to use a FQN.
	 * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)}.
	 * @see #rollbackFor
	 * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
	 */
	String[] rollbackForClassName() default {};

	/**
	 * Defines zero (0) or more exception {@link Class Classes}, which must be
	 * subclasses of {@link Throwable}, indicating which exception types must
	 * <b>not</b> cause a transaction rollback.
	 * <p>This is the preferred way to construct a rollback rule (in contrast
	 * to {@link #noRollbackForClassName}), matching the exception class and
	 * its subclasses.
	 * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)}.
	 * @see #noRollbackForClassName
	 * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
	 */
	Class<? extends Throwable>[] noRollbackFor() default {};

	/**
	 * Defines zero (0) or more exception names (for exceptions which must be a
	 * subclass of {@link Throwable}) indicating which exception types must <b>not</b>
	 * cause a transaction rollback.
	 * <p>See the description of {@link #rollbackForClassName} for further
	 * information on how the specified names are treated.
	 * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)}.
	 * @see #noRollbackFor
	 * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
	 */
	String[] noRollbackForClassName() default {};

}
attributetypedescribe
valueStringOptional qualified descriptor that specifies the transaction manager to use
transactionManagerStringOptional qualifier descriptor that specifies the qualifier value of the transaction
propagationenum: PropagationOptional transaction propagation behavior settings
isolationenum: isolationOptional transaction isolation level settings
timeoutint (in seconds granularity)Transaction timeout setting
readOnlybooleanRead / write or read-only transactions, read / write by default
rollbackForClass object array, which must inherit from ThrowableArray of exception classes causing transaction rollback
rollbackForClassNameClass name array, which must inherit from ThrowableArray of exception class names that cause transaction rollback
noRollbackForClass object array, which must inherit from ThrowableArray of exception classes that will not cause transaction rollback
noRollbackForClassNameClass name array, which must inherit from ThrowableArray of exception class names that will not cause transaction rollback

@Implementation principle of Transactional

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

@Implementation principle of Transactional:

  1. At the beginning of a transaction, a proxy connection object is generated through the AOP mechanism,
    And put it into a container related to DataSourceTransactionManager in the DataSource instance.
    In the next whole transaction, the customer code should use the connection to connect to the database,
    Execute all database commands.
    [database commands executed without connecting to the database using this connection cannot be rolled back when this transaction is rolled back]
    (physical connection) logically create a new session;
    DataSource (DataSource with the same configuration as TransactionManager)

  2. At the end of the transaction, roll back the database command executed on the proxy connection object obtained in step 1,
    Then close the proxy connection object.
    (after the transaction ends, the rollback operation will not work on the completed SQL operation commands)

Isolation level of transaction:

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) basically not used
  2. @Transactional(isolation = Isolation.READ_COMMITTED): read committed data (non repeatable and unreal reads will occur)
  3. @Transactional(isolation = Isolation.REPEATABLE_READ): repeatable (unreal reading will occur)
  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 a transaction currently exists, join the transaction; If there is no current transaction, a new transaction is created. This is the default.
  2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:
    Create a new transaction. If there is a current transaction, suspend the current transaction.
  3. TransactionDefinition.PROPAGATION_SUPPORTS:
    If a transaction currently exists, join the transaction; If there are currently no transactions, continue to run in a non transactional manner.
  4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
    Run in non transactional mode. If there is a transaction currently, suspend the current transaction.
  5. TransactionDefinition.PROPAGATION_NEVER:
    Run in non transactional mode, and throw an exception if there is a transaction currently.
  6. TransactionDefinition.PROPAGATION_MANDATORY:
    If a transaction currently exists, join the transaction; Throw an exception if there is no current transaction.
  7. TransactionDefinition.PROPAGATION_NESTED:
    If a transaction currently exists, create a transaction to run as a nested transaction of the current transaction;
    If there is no transaction at present, this value is equivalent to transactiondefinition.promotion_ REQUIRED.

spring transaction rollback rule:

  • The recommended method to instruct the spring transaction manager to roll back a transaction is to throw an exception in the context of the current transaction. The spring transaction manager will catch any unhandled exception, and then decide whether to roll back the transaction that threw the exception according to the rules
  • Under the default configuration, spring rolls back the transaction only when the exception thrown is a runtime unchecked exception, that is, throwing a subclass of RuntimeException (Errors will also cause the transaction to roll back), and throwing a checked exception will not cause the transaction to roll back.
  • Using the spring transaction manager, spring is responsible for opening, submitting and rolling back the database. By default, it will roll back in case of a throw new RuntimeException("comment"), that is, it will roll back in case of an unchecked exception; The exception to be caught (throw new RuntimeException("comment") will not be rolled back, that is, when the checked exception is encountered (that is, the exception thrown during non runtime, and the exception detected by the compiler is called the checked exception or the checked exception), we need to specify a way to roll back the transaction. To roll back all exceptions, add @ transactional (rollbackfor)= {exception. Class, other exceptions}), if unchecked exceptions are allowed, do not roll back; @ Transactional(notRollbackFor=RunTimeException.class)

Demo example

@The Transactional annotation supports the setting of 10 attributes. Here, we will only explain the three attributes that are used more frequently: propagation, isolation and rollback for. The propagation attribute is used to enumerate the propagation behavior of transactions, isolation is used to set the transaction isolation level, and rollback for abnormal transactions.

Simulated bank transfer: transfer 500 from user 1 to user 2

Prepare data

@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
  • @ Transactional is not used

Simulate normal transfer

@Service
public class TestUserServiceImpl extends ServiceImpl<TestUserMapper, TestUser> implements TestUserService {

    @Override
    public void testUser(TestUser testUser) throws CoBusinessException {

        // Update user 1, minus 500
        TestUser one = getById(1);
        one.setMoney(500);
        updateById(one);

        // Query whether the amount of user 2 exceeds the maximum value, throw an exception or pass normally
        TestUser two = getById(2);
        if (two.getMoney() > 1400) {
            throw new RuntimeException("The amount is too large");
        }

        // Update user 2's balance
        two.setMoney(1500);
        updateById(two);

    }
}

Changes in database tables

  • Before change

  • After change


Simulated transfer exception

public class TestUserServiceImpl extends ServiceImpl<TestUserMapper, TestUser> implements TestUserService {

    @Override
    public void testUser(TestUser testUser) throws CoBusinessException {

        // Update user 1, minus 500
        TestUser one = getById(1);
        one.setMoney(500);
        updateById(one);

        // Query whether the amount of user 2 exceeds the maximum value, throw an exception or pass normally
        TestUser two = getById(2);
        if (two.getMoney() > 400) {
            throw new RuntimeException("The amount is too large");
        }

        // Update user 2's balance
        two.setMoney(1500);
        updateById(two);

    }
}

Changes in database tables

  • Before change

  • After change


Summary: it can be seen that if @ Transactional is not added, there is no problem under normal circumstances. However, if an exception occurs, it will be found that the first data has been changed, but the second data has not, which leads to data synchronization and will be very bad.

  • Used @ Transactional

Simulate normal transfer

@Service
public class TestUserServiceImpl extends ServiceImpl<TestUserMapper, TestUser> implements TestUserService {

    @Override
    @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void testUser(TestUser testUser) throws CoBusinessException {

        // Update user 1, minus 500
        TestUser one = getById(1);
        one.setMoney(500);
        updateById(one);

        // Query whether the amount of user 2 exceeds the maximum value, throw an exception or pass normally
        TestUser two = getById(2);
        if (two.getMoney() > 1400) {
            throw new RuntimeException("The amount is too large");
        }

        // Update user 2's balance
        two.setMoney(1500);
        updateById(two);

    }
}

Changes in database tables

  • Before change

  • After change


Simulated transfer exception

public class TestUserServiceImpl extends ServiceImpl<TestUserMapper, TestUser> implements TestUserService {

    @Override
    @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void testUser(TestUser testUser) throws CoBusinessException {

        // Update user 1, minus 500
        TestUser one = getById(1);
        one.setMoney(500);
        updateById(one);

        // Query whether the amount of user 2 exceeds the maximum value, throw an exception or pass normally
        TestUser two = getById(2);
        if (two.getMoney() > 400) {
            throw new RuntimeException("The amount is too large");
        }

        // Update user 2's balance
        two.setMoney(1500);
        updateById(two);

    }
}

Changes in database tables

  • Before change

  • After change


Summary: it can be seen that with @ Transactional, no matter in normal or abnormal conditions, there will be no exceptions in the data on both sides

Tags: Spring Back-end

Posted on Sun, 24 Oct 2021 15:06:27 -0400 by SetToLoki