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); } }