传统上,Java EE 开发者在事务管理方面有两种选择:全局或局部事务,这两种事务都有很大的局限性。在接下来的两节中,我们将回顾全局和局部事务管理,然后讨论 Spring 框架的事务管理支持如何解决全局和局部事务模型的限制。
全局事务 / Global Transactions
全局事务让你与多个事务性资源一起工作,通常是关系型数据库和消息队列。应用服务器通过 JTA 管理全局事务,这是一个繁琐的 API(部分原因是其异常模型)。此外,JTA 的 UserTransaction 通常需要从 JNDI 获取,这意味着你也需要使用 JNDI 才能使用 JTA。全局事务的使用限制了任何潜在的应用程序代码的重用,因为 JTA 通常只在应用程序服务器环境中可用。
以前,使用全局事务的首选方式是通过 EJB CMT(容器管理事务)。CMT 是声明式事务管理的一种形式(区别于程序化事务管理)。EJB CMT 消除了对事务相关的 JNDI 查询的需要,尽管 EJB 的使用本身就需要使用 JNDI。它消除了大部分但不是全部编写 Java 代码来控制事务的需要。重要的缺点是,CMT 与 JTA 和应用服务器环境相联系。而且,只有当人们选择在 EJB 中实现业务逻辑(或至少在事务性的 EJB 接口后面)时,它才可用。EJB 的缺点是如此之多,以至于这不是一个有吸引力的提议,特别是在面对声明式事务管理的引人注目的替代方案时。
本地事务 / Local Transactions
本地事务是特定资源的,例如与 JDBC 连接相关的事务。本地事务可能更容易使用,但有一个明显的缺点。它们不能跨多个事务资源工作。例如,通过使用 JDBC 连接管理事务的代码不能在全局 JTA 事务中运行。因为应用服务器不参与事务管理,它不能帮助确保跨多个资源的正确性。(值得注意的是,大多数应用程序都使用单一的事务资源)。另一个缺点是,本地事务对编程模型有侵入性。
Spring Framework 的一致编程模型
Spring 解决了全局和局部事务的弊端。它让应用开发者在任何环境下都能使用一个一致的编程模型。你只需写一次代码,它就可以在不同的环境中受益于不同的事务管理策略。Spring 框架同时提供声明式和编程式事务管理。大多数用户更喜欢声明式事务管理,我们在大多数情况下都推荐这样做。
通过编程式事务管理,开发人员使用 Spring 框架的事务抽象,它可以在任何底层事务基础设施上运行。通过首选的声明式模型,开发人员通常很少或不写与事务管理有关的代码,因此,不依赖于 Spring 框架的事务 API 或任何其他事务 API。
您需要应用服务器来进行事务管理吗?
Spring 框架的事务管理支持改变了传统的规则,即企业 Java 应用何时需要应用服务器。
特别是,你不需要一个纯粹的应用服务器来通过 EJB 进行声明性事务。事实上,即使你的应用服务器有强大的 JTA 功能,你也可能决定 Spring 框架的声明式事务比 EJB CMT 提供更多的功能和更多的编程模型。
通常情况下,只有当你的应用程序需要 跨多个资源处理事务时,你才需要应用服务器的 JTA 能力,这对许多应用程序来说不是一个要求。许多高端应用使用单一的、高度可扩展的数据库(如 Oracle RAC)来代替。独立的事务管理器(如 Atomikos Transactions 和 JOTM)是其他选择。当然,你可能需要其他应用服务器的功能,如 Java 消息服务(JMS)和 Java EE 连接器架构(JCA)。
Spring 框架让你可以选择何时将你的应用扩展到一个满载的应用服务器。过去,使用 EJB CMT 或 JTA 的唯一选择是编写本地事务的代码(比如 JDBC 连接上的事务),如果你需要这些代码在全局的、容器管理的事务中运行,就要面临巨大的返工。有了 Spring 框架,只有你的配置文件中的一些 bean 定义需要改变(而不是你的代码)。
:::tips 这一块我没太看明白,对 EJB 那种事务管理方式不太了解。 :::