How does Spring @Transactional Really Work?--转
原文地址:http://blog.jhades.org/how-does-spring-transactional-really-work/
In this post we will do a deep dive into Spring transaction management. We will go over on how does @Transactional
really works under the hood. Other upcoming posts will include:
- how to use features like propagation and isolation
- what are the main pitfalls and how to avoid them
JPA and Transaction Management
It's important to notice that JPA on itself does not provide any type of declarative transaction management. When using JPA outside of a dependency injection container, transactions need to be handled programatically by the developer:
1
2
3
4
5
6
7
8
9
10
11
12
|
UserTransaction utx = entityManager.getTransaction(); try { utx.begin(); businessLogic(); utx.commit(); } catch (Exception ex) { utx.rollback(); throw ex; } |
This way of managing transactions makes the scope of the transaction very clear in the code, but it has several disavantages:
- it's repetitive and error prone
- any error can have a very high impact
- errors are hard to debug and reproduce
- this decreases the readability of the code base
- What if this method calls another transactional method?
Using Spring @Transactional
With Spring @Transactional
, the above code gets reduced to simply this:
1
2
3
4
|
@Transactional public void businessLogic() { ... use entity manager inside a transaction ... } |
This is much more convenient and readable, and is currently the recommended way to handle transactions in Spring.
By using @Transactional
, many important aspects such as transaction propagation are handled automatically. In this case if another transactional method is called by businessLogic()
, that method will have the option of joining the ongoing transaction.
One potential downside is that this powerful mechanism hides what is going on under the hood, making it hard to debug when things don't work.
What does @Transactional
mean?
One of the key points about @Transactional
is that there are two separate concepts to consider, each with it's own scope and life cycle:
- the persistence context
- the database transaction
The transactional annotation itself defines the scope of a single database transaction. The database transaction happens inside the scope of apersistence context.
The persistence context is in JPA the EntityManager
, implemented internally using an Hibernate Session
(when using Hibernate as the persistence provider).
The persistence context is just a synchronizer object that tracks the state of a limited set of Java objects and makes sure that changes on those objects are eventually persisted back into the database.
This is a very different notion than the one of a database transaction. One Entity Manager can be used across several database transactions, and it actually often is.
When does an EntityManager span multiple database transactions?
The most frequent case is when the application is using the Open Session In View pattern to deal with lazy initialization exceptions, see this previous blog post for it's pros and cons.
In such case the queries that run in the view layer are in separate database transactions than the one used for the business logic, but they are made via the same entity manager.
Another case is when the persistence context is marked by the developer as PersistenceContextType.EXTENDED
, which means that it can survive multiple requests.
What defines the EntityManager vs Transaction relation?
This is actually a choice of the application developer, but the most frequent way to use the JPA Entity Manager is with the
"Entity Manager per application transaction" pattern. This is the most common way to inject an entity manager:
1
2
|
@PersistenceContext private EntityManager em; |
Here we are by default in "Entity Manager per transaction" mode. In this mode, if we use this Entity Manager inside a @Transactional
method, then the method will run in a single database transaction.
How does @PersistenceContext work?
One question that comes to mind is, how can @PersistenceContext
inject an entity manager only once at container startup time, given that entity managers are so short lived, and that there are usually multiple per request.
The answer is that it can't: EntityManager
is an interface, and what gets injected in the spring bean is not the entity manager itself but a context aware proxy that will delegate to a concrete entity manager at runtime.
Usually the concrete class used for the proxy is SharedEntityManagerInvocationHandler
, this can be confirmed with the help a debugger.
How does @Transactional work then?
The persistence context proxy that implements EntityManager
is not the only component needed for making declarative transaction management work. Actually three separate components are needed:
- The EntityManager Proxy itself
- The Transactional Aspect
- The Transaction Manager
Let's go over each one and see how they interact.
The Transactional Aspect
The Transactional Aspect is an 'around' aspect that gets called both before and after the annotated business method. The concrete class for implementing the aspect is TransactionInterceptor
.
The Transactional Aspect has two main responsibilities:
At the 'before' moment, the aspect provides a hook point for determining if the business method about to be called should run in the scope of an ongoing database transaction, or if a new separate transaction should be started.
At the 'after' moment, the aspect needs to decide if the transaction should be committed, rolled back or left running.
At the 'before' moment the Transactional Aspect itself does not contain any decision logic, the decision to start a new transaction if needed is delegated to the Transaction Manager.
The Transaction Manager
The transaction manager needs to provide an answer to two questions:
- should a new Entity Manager be created?
- should a new database transaction be started?
This needs to be decided at the moment the Transactional Aspect 'before' logic is called. The transaction manager will decide based on:
- the fact that one transaction is already ongoing or not
- the propagation attribute of the transactional method (for example
REQUIRES_NEW
always starts a new transaction)
If the transaction manager decides to create a new transaction, then it will:
- create a new entity manager
- bind the entity manager to the current thread
- grab a connection from the DB connection pool
- bind the connection to the current thread
The entity manager and the connection are both bound to the current thread using ThreadLocal variables.
They are stored in the thread while the transaction is running, and it's up to the Transaction Manager to clean them up when no longer needed.
Any parts of the program that need the current entity manager or connection can retrieve them from the thread. One program component that does exactly that is the EntityManager proxy.
The EntityManager proxy
The EntityManager proxy (that we have introduced before) is the last piece of the puzzle. When the business method calls for example entityManager.persist()
, this call is not invoking the entity manager directly.
Instead the business method calls the proxy, which retrieves the current entity manager from the thread, where the Transaction Manager put it.
Knowing now what are the moving parts of the @Transactional
mechanism, let's go over the usual Spring configuration needed to make this work.
Putting It All Together
Let's go over how to setup the three components needed to make the transactional annotation work correctly. We start by defining the entity manager factory.
This will allow the injection of Entity Manager proxies via the persistence context annotation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Configuration public class EntityManagerFactoriesConfiguration { @Autowired private DataSource dataSource; @Bean (name = "entityManagerFactory" ) public LocalContainerEntityManagerFactoryBean emf() { LocalContainerEntityManagerFactoryBean emf = ... emf.setDataSource(dataSource); emf.setPackagesToScan( new String[] { "your.package" }); emf.setJpaVendorAdapter( new HibernateJpaVendorAdapter()); return emf; } } |
The next step is to configure the Transaction Manager and to apply the Transactional Aspect in @Transactional
annotated classes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@Configuration @EnableTransactionManagement public class TransactionManagersConfig { @Autowired EntityManagerFactory emf; @Autowired private DataSource dataSource; @Bean (name = "transactionManager" ) public PlatformTransactionManager transactionManager() { JpaTransactionManager tm = new JpaTransactionManager(); tm.setEntityManagerFactory(emf); tm.setDataSource(dataSource); return tm; } } |
The annotation @EnableTransactionManagement
tells Spring that classes with the @Transactional
annotation should be wrapped with the Transactional Aspect. With this the @Transactional
is now ready to be used.
Conclusion
The Spring declarative transaction management mechanism is very powerful, but it can be misused or wrongly configured easily.
Understanding how it works internally is helpful when troubleshooting situations when the mechanism is not at all working or is working in an unexpected way.
The most important thing to bear in mind is that there are really two concepts to take into account: the database transaction and the persistence context, each with it's own not readily apparent life cycle.
A future post will go over the most frequent pitfalls of the transactional annotation and how to avoid them.
How does Spring @Transactional Really Work?--转的更多相关文章
- 数据库事务中的隔离级别和锁+spring Transactional注解
数据库事务中的隔离级别和锁 数据库事务在后端开发中占非常重要的地位,如何确保数据读取的正确性.安全性也是我们需要研究的问题.ACID首先总结一下数据库事务正确执行的四个要素(ACID): 原子性(At ...
- Spring @Transactional使用的示例
Spring @Transactional使用的示例: 参考: http://blog.csdn.net/seng3018/article/details/6690527 http://blog.si ...
- Spring @Transactional 使用
Spring @Transactional是Spring提供的一个声明式事务,对代码的侵入性比较小,只需考虑业务逻辑,不需要把事务和业务搞混在一起. @Transactional 可以注解在inter ...
- Java:Spring @Transactional工作原理
本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的.之后的文章将介绍: propagation(事务传播)和isolation(隔离性)等属性的使用 事务使用 ...
- spring @Transactional 事务注解
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE, rollbackFor = ...
- Spring @Transactional (一)
Spring @Transactional (一) 博客分类: JAVA SpringJPAJDBCUPSQL Spring事务的传播行为 在service类前加上@Transactional,声明 ...
- [转]数据库事务中的隔离级别和锁+spring Transactional注解
数据库事务中的隔离级别和锁 数据库事务在后端开发中占非常重要的地位,如何确保数据读取的正确性.安全性也是我们需要研究的问题.ACID首先总结一下数据库事务正确执行的四个要素(ACID): 原子性(At ...
- 25.Spring @Transactional工作原理
转自:http://www.importnew.com/12300.html 本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的.之后的文章将介绍: prop ...
- Spring @Transactional ——事务回滚
工作原理运行配置@Transactional注解的测试类的时候,具体会发生如下步骤1)事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入DataSource实例的某个与Dat ...
随机推荐
- 萝卜白菜,给有所爱——C#和JAVA都会终将被时代淘汰
看到园子里又有一波试图掀起C#和JAVA的谁更好的争论,对于这些一直不断的争论,我觉得实在没有必要,黑格尔的存在即合理,中国的老古语说的萝卜白菜各有所爱,大家争论的再多其实卵用也没用,还不如趁着闲暇时 ...
- 深入剖析Nginx一点小笔记
前几天在图书馆看书,恰好看到这本<深入剖析nginx>,花了快一周的时间看完了这本书,写点笔记心得便于以后复习. 以前对nginx的认识就只是停留在一个反向代理服务器上.百度了一下ngin ...
- linux安装oracle 11g rac
安装oracle 11gR2 RAC 一.网络规划及安装虚拟主机 主机名 主机版本 Ip rac1.localdomain Redhat 6.5 RAC节点1 192.168.100.11 rac2. ...
- QC11客户端安装
win10使用hp qc11 步骤1:安装vcredist_x86,32位 步骤2:安装浏览器客户端 ALMExplorerAddIn,11版本 可能遇到的问题 1. 出现Initialization ...
- 使用XmlDataDocument将数据存储到XML文档
string str = "Data Source=192.168.1.20;Initial Catalog=WebTest;User ID=sa;Password="; SqlC ...
- Java语法糖2:自动装箱和自动拆箱
前言 一开始想学学自动拆箱和自动装箱是被这个名字吸引到,听上去好像很高端的样子,其实自动拆箱.自动装箱是很简单的内容. 自动拆箱和自动装箱 Java为每种基本数据类型都提供了对应的包装器类型.举个例子 ...
- 人人都是 DBA(X)资源信息收集脚本汇编
什么?有个 SQL 执行了 8 秒! 哪里出了问题?臣妾不知道啊,得找 DBA 啊. DBA 人呢?离职了!!擦!!! 程序员在无处寻求帮助时,就得想办法自救,努力让自己变成 "伪 DBA& ...
- 人人都是 DBA(IX)服务器信息收集脚本汇编
什么?有个 SQL 执行了 8 秒! 哪里出了问题?臣妾不知道啊,得找 DBA 啊. DBA 人呢?离职了!!擦!!! 程序员在无处寻求帮助时,就得想办法自救,努力让自己变成 "伪 DBA& ...
- 使用ViwePager显示图片时如何防止内存泄露。
内存泄露的检测. 1. 在Android Studio中运行你的应用,然后切换到输出窗口的Android tab. 2. 尽情的玩耍你的应用,最好各个功能都用到,如果是Viewpager,则多滑动一些 ...
- JavaScript 数组详解
在程序语言中数组的重要性不言而喻,JavaScript中数组也是最常使用的对象之一,数组是值的有序集合,由于弱类型的原因,JavaScript中数组十分灵活.强大,不像是Java等强类型高级语言数组只 ...