This article will conduct in-depth research on Spring's transaction management. Mainly introduces how @Transactional works at the bottom. The following article will introduce:
Use of attributes such as propagation (transaction propagation) and isolation (isolation)
What are the pitfalls of transaction use and how to avoid it
JPA and Transaction Management
It is important that JPA itself does not provide any kind of declarative transaction management. If you use JPA outside of a dependency injection container, the transaction must be implemented programmatically by the developer.
UserTransaction utx = entityManager.getTransaction();try{utx.begin();businessLogic();utx.commit();}catch(Exception ex) {utx.rollback();throwex;}This way of transaction management allows transaction scope to be clearly expressed in the code, but it has the following disadvantages:
Repeated code and errors are prone to
Any errors may have a greater impact
Errors are difficult to debug and reproduce
Reduces readability of the code base
What if the method calls other transaction methods?
Using Spring @Transactional
Using Spring @Transactional, the above code is simplified to:
@Transactional publicvoid businessLogic() { ... use entity manager inside a transaction ... }The code is more concise and readable, and it is also the recommended way to deal in Spring at present.
Many important aspects such as transaction propagation can be handled automatically by using @Transactional. In this case, if businessLogic() calls another transaction method, the method will determine how to join the running transaction based on the options.
One potential drawback of this powerful mechanism is that it hides the underlying operation and is difficult to debug when it doesn't work properly.
@Transactional Meaning
One of the key points about @Transactional is to consider two separate concepts, both of which have their own scope and life cycle:
persistence context (persistence context)
database transaction
@Transactional itself defines the scope of a single transaction. This transaction is within the scope of the persistence context.
The persistence context in JPA is EntityManager, and the internal implementation uses the Hibernate Session (using Hibernate as the persistence provider).
The persistence context is just a synchronous object that records the state of Java objects of a finite collection and ensures that changes in these objects are ultimately persisted to the database.
This is a very different concept from a single transaction. An Entity Manager can be used across multiple transactions, and it is indeed used in this way.
When does EntityManager span multiple transactions?
The most common situation is when an application uses Open Session In View mode to handle lazy initialization exceptions. Previous articles have introduced the advantages and disadvantages of this practice.
In this case, multiple queries run by the view layer are in separate transactions, rather than single transaction business logic, but these queries are managed by the same entity manager.
Another scenario is that the developer marks the persistence context as PersistenceContextType.EXTENDED, which means it is able to respond to multiple requests.
How to define the relationship between EntityManager and Transaction?
This is chosen by application developers, but the most common way for JPA Entity Manager is the "Entity Manager per application transaction" mode. Common methods of entity manager injection are:
@PersistenceContext privateEntityManager em;
The default is "Entity Manager per transaction" mode. In this mode, if the Entity Manager is used inside the @Transactional method, the method will run in a single transaction.
How does @PersistenceContext work?
The question that follows is how @PersistenceContext can inject entity manager only when the container starts, assuming that the entity manager life cycle is short and requires multiple entity managers per request.
The answer is that it cannot: EntityManager is an interface, and what is injected into the spring bean is not the entity manager itself, but the context aware proxy (context aware proxy) of the specific entity manager in the runtime.
The specific class commonly used for proxy is SharedEntityManagerInvocationHandler, which can be confirmed with the help of a debugger.
So how does @Transactional work?
The persistent context proxy that implements the EntityManager interface is not the only part of declarative transaction management, but actually contains three components:
EntityManager Proxy itself
The section of the transaction
Transaction Manager
Take a look at these three parts and their interactions.
The section of the transaction
The transaction section is an "around" section that can be called before and after the annotated business method. The specific class that implements the section is TransactionInterceptor.
There are two main responsibilities in the section of a transaction:
In 'before', the section provides a call point to decide whether the called business method should run within the scope of the ongoing transaction or start a new independent transaction.
In 'after', the section needs to determine that the transaction is committed, rolled back or continued to run.
In 'before', the transaction section itself does not contain any decision logic, and the decision to start a new transaction is delegated to the transaction manager for completion.
Transaction Manager
The transaction manager needs to solve the following two problems:
Should the new Entity Manager be created?
Should a new transaction be started?
These require transaction sections to be determined when the 'before' logic is called. The decisions of the transaction manager are based on the following two points:
Is the transaction going on
The propagation property of the transaction method (for example, REQUIRES_NEW always starts a new transaction)
If the transaction manager determines that you want to create a new transaction, it will:
1. Create a new entity manager
2. The intity manager is bound to the current thread
3. Get connection from database connection pool
4. Bind the connection to the current thread
Use the ThreadLocal variable to bind both the entity manager and the database connection to the current thread.
Transactions run when they are stored in the thread, and when they are no longer in use, the transaction manager decides whether to clear them.
Any part of the program can be retrieved from the thread if the current entity manager and database connection are required.
EntityManager proxy
EntityManager proxy (already introduced earlier) is the last part of the puzzle. When the business method calls entityManager.persist(), this is not called directly by the entity manager.
Instead, the business method calls the agent, which gets the current entity manager from the thread. As mentioned earlier, the transaction manager binds the entity manager to the thread.
After understanding the various parts of the @Transactional mechanism, let's take a look at the commonly used Spring configurations that implement it.
Integrate three parts
How to combine three parts so that transaction annotations can work correctly? First define the entity manager factory.
This allows you to inject Entity Manager proxy with persistence context annotation.
@Configuration publicclass EntityManagerFactoryConfiguration {@Autowired privateDataSource dataSource;@Bean(name = "entityManagerFactory") publicLocalContainerEntityManagerFactoryBean emf() {LocalContainerEntityManagerFactoryBean emf = ... emf.setDataSource(dataSource);emf.setPackagesToScan( newString[] {"your.package"});emf.setJpaVendorAdapter( newHibernateJpaVendorAdapter());returnemf;}}The next step is to configure the transaction manager and apply the transaction facets in the class annotated @Transactional.
@Configuration @EnableTransactionManagement publicclass TransactionManagersConfig {@Autowired EntityManagerFactory emf;@Autowired privateDataSource dataSource;@Bean(name = "transactionManager") publicPlatformTransactionManager transactionManager() {JpaTransactionManager tm = newJpaTransactionManager();tm.setEntityManagerFactory(emf);tm.setDataSource(dataSource);returnm;}}Annotation @EnableTransactionManagement notifies Spring that the class annotated by @Transactional is surrounded by the transaction's cut-away. This way @Transactional can be used.
Summarize
Spring's declarative transaction management mechanism is very powerful, but it can be misused or easily configured errors.
When problems such as this mechanism does not work properly or fail to achieve the expected operating results, it is helpful to understand its internal workings.
The most important thing to remember is to take into account two concepts: transactions and persistence contexts, each with its own obvious life cycle that is unreadable.
The above is all the content of this article about the working principle of Spring @Transactional. I hope it will be helpful to everyone. Interested friends can continue to refer to other related topics on this site. If there are any shortcomings, please leave a message to point it out. Thank you friends for your support for this site!