transactional_Spring @Transactional实际如何工作?

transactional

transactional

在本文中,我们将深入探讨Spring事务管理。 我们将探讨@Transactional@Transactional如何真正工作。 其他即将发布的帖子包括:

  • 如何使用传播和隔离等功能
  • 主要陷阱是什么以及如何避免它们

JPA和事务管理

重要的是要注意,JPA本身不提供任何类型的声明式事务管理。 在依赖项注入容器之外使用JPA时,开发人员需要以编程方式处理事务:

UserTransaction utx = entityManager.getTransaction(); 

    try { 
        utx.begin(); 

        businessLogic();

        utx.commit(); 
    } catch(Exception ex) { 
        utx.rollback(); 
        throw ex; 
    }

这种管理事务的方式使代码中的事务范围非常清楚,但是有一些缺点:

  • 它是重复性的并且容易出错
  • 任何错误都会产生很大的影响
  • 错误很难调试和重现
  • 这降低了代码库的可读性
  • 如果此方法调用另一个事务方法怎么办?

使用Spring @Transactional

使用Spring @Transactional ,上述代码简化为:

@Transactional
    public void businessLogic() {
        ... use entity manager inside a transaction ...
    }

这更加方便和易读,是当前在Spring中处理事务的推荐方法。

通过使用@Transactional ,可以自动处理许多重要方面,例如事务传播。 在这种情况下,如果businessLogic()调用了另一个事务方法,则该方法可以选择加入正在进行的事务。

潜在的不利之处在于,这种强大的机制隐藏了引擎盖下发生的事情,从而在无法正常工作时很难进行调试。

@Transactional的关键点之一是要考虑两个单独的概念,每个概念都有自己的范围和生命周期:

  • 持久性环境
  • 数据库事务

事务注释本身定义了单个数据库事务的范围。 数据库事务在持久性上下文的范围内发生。

持久性上下文是在JPA中的EntityManager ,它是使用Hibernate Session在内部实现的(当使用Hibernate作为持久性提供程序时)。

持久性上下文只是一个同步器对象,该对象跟踪一组有限的Java对象的状态,并确保最终将这些对象上的更改持久化回到数据库中。

这与数据库事务的概念非常不同。 一个实体管理器可用于多个数据库事务,而实际上经常是这样。

EntityManager何时跨多个数据库事务?

最常见的情况是,当应用程序使用“在视图中打开会话”模式来处理延迟的初始化异常时,请参见上一篇博客文章,以了解其优缺点

在这种情况下,在视图层中运行的查询与用于业务逻辑的查询不在单独的数据库事务中,但是它们是通过同一实体管理器进行的。

另一种情况是,开发人员将持久性上下文标记为PersistenceContextType.EXTENDED ,这意味着它可以承受多个请求。

什么定义了EntityManager vs Transaction关系?

这实际上是应用程序开发人员的选择,但是使用JPA实体管理器的最常见方法是使用“每个应用程序事务的实体管理器”模式。 这是注入实体管理器的最常见方法:

@PersistenceContext
    private EntityManager em;

在这里,默认情况下,我们处于“每个事务实体管理器”模式。 在这种模式下,如果我们在@Transactional方法内使用此实体管理器,则该方法将在单个数据库事务中运行。

@PersistenceContext如何工作?

想到的一个问题是,鉴于实体管理器的生命周期如此短暂,并且每个请求通常有多个实体,因此@PersistenceContext如何在容器启动时仅注入一次实体管理器。

答案是:它不能: EntityManager是一个接口,注入到Spring bean中的不是实体管理器本身,而是上下文感知的代理,它将在运行时委派给具体的实体管理器。

通常,用于代理的具体类是SharedEntityManagerInvocationHandler ,可以在调试器的帮助下进行确认。

@Transactional如何工作?

实现EntityManager的持久性上下文代理不是使声明式事务管理工作所需的唯一组件。 实际上需要三个独立的组件:

  • EntityManager代理本身
  • 交易方面
  • 交易经理

让我们逐一检查一下,看看它们如何相互作用。

交易方面

事务方面是在注释的业务方法之前和之后都被调用的“周围”方面。 实现方面的具体类是TransactionInterceptor

事务方面有两个主要职责:

  • 在“之前”时刻,该方面提供了一个挂钩点,用于确定要调用的业务方法是否应在正在进行的数据库事务范围内运行,或者是否应该启动新的单独事务。
  • 在“之后”时刻,方面需要确定是应该提交事务,回滚事务还是保持运行。

在“之前”时刻,事务方面本身不包含任何决策逻辑,如果需要,则启动新事务的决策将委托给事务管理器。

交易经理

交易经理需要提供以下两个问题的答案:

  • 是否应该创建一个新的实体管理器?
  • 是否应该启动新的数据库事务?

这需要在调用事务方面“之前”逻辑时确定。 交易经理将根据以下内容做出决定:

  • 一项交易是否已经进行的事实
  • 事务方法的传播属性(例如, REQUIRES_NEW始终启动新事务)

如果交易经理决定创建新交易,则它将:

  • 创建一个新的实体经理
  • 将实体管理器绑定到当前线程
  • 从数据库连接池中获取连接
  • 将连接绑定到当前线程

实体管理器和连接都使用ThreadLocal变量绑定到当前线程。

它们在事务运行时存储在线程中,并且当不再需要它们时,由事务管理器来清理它们。

程序中需要当前实体管理器或连接的任何部分都可以从线程中检索它们。 正是这样做的一个程序组件是EntityManager代理。

EntityManager代理

最后一步是EntityManager代理(我们之前已经介绍过)。 以业务方法为例entityManager.persist() ,此调用未直接调用实体管理器。

相反,业务方法调用代理,该代理从事务管理器放置在其中的线程中检索当前的实体管理器。

现在知道了@Transactional机制的哪些移动部分,让我们@Transactional这项工作所需的常规Spring配置。

放在一起

让我们研究一下如何设置使事务注释正确工作所需的三个组件。 我们首先定义实体管理器工厂。

这将允许通过持久性上下文注释注入Entity Manager代理:

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

下一步是配置事务管理器,并将事务方面应用到@Transactional注释类中:

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

注释@EnableTransactionManagement告诉Spring带有@Transactional注释的类应使用Transactional Aspect包装。 这样,@ @Transactional现在可以使用了。

结论

Spring声明式事务管理机制非常强大,但是很容易被滥用或错误配置。

当对机制根本不起作用或无法正常工作的情况进行故障排除时,了解其内部工作方式将很有帮助。

需要牢记的最重要的一点是,实际上有两个概念需要考虑:数据库事务和持久性上下文,每个都有其自身不容易发现的生命周期。

将来的帖子将介绍事务注释的最常见陷阱以及如何避免它们。

翻译自: https://www.javacodegeeks.com/2014/06/how-does-spring-transactional-really-work.html

transactional

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值