[Spring transaction details] - 2 Considerations for transaction application

preface

Go on [detailed explanation of Spring transactions] - 1 Case demonstration of transaction propagation In this article, let's take a look at the specific use of transactions.

It is very simple to start transaction management through Spring. Two methods are supported by default, one is programmatic transaction and the other is declarative transaction. Most people are familiar with declarative transaction. Let's start with it.

Declarative transaction

It is very simple to open a declarative transaction. You can directly use the @ Transactional annotation

@Transactional
public void func() {
    
}

Abuse @ Transactional

However, because of its simplicity and flexibility, it often leads to abuse.

Never directly add annotations to the Service, which will cause the methods in the whole Service to be subject to transaction management as long as they are called to the database, thus affecting the QPS of the database and Web services.

@Service
@Transactional // Don't add it to the Service
public class DemoService {

    
}

Long affairs

In short, in the whole method life cycle, the method that really needs transaction management may only take 200 milliseconds, while other business processes take 2 seconds. However, because the transaction takes effect on the whole method, a database connection takes more than 2 seconds.

@Transactional
public void func() {
	// Two select took 2 seconds
	select1();
	select2();
	// Two save s took only 200 milliseconds
	save1();
	save2();
}

The solution is also very simple. You can split long transactions into short transactions

public void func() {
	select1();
	select2();
	manager.save();
}

@Transactional
public void save() {
	save1();
	save2();
}

Programming transaction

The biggest problem of declarative transactions is granularity control. The smallest granularity of declarative transactions is also method level, which can easily lead to long transaction problems. Therefore, we generally use programmatic transactions instead.

The TransactionTemplate and PlatformTransactionManager provided by Spring support the implementation of programmatic transactions. TransactionTemplate encapsulates the original transaction management class again and calls its core method execute to realize the management of the whole transaction.

@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
	Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
	if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
		return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
	}
	else {
		TransactionStatus status = this.transactionManager.getTransaction(this);
		T result;
		try {
			result = action.doInTransaction(status);
		}
		catch (RuntimeException | Error ex) {
			// Transactional code threw application exception -> rollback
			rollbackOnException(status, ex);
			throw ex;
		}
		catch (Throwable ex) {
			// Transactional code threw unexpected exception -> rollback
			rollbackOnException(status, ex);
			throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
		}
		this.transactionManager.commit(status);
		return result;
	}
}

Platform transaction manager is a little more flexible. It defines three key methods, which can be understood at a glance

public interface PlatformTransactionManager extends TransactionManager {

	
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;

	
	void commit(TransactionStatus status) throws TransactionException;

	
	void rollback(TransactionStatus status) throws TransactionException;

}

TransactionTemplate demo

The input parameter in execute is TransactionCallback, which is a functional interface. Only one method doInTransaction is defined

You can pass in TransactionCallbackWithoutResult without return parameters

@Resource
private TransactionTemplate transactionTemplate;

public void func() {
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            testMapper.updateT1();
            t2Service.func();
            int i = 1 / 0;
        }
    });
}

Or you can directly pass in the TransactionCallback with return parameters.

Platform transaction manager demo

@Resource
private PlatformTransactionManager platformTransactionManager;

public void func() {
    TransactionStatus status = platformTransactionManager.getTransaction(new DefaultTransactionDefinition());
    try {
        testMapper.updateT1();
        t2Service.func();
        int i = 1 / 0;
        platformTransactionManager.commit(status);
    } catch (Exception e) {
        e.printStackTrace();
        platformTransactionManager.rollback(status);
    }
}

Keywords: Java Spring Back-end

Added by DyslexicDog on Tue, 04 Jan 2022 15:42:46 +0200