欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring @Transactional工作原理详解

程序员文章站 2023-12-18 10:38:46
本文将深入研究spring的事务管理。主要介绍@transactional在底层是如何工作的。之后的文章将介绍: propagation(事务传播)和isolation(...

本文将深入研究spring的事务管理。主要介绍@transactional在底层是如何工作的。之后的文章将介绍:

propagation(事务传播)和isolation(隔离性)等属性的使用

事务使用的陷阱有哪些以及如何避免

jpa和事务管理

很重要的一点是jpa本身并不提供任何类型的声明式事务管理。如果在依赖注入容器之外使用jpa,事务处理必须由开发人员编程实现。

usertransaction utx = entitymanager.gettransaction();
try{
	utx.begin();
	businesslogic();
	utx.commit();
}
catch(exception ex) {
	utx.rollback();
	throwex;
}

这种方式的事务管理使事务范围可以在代码中很清晰地表达出来,但它有以下缺点:

容易出现重复代码和错误

任何错误可能产生较大的影响

错误难以调试和复现

降低了代码库的可读性

如果该方法调用了其他的事务方法如何处理呢?

使用spring @transactional

使用spring @transactional,上面的代码就简化为:

@transactional 
  publicvoid businesslogic() { 
    ... use entity manager inside a transaction ... 
  } 

代码更加简洁,可读性更好,也是目前spring中事务处理的推荐方式。

通过使用@transactional,事务传播等很多重要方面可以自动处理。这种情况下如果businesslogic()调用了其他事务方法,该方法将根据选项确定如何加入正在运行事务。

这个强大机制的一个潜在缺点是它隐藏了底层的运行,当它不能正常工作时很难调试。

@transactional含义

关于@transactional,关键点之一是要考虑两个独立的概念,它们都有各自的范围和生命周期:

persistence context(持久化上下文)

database transaction(事务)

@transactional本身定义了单个事务的范围。这个事务在persistence context的范围内。

jpa中的持久化上下文是entitymanager,内部实现使用了hibernate session(使用hibernate作为持久化provider)。

持久化上下文仅仅是一个同步对象,它记录了有限集合的java对象的状态,并且保证这些对象的变化最终持久化到数据库。

这是与单个事务非常不同的概念。一个entity manager可以跨越多个事务使用,而且的确是这样使用的。

entitymanager何时跨越多个事务?

最常见的情况是应用使用open session in view模式处理懒初始化异常时,之前的文章介绍过这种做法的优势和劣势。

这种情况下视图层运行的多个查询处于独立的事务中,而不是单事务的业务逻辑,但这些查询由相同的entity manager管理。

另一种情况是开发人员将持久化上下文标记为persistencecontexttype.extended,这表示它能够响应多个请求。

如何定义entitymanager和transaction之间的关系?

这由应用开发者来选择,但是jpa entity manager最常用的方式是“entity manager per application transaction”(每个事务都有自己的实体管理器)模式。entity manager注入的常用方法是:

@persistencecontext 
  privateentitymanager em; 

这里默认为“entity manager per transaction”模式。这种模式下如果在@transactional方法内部使用该entity manager,那么该方法将在单一事务中运行。

@persistencecontext如何工作?

随之而来的问题就是@persistencecontext如何仅在容器启动时注入entity manager,假定entity manager生命周期很短暂,而且每次请求需要多个entity manager。

答案是它不能:entitymanager是一个接口,注入到spring bean中的不是entity manager本身,而是在运行时代理具体entity manager的context aware proxy(上下文感知代理)。

通常用于代理的具体类为sharedentitymanagerinvocationhandler,借助调试器可以确认这一点。

那么@transactional如何工作?

实现了entitymanager接口的持久化上下文代理并不是声明式事务管理的唯一部分,事实上包含三个组成部分:

entitymanager proxy本身

事务的切面

事务管理器

看一下这三部分以及它们之间的相互作用。

事务的切面

事务的切面是一个“around(环绕)”切面,在注解的业务方法前后都可以被调用。实现切面的具体类是transactioninterceptor。

事务的切面有两个主要职责:

在'before'时,切面提供一个调用点,来决定被调用业务方法应该在正在进行事务的范围内运行,还是开始一个新的独立事务。

在'after'时,切面需要确定事务被提交,回滚或者继续运行。

在'before'时,事务切面自身不包含任何决策逻辑,是否开始新事务的决策委派给事务管理器完成。

事务管理器

事务管理器需要解决下面两个问题:

新的entity manager是否应该被创建?

是否应该开始新的事务?

这些需要事务切面'before'逻辑被调用时决定。事务管理器的决策基于以下两点:

事务是否正在进行

事务方法的propagation属性(比如requires_new总要开始新事务)

如果事务管理器确定要创建新事务,那么将:

1.创建一个新的entity manager

2.entity manager绑定到当前线程

3.从数据库连接池中获取连接

4.将连接绑定到当前线程

使用threadlocal变量将entity manager和数据库连接都绑定到当前线程。

事务运行时他们存储在线程中,当它们不再被使用时,事务管理器决定是否将他们清除。

程序的任何部分如果需要当前的entity manager和数据库连接都可以从线程中获取。

entitymanager proxy

entitymanager proxy(前面已经介绍过)就是谜题的最后一部分。当业务方法调用entitymanager.persist()时,这不是由entity manager直接调用的。

而是业务方法调用代理,代理从线程获取当前的entity manager,前面介绍过事务管理器将entity manager绑定到线程。

了解了@transactional机制的各个部分,我们来看一下实现它的常用spring配置。

整合三个部分

如何将三个部分组合起来使事务注解可以正确地发挥作用呢?首先定义entity manager工厂。

这样就可以通过持久化上下文注解注入entity manager proxy。

@configuration 
  publicclass entitymanagerfactoriesconfiguration {
	@autowired 
	    privatedatasource datasource;
	@bean(name = "entitymanagerfactory") 
	    publiclocalcontainerentitymanagerfactorybean emf() {
		localcontainerentitymanagerfactorybean emf = ... 
		      emf.setdatasource(datasource);
		emf.setpackagestoscan( 
		        newstring[] {
			"your.package"
		}
		);
		emf.setjpavendoradapter( 
		        newhibernatejpavendoradapter());
		returnemf;
	}
}

下一步实现配置事务管理器和在@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);
		returntm;
	}
}

注解@enabletransactionmanagement通知spring,@transactional注解的类被事务的切面包围。这样@transactional就可以使用了。

总结

spring声明式事务管理机制非常强大,但它可能被误用或者容易发生配置错误。

当这个机制不能正常工作或者未达到预期运行结果等问题出现时,理解它的内部工作情况是很有帮助的。

需要记住的最重要的一点是,要考虑到两个概念:事务和持久化上下文,每个都有自己不可读的明显的生命周期。

以上就是本文关于spring @transactional工作原理详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

上一篇:

下一篇: