Cause problems

This morning sentry reported an error, Transaction rolled back because it has been marked as rollback-only, this Transaction will be rolled back because it was marked as rollback before.

In fact, this is not the first time I have encountered, but I have not been able to get to the root of the problem to find out why, just recently in writing a simple transaction manager, this is not caught in a hammer.

Taking a look at the code, the basic model looks like this:

@Transactional
class A{
    public void testA(){// try{classb.testb (); }catch(Ex e){ log.info("Do not let sub-business affect the main process, do not throw exceptions only record");
        }
        return;
    }
}


@Transactional
ClassB{
    pulbic void testB() throws Exception{// Operates on some databases}}Copy the code

The Transactional annotation @Transactional will use cglib to implement an AOP proxy using the current method or class. This proxy simply adds a judgment, A Connection object is used to submit a statement to the database.

The spring transaction propagation mechanism is Required by default, meaning that if there is a transaction, it will be added to the transaction without re-creating a new transaction with db.getConnection. However, it is this operation that causes the error.

This is because a DeadLockException occurs when a ClassB method is called in ClaasA, and is caught in ClassB Aop’s enhanced object after viewing the log.

Run another completeTransactionAfterThrowing treatment methods, this method back to figure out whether the current enhanced the outermost layer of the transaction, because actually away from the transaction processor is used a ThreadLocal variables, It holds the Connection object of the currently acquired database, and this object is passed through the thread. AOP is then nested layer by layer, such as ClassA layer, ClassB layer, ClassA call ClassB, Commit () will not be called when the ClassB call is finished. It is not the outermost call, and only the outermost call will be returned. The commit() operation can be performed to ensure the integrity of the entire service, and the rollback() operation is also performed on the outermost part of the entire service.

Threadlocal
rollbackonly
true
Exception
ClassA
ClassA
commit()
roobackonly

Transaction rolled back because it has been marked as rollback-only
ClassB
ClassA
ClassB

TestB () adds a @transactional (Propagation = Propagation.NESTED) method to specify the propagation hierarchy of NESTED transactions. This is actually a Mysql provides a function, can achieve partial rollback (this feeling a little magic), is actually inside the inner affairs with a mark point, if there is something wrong with the inner affairs, will roll back to the record, if successful the outermost transaction submitted after I followed the submitted together, if an error on my own, Then I will roll back, not affect you ClassA. I have to say that the Spring framework is very perfect, we are wrong to blame him…

Transaction propagation mechanisms are posted below to warn: PROPAGATION_REQUIRED: Spring’s default propagation level, PROPAGATION_REQUIRED: If a transaction exists in the context, add the current transaction, or create a new transaction if none exists.

PROPAGATION_SUPPORTS: Joins the current transaction if a transaction exists in the context, or executes non-transactionally if no transaction exists. PROPAGATION_MANDATORY: This propagation level requires that a transaction must exist in the context, or an exception is thrown. PROPAGATION_REQUIRES_NEW: This propagation level creates a new transaction each time it executes, suspends the transaction in context, and resumes the transaction in context after the current thread executes. (The execution result of the child transaction does not affect the execution and rollback of the parent transaction.) PROPAGATION_NOT_SUPPORTED: Suspends the current transaction if there is a transaction in the context, and resumes the context transaction after the current logic is executed. (Reduce transaction size to wrap non-core execution logic.) PROPAGATION_NEVER: This propagation level requires that no transactions exist in the context, or an exception is thrown. PROPAGATION_NESTED: Nested transactions are executed if a transaction exists in the context, or new transactions are created if one does not exist. (Save Point concept)Copy the code
  • Mysql transactions partially rollback using SavePoint
  • Spring implements the principle of nested transactions using savepoints
  • CRUD is more interested in the Spring transaction propagation mechanism
  • Spring transaction propagation behavior