Apache Karaf 事务指南
前言 复制链接链接已复制到粘贴板!
本指南提供有关实施 Fuse 事务应用程序的信息和说明。该信息组织如下:
第 1 章 事务简介 复制链接链接已复制到粘贴板!
本章介绍了一些基本事务概念以及在事务管理器中重要的服务严重性。该信息组织如下:
1.1. 什么是事务? 复制链接链接已复制到粘贴板!
事务建模是一个操作,其概念上包含一个步骤(例如,将资金从帐户 A 转移到帐户 B),但必须作为一系列步骤实施。此类操作容易受到系统故障的影响,因为失败可能会使一些步骤无法完成,这会使系统处于不一致的状态。例如,考虑将资金从 A 转移到帐户 B 的操作。假设系统在去除帐户 A 后失败,但在信用帐户 B 之前失败。因此,结果会消失。
为确保此操作可靠,请将其实施为 事务。事务保证了可靠的执行,因为它是原子、一致、隔离和持久化。这些属性称为事务的 ACID 属性。
1.2. 事务的 ACID 属性 复制链接链接已复制到粘贴板!
事务的 ACID 属性定义如下:
- Atomic-a 事务是所有或无任何内容的步骤。当事务完成时,单独更新会被编译,并同时提交或中止(回滚)。
- 致- 一个事务是将系统从一个一致状态移到另一个一致状态的工作单元。
- isolated- 在事务执行时,其部分结果会与其他实体隐藏。
- durable- 事务的结果在提交事务后马上失败。
1.3. 关于事务客户端 复制链接链接已复制到粘贴板!
事务客户端 是一个 API 或对象,可让您启动和结束事务。通常,事务客户端会公开 开始、提交 或回滚 事务的操作。
在标准 hundreds 应用中,javax.transaction.UserTransaction 接口会公开事务客户端 API。在 Spring Framework 的上下文中,Spring Boot,org.springframework.transaction.PlatformTransactionManager 接口会公开一个事务客户端 API。
1.4. 事务术语的描述 复制链接链接已复制到粘贴板!
下表定义了一些重要的事务术语:
| 术语 | 描述 |
|---|---|
| demarcation | 事务处理指的是启动和结束事务。结束事务意味着事务中完成的工作是提交或回滚。解译可以是显式的,例如,调用事务客户端 API 或隐式(例如,每当消息从事务端点轮询时)。详情请查看 第 9 章 编写使用事务的 Camel 应用程序。 |
| Resources | 资源是 可以处理持久或永久更改的计算机系统的任何组件。在实践中,资源几乎始终是在数据库中分层的数据库或服务,例如具有持久性的消息服务。然而,其他类型的资源是可有效的。例如,自动化 Teller 机器(ATM)是一种资源。客户从计算机物理接受 cash 后,无法撤销事务。 |
| 事务管理器 | 事务管理器 负责跨一个或多个资源协调事务。在很多情况下,事务管理器内置在一个资源中。例如,企业级数据库通常包含一个事务管理器,能够管理更改该数据库中内容的事务。涉及 多个资源 的事务通常需要 一个外部 事务管理器。 |
| 事务上下文 | 事务上下文是 一个对象,它封装了跟踪事务所需的信息。事务上下文的格式完全取决于相关的事务管理器实现。事务上下文至少包含唯一的事务标识符。 |
| 分布式事务 | 分布式事务指的是分布式系统中的事务,其中事务范围跨越多个网络节点。支持分布式事务的基本先决条件是支持以规范格式传输事务上下文的网络协议。分布式事务不在 Apache Camel 事务范围内。另请参阅: 第 3.2.3 节 “关于分布式事务管理器”。 |
| X/Open XA 标准 | X/Open XA 标准描述了将资源与事务管理器集成的接口。要管理包含多个资源的事务,参与资源必须支持 XA 标准。支持 XA 标准的资源会公开一个特殊对象( XA 交换机 ),它允许事务管理器(或事务处理监视器)控制资源事务。XA 标准支持 1 阶段提交协议和 2 阶段提交协议。 |
1.5. 管理修改多个资源的事务 复制链接链接已复制到粘贴板!
对于涉及 单个资源 的事务,通常可以使用内置在资源中的事务管理器。对于 涉及多个 资源的事务,需要使用外部事务管理器或事务处理(TP)监控。在这种情况下,资源必须通过注册 XA 交换机来与事务管理器集成。
协议之间有一个重要的区别,用于提交在单个资源系统上运行的事务,以及用于提交在多个资源系统上操作的事务的协议:
- 1-phase commit-is 用于单资源系统。此协议在单个步骤中提交事务。
- 2-phase commit-is 用于多个资源系统。此协议在两个步骤中提交一个事务。
在一个事务中包含多个资源会带来系统故障的风险,在某些资源上提交事务,而不是所有资源。这会使系统处于不一致的状态。2 阶段提交协议旨在消除这一风险。它确保系统重启后 始终 可以恢复到一致的状态。
1.6. 事务和线程之间的关系 复制链接链接已复制到粘贴板!
为了了解事务处理,非常感谢事务和线程之间的基本关系非常重要: 事务是特定于线程的。也就是说,当事务启动时,它会附加到特定的线程。(从技术上,创建 事务上下文 对象并与当前线程相关联)。此时,线程中的所有活动都会在这个事务范围内发生。其他线程中的活动 不会 属于这个事务的范围。但是,任何其他线程中的活动都可以在某些其他事务范围内。
这个事务和线程之间的关系意味着:
- 只要在单独的线程中创建每个 事务,应用程序就可以同时处理多个 事务。
-
注意在事务中创建子线程。如果您在事务中,并且您创建了一个新的线程池,例如通过调用
threads ()Camel DSL 命令,新的线程 不在 原始事务范围内。 - 注意处理步骤,根据前面点上给出的相同原因,隐式创建新的线程。
-
事务范围通常不会跨越路由段扩展。也就是说,如果一个路由片段
以(JoinEndpoint)结尾,另一个路由片段从(JoinEndpoint)开始,这些路由片段 通常不 属于相同的事务。然而,有一些例外。
有些高级事务管理器实现可让您自由地将事务上下文与线程分离和附加。例如,这样可以将事务上下文从一个线程移到另一个线程。在某些情况下,也可以将单个事务上下文附加到多个线程。
1.7. 关于事务服务质量 复制链接链接已复制到粘贴板!
在选择实施您的交易系统的产品时,提供各种数据库产品和交易管理器,有些免费收费和一些商业系统。所有这些都不支持事务处理,但这些产品支持的服务质量有显著的差异。本节提供了比较不同交易产品的可靠性和复杂程度时需要考虑的功能的简单指南。
1.7.1. 由资源提供的服务质量 复制链接链接已复制到粘贴板!
以下特性决定了资源的服务质量:
1.7.1.1. 事务隔离级别 复制链接链接已复制到粘贴板!
ANSI SQL 定义四个 事务隔离级别,如下所示:
SERIALIZABLE- 事务完全相互隔离。也就是说,在提交事务前,一个事务都不会影响任何其他事务。这个隔离级别被描述为可 序列 的,因为其效果如同在所有事务都执行后执行,但实际上,资源通常会优化算法,以便允许一些事务同时进行。
REPEATABLE_READ-
每次事务读取或更新数据库时,都会获得读取或写入锁定,直到事务结束为止。这提供了几乎完美的隔离。但是,有一种情况是隔离并不完美。考虑使用
WHERE子句读取行范围的 SQLSELECT语句。如果在第一个事务运行时,另一个事务向这个范围添加一个行,则第一个事务可以查看这个新行(如果它重复SELECT调用( 读取)。 READ_COMMITTED- 在事务结束前,写入锁定会被保存。在事务结束前,读取锁定 不会被 保存。因此,重复读取可能会提供不同的结果,因为其他事务所提交的更新对持续事务可见。
READ_UNCOMMITTED- 在事务结束前,读取锁定或写锁都不会被保存。因此,脏读取是可能的。脏就绪是,当其他事务中未提交的更改对持续的事务可见。
数据库通常不支持所有不同的事务隔离级别。例如,一些可用的数据库只支持 READ_UNCOMMITTED。另外,一些数据库以与 ANSI 标准不同的方式实施事务隔离级别。隔离是一个复杂的问题,它涉及使用数据库性能权衡(例如,请参见 Wikipedia 中的隔离)。
1.7.1.2. 支持 XA 标准 复制链接链接已复制到粘贴板!
要使资源参与涉及多个资源的事务,它需要支持 X/Open XA 标准。务必检查资源的 XA 标准的实现是否受到任何特殊限制的影响。例如,XA 标准的一些实现仅限于单个数据库连接,这意味着一次只有一个线程可以处理涉及该资源的事务。
1.7.2. 事务管理器提供的服务质量 复制链接链接已复制到粘贴板!
以下功能决定了事务管理器的服务质量:
1.7.2.1. 支持 suspend/resume 和 attach/detach 复制链接链接已复制到粘贴板!
有些事务管理器支持操作事务上下文和应用程序线程之间关联的高级功能,如下所示:
- suspend /resume current transaction- 可让您临时暂停当前事务上下文,而应用在当前线程中执行一些非事务工作。
- attach/detach transaction context- 可让您将事务上下文从一个线程移到另一个线程,或者扩展事务范围使其包含多个线程。
1.7.2.2. 支持多个资源 复制链接链接已复制到粘贴板!
事务管理器的主要区别是能够支持多个资源。这通常支持 XA 标准,其中事务管理器为资源提供了注册其 XA 交换机的方法。
严格说,XA 标准不是您可以用来支持多个资源的唯一方法,但它是最实际的一个。替代方案通常涉及编写繁琐(和关键)自定义代码,以实施通常由 XA 交换机提供的算法。
1.7.2.3. 分布式事务 复制链接链接已复制到粘贴板!
有些事务管理器能够管理范围在分布式系统中包含多个节点的事务。通过使用特殊的协议(如 WS-AtomicTransactions 或 CORBA OTS)将事务上下文从节点传播到节点。
1.7.2.4. 事务监控 复制链接链接已复制到粘贴板!
高级事务管理器通常提供可视化工具来监控待处理事务的状态。此类工具在系统失败后特别有用,它可以帮助识别和解决处于不确定状态(硬例外)的事务。
1.7.2.5. 从失败中恢复 复制链接链接已复制到粘贴板!
在出现系统故障(crash)时,事务管理器与其稳健性相关的显著区别。事务管理器使用的关键策略是将数据写入持久日志,然后执行事务的每个步骤。如果出现故障,日志中的数据可用于恢复事务。一些交易经理会比其他人更仔细地实施此策略。例如,高端事务管理器通常会复制持久性事务日志,并允许每个日志存储在单独的主机机器上。
第 2 章 开始使用 Karaf 上的事务(OSGi) 复制链接链接已复制到粘贴板!
本节介绍使用事务访问 Artemis JMS 代理的 Camel 应用程序。该信息组织如下:
2.1. 先决条件 复制链接链接已复制到粘贴板!
此 Camel 应用程序的实现需要满足以下先决条件:
外部 AMQ 7 JMS 消息代理必须正在运行。
以下示例代码运行
amq-broker-7.1.0-bin.zip的独立(非 Docker)版本。执行创建并运行amq7实例:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 客户端库是必需的。Artemis 库在 Maven Central 或红帽存储库中提供。例如,您可以使用:
-
mvn:org.apache.activemq/artemis-core-client/2.4.0.amq-710008-redhat-1 -
mvn:org.apache.activemq/artemis-jms-client/2.4.0.amq-710008-redhat-1
或者,Artemis/AMQ 7 客户端库也可以作为 Karaf 功能安装,例如:
-
Karaf@root ()> 功能:install artemis-jms-client artemis-core-client
-
需要一些提供 Karaf shell 命令或专用 Artemis 支持的功能:
karaf@root()> feature:install jms pax-jms-artemis pax-jms-config
karaf@root()> feature:install jms pax-jms-artemis pax-jms-configCopy to Clipboard Copied! Toggle word wrap Toggle overflow 所需的 Camel 功能包括:
karaf@root()> feature:install camel-jms camel-blueprint
karaf@root()> feature:install camel-jms camel-blueprintCopy to Clipboard Copied! Toggle word wrap Toggle overflow
2.2. 构建 camel-jms 项目 复制链接链接已复制到粘贴板!
您可以从 Fuse Software Downloads 页面下载 快速入门。
将 zip 文件的内容提取到本地文件夹中,例如一个名为 quickstarts 的新文件夹。
然后,您可以构建并安装 /camel/camel-jms 示例作为 OSGi 捆绑包。此捆绑包包含 Camel 路由的蓝图 XML 定义,用于将消息发送到 AMQ 7 JMS 队列。
在以下示例中,$FUSE_HOME 是解压缩 Fuse 分发的位置。构建此项目:
调用 Maven 以构建项目:
cd quickstarts mvn clean install -f camel/camel-jms/
$ cd quickstarts $ mvn clean install -f camel/camel-jms/Copy to Clipboard Copied! Toggle word wrap Toggle overflow 创建 JMS 连接工厂配置,使得
javax.jms.ConnectionFactory服务在 OSGi 运行时中发布。为此,请将quickstarts/camel/camel-jms/src/main/resources/etc/org.ops4j.connectionfactory-amq7.cfg复制到$FUSE_HOME/etc目录中。将处理此配置来创建正常工作的连接工厂。例如:cp camel/camel-jms/src/main/resources/etc/org.ops4j.connectionfactory-amq7.cfg ../etc/
$ cp camel/camel-jms/src/main/resources/etc/org.ops4j.connectionfactory-amq7.cfg ../etc/Copy to Clipboard Copied! Toggle word wrap Toggle overflow 验证发布的连接工厂:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 安装捆绑包:
karaf@root()> install -s mvn:org.jboss.fuse.quickstarts/camel-jms/7.0.0.redhat-SNAPSHOT Bundle ID: 256
karaf@root()> install -s mvn:org.jboss.fuse.quickstarts/camel-jms/7.0.0.redhat-SNAPSHOT Bundle ID: 256Copy to Clipboard Copied! Toggle word wrap Toggle overflow 确认它正在正常工作:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
Camel 路由启动后,您可以在 Fuse 安装中看到目录
work/jms/input。将您在这个 quickstart 的src/main/data目录中找到的文件复制到新创建的work/jms/input目录中。 稍等片刻,您将在
work/jms/output目录下找到国家按国家组织相同的文件:-
order1.xml,order2.xml和order4.xmlinwork/jms/output/others -
work/jms/output/us中的order3.xml和order5.xml -
order6.xmlinwork/jms/output/fr
-
查看日志以查看业务日志记录:
2018-05-02 17:20:47,952 | INFO | ile://work/jms/input | file-to-jms-route | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Receiving order order1.xml 2018-05-02 17:20:48,052 | INFO | umer[incomingOrders] | jms-cbr-route | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Sending order order1.xml to another country 2018-05-02 17:20:48,053 | INFO | umer[incomingOrders] | jms-cbr-route | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Done processing order1.xml
2018-05-02 17:20:47,952 | INFO | ile://work/jms/input | file-to-jms-route | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Receiving order order1.xml 2018-05-02 17:20:48,052 | INFO | umer[incomingOrders] | jms-cbr-route | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Sending order order1.xml to another country 2018-05-02 17:20:48,053 | INFO | umer[incomingOrders] | jms-cbr-route | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Done processing order1.xmlCopy to Clipboard Copied! Toggle word wrap Toggle overflow 查看队列是动态创建的:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 检查 Camel 路由统计信息:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.3. camel-jms 项目的解释 复制链接链接已复制到粘贴板!
Camel 路由使用以下端点 URI:
jms 组件使用此片断进行配置:
在 transactionManager 引用时:
<reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />
<reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />
正如您所见,JMS 连接工厂和 PlatformTransactionManager 的 Spring 接口仅引用。不需要在蓝图 XML 中定义 它们。这些服务 由 Fuse 本身公开。
您已看到 javax.jms.ConnectionFactory 是使用 etc/org.ops4j.connectionfactory-amq7.cfg 创建的。
事务管理器是:
检查注册实际事务管理器的其他接口:
事务管理器可从这些接口获得:
-
javax.transaction.TransactionManager -
javax.transaction.TransactionSynchronizationRegistry -
javax.transaction.UserTransaction -
org.jboss.narayana.osgi.jta.ObjStoreBrowserService -
org.ops4j.pax.transx.tm.TransactionManager -
org.springframework.transaction.PlatformTransactionManager
您可以在您需要的任何上下文中使用它们。例如,camel-jms 需要初始化 org.apache.camel.component.jms.JmsConfiguration.transactionManager 字段。这就是示例使用的原因:
<reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />
<reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />
例如:
<reference id="transactionManager" interface="javax.transaction.TransactionManager" />
<reference id="transactionManager" interface="javax.transaction.TransactionManager" />
第 3 章 用于配置和引用事务管理器的接口 复制链接链接已复制到粘贴板!
JSR 和 Spring Boot 各自提供一个事务客户端接口,用于在 Fuse 中配置事务管理器,并在已部署的应用中使用事务管理器。配置之间有明确的区别,即管理任务和引用,这是开发任务。应用程序开发人员负责将应用指向之前配置的事务管理器。
3.1. 事务管理器的作用 复制链接链接已复制到粘贴板!
事务管理器是应用程序的一部分,负责跨一个或多个资源协调事务。事务管理器的职责如下:
- demarcation - 使用 begin、commit 和 rollback 方法开始和结束事务。
- 管理事务上下文 - 事务上下文包含事务管理器跟踪事务所需的信息。事务管理器负责创建事务上下文并将其附加到当前线程中。
协调多个资源之间的事务 - 企业级的事务通常能够跨多个资源协调事务。此功能要求使用 XA 协议注册和管理 2 阶段提交协议和资源。请参阅 第 1.7.1.2 节 “支持 XA 标准”。
这是一个高级功能,不受到所有事务管理器的支持。
- 从失败中恢复 - 事务管理器负责确保在系统故障且应用失败时不会处于不一致状态。在某些情况下,可能需要人工干预才能将系统恢复到一致的状态。
3.2. 关于本地、全局和分布式事务管理器 复制链接链接已复制到粘贴板!
事务管理器可以是 local、global 或 distributed。
3.2.1. 关于本地事务管理器 复制链接链接已复制到粘贴板!
本地事务管理器 是一个事务管理器,只能协调单个资源的事务。本地事务管理器的实施通常嵌入到资源本身中,应用程序使用的事务管理器是围绕此内置事务管理器的精简包装器。
例如,Oracle 数据库有一个内置事务管理器,它支持 decation 操作(使用 SQL BEGIN、COMMIT、or zFCPLBACK 语句,或使用原生 Oracle API)以及各种级别的事务隔离。可以通过 JDBC 导出对 Oracle 事务管理器的控制,应用程序使用此 JDBC API 来划分事务。
在此上下文中,了解构成资源的内容非常重要。例如,如果您使用 JMS 产品,JMS 资源是 JMS 产品的单个正在运行的实例,而不是单个队列和主题。此外,如果以不同方式访问相同的底层资源,有时似乎是多个资源可能实际上是一个资源。例如,您的应用可能会直接访问关系数据库(通过 JDBC)和间接访问(通过对象关系映射工具,如 Hibernate)。在这种情况下,涉及相同的底层事务管理器,因此应可以在相同的事务中注册这两个代码片段。
无法保证每个情况下都可以正常工作。虽然原则上可能,但设计 Spring Framework 或其他打包程序层的一些细节可能会阻止它在实践中工作。
应用可能具有许多不同的本地事务管理器相互独立工作。例如,您可以有一个 Camel 路由来处理 JMS 队列和主题,其中 JMS 端点引用 JMS 事务管理器。另一个路由可以通过 JDBC 访问关系数据库。但是您不能在同一路由中组合 JDBC 和 JMS 访问,并且它们都参与同一事务。
3.2.2. 关于全局事务管理器 复制链接链接已复制到粘贴板!
全局事务管理器是一个事务管理器,它可以协调多个资源上的事务。当您无法依赖内置于资源本身的事务管理器时,这是必需的。外部系统有时被称为事务处理监控器(TP monitor),能够跨不同资源协调事务。
以下是在多个资源上运行的事务的先决条件:
- 全局事务管理器或 TP monitor - 实现 2 阶段提交协议的外部事务系统,用于协调多个 XA 资源。
- 支持 XA 标准 的资源 - 要参与 2 阶段提交,资源必须支持 XA 标准。请参阅 第 1.7.1.2 节 “支持 XA 标准”。实际上,这意味着资源能够导出 XA 交换机 对象,它可以完全控制到外部 TP 监控器的事务。
Spring Framework 本身不提供 TP 监控器来管理全局事务。但是,它确实支持与 OSGi 提供的 TP monitor 集成,或与 gRPC 提供的 TP monitor 集成(其中集成由 JtaTransactionManager 类实施)。因此,如果您将应用程序部署到带有完整事务支持的 OSGi 容器中,您可以在 Spring 中使用多个事务资源。
3.2.3. 关于分布式事务管理器 复制链接链接已复制到粘贴板!
通常,服务器直接连接到事务中涉及的资源。但是,在分布式系统中,偶尔需要通过 Web 服务连接到仅间接公开的资源。在这种情况下,您需要一个能够支持分布式事务的 TP 监控器。有几个标准可用于描述如何支持各种分布式协议的事务,例如,适用于 Web 服务的 WS-AtomicTransactions 规格。
3.3. 使用>=< 事务客户端 复制链接链接已复制到粘贴板!
使用 802-1 时,与事务管理器交互的最资金和标准方法是 Java Transaction API (JTA)接口 javax.transaction.UserTransaction。规范用法是:
从 JNDI (Java 命名和目录接口)获取用户 事务 实例是获取事务客户端的一种方式。在 interaction 环境中,您可以访问事务客户端,例如使用 CDI (上下文和依赖项注入)。
下图显示了 typica developer Camel 应用。
图显示 Camel 代码和应用程序代码都可以访问:
-
javax.transaction.UserTransaction实例,可以使用 SpringTransactionTemplate类直接从应用程序或内部处理事务处理。 -
数据库通过 JDBC API 直接或使用 Spring 的
JdbcTemplate,或者使用camel-jdbc组件。 -
使用 Spring 的
JmsTemplate类或使用camel-jms组件直接通过 JMS API 进行消息代理。
使用 javax.transaction.UserTransaction 对象时,您不需要了解正在使用的实际事务管理器,因为您只直接使用事务客户端。(请参阅 第 1.3 节 “关于事务客户端”。) Spring 和 Camel 采用不同的方法,因为它在内部使用 Spring 的事务设施。
iwl Application
在典型的>=< 情景中,应用被部署到 ausearch 应用服务器,通常作为 WAR 或 EAR 存档。通过 JNDI 或 CDI,应用程序可以访问 javax.transaction.UserTransaction 服务的实例。然后,使用这个事务客户端实例来划分事务。在事务中,应用执行 JDBC 和/或 JMS 访问。
Camel 组件和应用程序代码
它们代表了执行 JMS/JDBC 操作的代码。Camel 具有自己的高级方法来访问 JMS/JDBC 资源。应用程序代码可以直接使用给定的 API。
JMS Connection Factory
这是 javax.jms.ConnectionFactory 接口,用于获取 javax.jms.Connection 的实例,然后是 javax.jms.Session (或 JMS 2.0 中的 javax.jms.JmsContext )。这可以由应用程序直接使用,或者在 Camel 组件中间接使用,这些组件可以在内部使用 org.springframework.jms.core.JmsTemplate。应用程序代码和 Camel 组件都不需要此连接工厂的详细信息。连接工厂在应用服务器中配置。您可以在 regex 服务器中看到此配置。Fuse 等 OSGi 服务器类似。系统管理员独立于应用程序配置连接工厂。通常,连接工厂实现池功能。
JDBC 数据源
这是 javax.sql.DataSource 接口,用于获取 java.sql.Connection 的实例。与 JMS 一样,此数据源可以直接或间接使用。例如,camel-sql 组件在内部使用 org.springframework.jdbc.core.JdbcTemplate 类。与 JMS 一样,应用程序代码和 Camel 都不需要此数据源的详细信息。配置是在应用服务器内或 OSGi 服务器内完成的,方法是使用 第 4 章 配置 Narayana 事务管理器 中描述的方法。
3.4. 使用 Spring Boot 事务客户端 复制链接链接已复制到粘贴板!
Spring Framework (和 Spring Boot)的一个主要目标是使 QPC API 更易于使用。所有主要的 vanilla API 将其部分在 Spring 框架(Spring Boot)中。这些不是给定 API 的替代方案 或替换,而是添加更多配置选项或更一致的用法的打包程序,例如处理异常。
下表与 Spring 相关的接口匹配给定了>=< API:
| JavaEE API | Spring utility | 配置为 |
|---|---|---|
| JDBC |
|
|
| JMS |
|
|
| JTA |
|
|
JdbcTemplate 和 JmsTemplate 分别直接使用 javax.sql.DataSource 和 javax.jms.ConnectionFactory。但是 TransactionTemplate 使用 PlatformTransactionManager 的 Spring 接口。在这里,Spring 并不只是 改进 QPC,而是将 binutils 客户端 API 替换为自己的 API。
Spring 将 javax.transaction.UserTransaction 视为接口,对于真实情况来说非常简单。另外,因为 javax.transaction.UserTransaction 没有区分本地、单一资源事务和全局多资源事务,因此 org.springframework.transaction.PlatformTransactionManager 的实现使开发人员更自由。
以下是 Spring Boot 的规范 API 用法:
在上例中,所有三种 模板 都仅实例化,但它们也可以从 Spring 的 ApplicationContext 获取,也可以使用 @Autowired 注解注入。
3.4.1. 使用 Spring PlatformTransactionManager 接口 复制链接链接已复制到粘贴板!
如前文所述,javax.transaction.UserTransaction 通常从 QPC 应用中的 JNDI 获取。但是,Spring 为许多场景提供明确的这个接口实现。您不需要完整的 JTA 场景,有时应用程序只需要访问单个资源,如 JDBC。
通常,org.springframework.transaction.PlatformTransactionManager 是 Spring 事务客户端 API,它提供经典事务客户端操作: begin、commit 和 rollback。换句话说,这个接口提供了在运行时控制事务的基本方法。
任何事务系统的其他关键方面都是实施事务资源的 API。但是,事务资源通常由底层数据库实施,因此该事务编程方面很少会考虑应用程序。
3.4.1.1. PlatformTransactionManager 接口的定义 复制链接链接已复制到粘贴板!
3.4.1.2. 关于 TransactionDefinition 接口 复制链接链接已复制到粘贴板!
您可以使用 TransactionDefinition 接口指定新创建的事务的特征。您可以指定隔离级别和新事务的传播策略。详情请查看 第 9.4 节 “事务传播策略”。
3.4.1.3. TransactionStatus 接口的定义 复制链接链接已复制到粘贴板!
您可以使用 TransactionStatus 接口来检查当前事务的状态,即与当前线程关联的事务,并标记当前的事务进行回滚。这是接口定义:
3.4.1.4. 平台TransactionManager 接口定义的方法 复制链接链接已复制到粘贴板!
PlatformTransactionManager 接口定义以下方法:
- getTransaction()
-
创建新的事务,并通过传递定义新事务的特征的
TransactionDefinition对象来将其与当前的线程相关联。这与许多其他事务客户端 API 的begin ()方法类似。 - commit()
- 提交当前事务,从而对已注册资源进行所有待处理的更改。
- rollback()
- 回滚当前事务,从而撤销对注册资源的所有待处理更改。
3.4.2. 使用事务管理器的步骤 复制链接链接已复制到粘贴板!
通常,您不直接使用 PlatformTransactionManager 接口。在 Apache Camel 中,您通常使用事务管理器,如下所示:
- 创建事务管理器的实例。Spring 中有几个不同的实现,请参阅 第 3.4 节 “使用 Spring Boot 事务客户端”。
-
将事务管理器实例传递到 Apache Camel 组件或路由中的
transacted ()DSL 命令。然后,事务组件或transacted ()命令负责处理事务。详情请查看 第 9 章 编写使用事务的 Camel 应用程序。
3.4.3. 关于 Spring PlatformTransactionManager 实现 复制链接链接已复制到粘贴板!
本节概述了 Spring Framework 提供的事务管理器实施。这些实施分为两类:本地事务管理器和全局事务管理器。
从 Camel 开始:
-
对象需要一个camel-jms组件使用的 org.apache.camel.component.JmsConfigurationorg.springframework.transaction.PlatformTransactionManager接口的实例。 -
org.apache.camel.component.sql.SqlComponent在内部使用org.springframework.jdbc.core.JdbcTemplate类,并且此 JDBC 模板也与org.springframework.transaction.PlatformTransactionManager集成。
正如您所见,必须有 这个接口的一些 实现。根据具体情况,您可以配置所需的平台事务管理器。
3.4.3.1. 本地平台TransactionManager 实现 复制链接链接已复制到粘贴板!
以下列表总结了 Spring Framework 提供的本地事务管理器实施。这些事务管理器只支持一个资源。
- org.springframework.jms.connection.JmsTransactionManager
- 此事务管理器实施能够管理 单个 JMS 资源。您可以连接到任意数量的队列或主题,但只有在它们属于同一底层 JMS 消息传递产品实例时。此外,您无法在事务中列出任何其他类型的资源。
- org.springframework.jdbc.datasource.DataSourceTransactionManager
- 此事务管理器实施能够管理 单个 JDBC 数据库资源。您可以更新任意数量的不同的数据库表,但仅在 它们属于同一底层数据库实例时。
- org.springframework.orm.jpa.JpaTransactionManager
- 此事务管理器实施能够管理 Java Persistence API (jpa)资源。但是,无法同时列出事务中任何其他种类的资源。
- org.springframework.orm.hibernate5.HibernateTransactionManager
- 此事务管理器实施能够管理 Hibernate 资源。但是,无法同时列出事务中任何其他种类的资源。此外,比原生 Hibernate API 首选 JPA API。
另外还有其他常用的,平台TransactionManager 的实现。
3.4.3.2. 全局 PlatformTransactionManager 实现 复制链接链接已复制到粘贴板!
Spring Framework 提供了一个全局事务管理器实现,供 OSGi 运行时使用。org.springframework.transaction.jta.JtaTransactionManager 支持事务中的多个资源的操作。此事务管理器支持 XA 事务 API,并可在事务中列出多个资源。要使用此事务管理器,您必须将应用程序部署到 OSGi 容器或 iwl 服务器。
虽然 PlatformTransactionManager 的单资源实现是实际 实现,但JtaTransactionManager 更是标准 javax.transaction.TransactionManager 的实际实现的打包程序。
这就是为什么最好在一个环境中使用平台事务管理器的 Jta 实现(通过 JNDI 或 CDI)一个已经配置的 TransactionManager javax.transaction.TransactionManager 实例,通常是 javax.transaction.UserTransaction。通常,这两个 JTA 接口都通过单个对象/服务来实施。
以下是配置/使用 JtaTransactionManager 的示例:
在上例中,实际为 JTA 对象(UserTransaction 和 TransactionManager)的实例是从 JNDI 获取的。在 OSGi 中,它们也可以从 OSGi 服务注册表获取。
3.5. 事务客户端和事务管理器之间的 OSGi 接口 复制链接链接已复制到粘贴板!
在 description of the client API 和 Spring Boot 事务客户端 API 的描述后,查看 OSGi 服务器(如 Fuse)中的关系会很有帮助。OSGi 的一个功能是全局服务 registry,可用于:
- 通过过滤或接口查找服务。
- 使用给定接口和属性注册服务。
同样,在>=< 应用程序服务器中部署的应用程序使用 JNDI (服务 locator 方法)获取对 javax.transaction.UserTransaction 的引用,或者使它们被 CDI注入(依赖项注入 方法)注入,您可以使用以下方法获取相同的引用(直接或间接):
-
调用
org.osgi.framework.BundleContext.getServiceReference ()方法(服务 locator)。 - 将它们注入到 Blueprint 容器中。
- 使用 Service 组件运行时(SCR)注解(依赖项注入)。
下图显示了在 OSGi 运行时中部署的 Fuse 应用程序。应用程序代码和/或 Camel 组件使用其 API 获取事务管理器、数据源和连接工厂的引用。
应用程序(捆绑包)与 OSGi 注册表中注册的服务交互。通过 接口执行访问权限,这与应用程序应相关。
在 Fuse 中,实施(直接或通过小包装器)事务的客户端接口的基本对象是 org.jboss.narayana.osgi.jta.internal.OsgiTransactionManager。您可以使用以下接口访问事务管理器:
-
javax.transaction.TransactionManager -
javax.transaction.UserTransaction -
org.springframework.transaction.PlatformTransactionManager -
org.ops4j.pax.transx.tm.TransactionManager
您可以直接使用这些接口,或通过选择框架或库(如 Camel)来隐式使用它们。
有关在 Fuse 中配置 org.jboss.narayana.osgi.jta.internal.OsgiTransactionManager 的方法的详情,请参考 第 4 章 配置 Narayana 事务管理器。本指南中的后续章节将构建本章中的信息,并描述了如何配置和使用其他服务,如 JDBC 数据源和 JMS 连接工厂。
第 4 章 配置 Narayana 事务管理器 复制链接链接已复制到粘贴板!
在 Fuse 中,内置的全局事务管理器是 JBoss Narayana Transaction Manager,后者是企业应用平台(EAP) 7 使用的同一事务管理器。
在 OSGi 运行时中,与用于 Karaf 的 Fuse 一样,额外的集成层由 PAX TRANSX 项目提供。
以下主题讨论 Narayana 配置:
4.1. 关于 Narayana 安装 复制链接链接已复制到粘贴板!
Narayana 事务管理器在以下接口下的 OSGi 捆绑包中公开使用,以及一些额外的支持接口:
-
javax.transaction.TransactionManager -
javax.transaction.UserTransaction -
org.springframework.transaction.PlatformTransactionManager -
org.ops4j.pax.transx.tm.TransactionManager
7.5.0.fuse-750035-redhat-00001 发布使这些接口在启动时可用。
pax-transx-tm-narayana 功能包含一个覆盖捆绑包,用于嵌入 Narayana:
fuse-pax-transx-tm-narayana 捆绑包提供的服务有:
由于此捆绑包注册 org.osgi.service.cm.ManagedService,所以它会跟踪并响应 CM 配置中的更改:
默认的 org.ops4j.pax.transx.tm.narayana PID 为:
概述:
- 用于 Karaf 的 Fuse 包括 Narayana 事务管理器的全功能全球。
- 事务管理器在各种客户端接口(JTA、Spring-tx、PAX JMS)下正确公开。
-
您可以使用标准 OSGi 方法 Configuration Admin 来配置 Narayana,该方法可在
org.ops4j.pax.transx.tm.narayana中提供。 -
默认配置在
$FUSE_HOME/etc/org.ops4j.pax.transx.tm.narayana.cfg中提供。
4.2. 支持的事务协议 复制链接链接已复制到粘贴板!
Narayana 事务管理器 是 EAP 中使用的 JBoss/红帽产品。Narayana 是一个交易工具包,为使用各种标准交易协议开发的应用程序提供支持:
- JTA
- JTS
- web-Service Transactions
- REST Transactions
- STM
- XATMI/TX
4.3. 关于 Narayana 配置 复制链接链接已复制到粘贴板!
pax-transx-tm-narayana 捆绑包包括 jbossts-properties.xml 文件,它提供事务管理器的不同方面的默认配置。所有这些属性都可以在 $FUSE_HOME/etc/org.ops4j.pax.transx.tm.narayana.cfg 文件中直接覆盖,或使用 Configuration Admin API 覆盖。
Narayana 的基本配置是通过各种 EnvironmentBean 对象实现的。每个 bean 都可以通过使用具有不同前缀的属性来配置。下表提供了使用的配置对象和前缀的概述:
| 配置 Bean | 属性前缀 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
前缀 可以简化配置。但是,您通常使用以下格式之一:
NameEnvironmentBean.propertyName (首选格式)或
fully-qualified-class-name.field-name
例如,考虑 com.arjuna.ats.arjuna.common.CoordinatorEnvironmentBean.commitOnePhase 字段。它可以通过使用 com.arjuna.ats.arjuna.common. 属性进行配置,也可以使用更简单(preferred)表单 CoordinatorEnvironment.commitOnePhase 进行配置。有关如何设置属性以及可配置 Bean 的完整详情,请参阅 Narayana 产品文档。
CoordinatorEnvironmentBean.commitOnePhase
一些 Bean (如 ObjectStoreEnvironmentBean )可能会多次配置,每个指定实例都为不同的目的提供配置。在这种情况下,实例的名称在前缀(上述任意位置)和 field-name 之间使用。例如,可以使用命名的属性配置名为 communicationStore 的 ObjectStore 实例的对象存储类型:
-
com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.communicationStore.objectStoreType -
ObjectStoreEnvironmentBean.communicationStore.objectStoreType
4.4. 配置日志存储 复制链接链接已复制到粘贴板!
最重要的配置是对象日志存储的类型和位置。com.arjuna.ats.arjuna.objectstore.ObjectStoreAPI 接口通常有三个实现:
- com.arjuna.ats.internal.arjuna.objectstore.hornetq.HornetqObjectStoreAdaptor
-
在内部使用
org.apache.activemq.artemis.core.journal.Journal存储。 - com.arjuna.ats.internal.arjuna.objectstore.jdbc.JDBCStore
- 使用 JDBC 来保留 TX 日志文件。
- com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore (及专用实施)
- 使用基于文件的自定义日志存储。
默认情况下,Fuse 使用 com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore,它是 FileSystemStore 的专用实现。
Narayana 有三个存储 保留了事务/对象日志:
-
defaultStore -
communicationStore -
stateStore
如需了解更多详细信息,请参阅 Narayana 文档中的状态管理。
这三种 存储的默认配置是 :
ShadowNoFileLockStore 使用基础目录(objectStoreDir)和特定存储的目录(localOSRoot)配置。
Narayana 文档 指南 中包含许多配置选项。但是,Narayana 文档指出,对配置选项的规范引用是各种 EnvironmentBean 类的 Javadoc。
第 5 章 使用 Narayana 事务管理器 复制链接链接已复制到粘贴板!
本节通过实施 javax.transaction.UserTransaction 接口、org.springframework.transaction.PlatformTransactionManager 接口或 javax.transaction.Transaction 接口来使用 Narayana 事务管理器的详细信息。您选择使用哪个接口取决于应用程序的需求。在本章的结尾处,可以讨论列出 XA 资源的问题解决办法。该信息组织如下:
有关 Java 事务 API 详情,请查看 Java Transaction API (JTA) 1.2 规格和 Javadoc。
5.1. 使用 UserTransaction 对象 复制链接链接已复制到粘贴板!
实施 javax.transaction.UserTransaction 接口以进行事务处理。也就是说,用于开始、提交或回滚事务。这是您很可能直接在应用程序代码中使用的 JTA 接口。但是,UserTransaction 接口只是分解事务的方法之一。有关您可以分离事务的不同方法的讨论,请参阅 第 9 章 编写使用事务的 Camel 应用程序。
5.1.1. UserTransaction 接口的定义 复制链接链接已复制到粘贴板!
JTA UserTransaction 接口定义如下:
5.1.2. 用户事务方法的描述 复制链接链接已复制到粘贴板!
UserTransaction 接口定义了以下方法:
- begin()
- 启动新的事务,并将其与当前线程相关联。如果有任何 XA 资源与此事务相关联,则事务会隐式成为 XA 事务。
- commit()
正常完成当前事务,以便所有待处理的更改变得永久。提交后,不再有与当前线程关联的事务。
注意但是,如果当前事务仅标记为回滚,则当称为
commit ()时实际会回滚事务。- rollback()
- 立即中止事务,以便丢弃所有待处理的更改。回滚后,不再有与当前线程关联的事务。
- setRollbackOnly()
- 修改当前事务的状态,以便回滚是唯一可能的结果,但尚未执行回滚。
- getStatus()
返回当前事务的状态,可以是以下整数值之一,如
javax.transaction.Status接口中定义的:-
STATUS_ACTIVE -
STATUS_COMMITTED -
STATUS_COMMITTING -
STATUS_MARKED_ROLLBACK -
STATUS_NO_TRANSACTION -
STATUS_PREPARED -
STATUS_PREPARING -
STATUS_ROLLEDBACK -
STATUS_ROLLING_BACK -
STATUS_UNKNOWN
-
- setTransactionTimeout()
- 自定义当前事务的超时时间,以秒为单位指定。如果在指定的超时时间内没有解决事务,事务管理器会自动回滚。
5.2. 使用 TransactionManager 对象 复制链接链接已复制到粘贴板!
使用 javax.transaction.TransactionManager 对象的最常见方法是将它传递给框架 API,例如,传递给 Camel JMS 组件。这可让框架在您事务处理后进行查找。有时,您可能想要直接使用 TransactionManager 对象。当您需要访问高级事务 API,如 suspend () 和 resume () 方法时,这非常有用。
5.2.1. TransactionManager 接口的定义 复制链接链接已复制到粘贴板!
JTA TransactionManager 接口有以下定义:
5.2.2. TransactionManager 方法的描述 复制链接链接已复制到粘贴板!
TransactionManager 接口支持 UserTransaction 接口中找到的所有方法。您可以使用 TransactionManager 对象进行事务处理。另外,TransactionManager 对象支持以下方法:
- getTransaction()
-
获取对当前事务的引用,这是与当前线程关联的事务。如果没有当前的事务,此方法会返回
null。 - suspend()
将当前事务从当前线程中分离,并返回对事务的引用。调用此方法后,当前线程不再有事务上下文。您在此时之后执行的所有工作都不再在事务环境中完成。
注意并非所有事务管理器都支持挂起事务。但是,Narayana 支持此功能。
- resume()
- 将暂停的事务重新附加到当前线程上下文。调用此方法后,事务上下文会被恢复,并在此时在事务环境中完成的所有工作。
5.3. 使用 Transaction 对象 复制链接链接已复制到粘贴板!
如果您要暂停/恢复事务,或者需要明确列出资源,您可能需要直接使用 javax.transaction.Transaction 对象。如 第 5.4 节 “解决 XA enlistment 问题” 所述,框架或容器通常会自动处理资源。
5.3.1. Transaction 接口的定义 复制链接链接已复制到粘贴板!
JTA Transaction 接口具有以下定义:
5.3.2. Transaction 方法的描述 复制链接链接已复制到粘贴板!
commit (), rollback (), setRollbackOnly (), 和 getStatus () 方法的行为与 UserTransaction 接口的对应方法相同。实际上,UserTransaction 对象是一个方便的打包程序,用于检索当前事务,然后在 Transaction 对象上调用对应的方法。
另外,Transaction 接口定义了以下方法,它在 UserTransaction 接口中没有对应的方法:
- enlistResource()
将 XA 资源与当前事务相关联。
注意这个方法是 XA 事务上下文中的关键重要。使用当前事务代表 XA 事务的功能,可以精确列出多个 XA 资源。另一方面,明确列出资源是微不足道的资源,您通常希望您的框架或容器为您完成此操作。例如,请参阅 第 5.4 节 “解决 XA enlistment 问题”。
- delistResource()
解除指定资源与事务相关的关联。flag 参数可以采用
javax.transaction.Transaction接口中定义的以下整数值之一:-
TMSUCCESS -
TMFAIL -
TMSUSPEND
-
- registerSynchronization()
-
使用当前事务注册
javax.transaction.Synchronization对象。Synchronization对象仅在提交准备阶段之前收到回调,并在事务完成后接收回调。
5.4. 解决 XA enlistment 问题 复制链接链接已复制到粘贴板!
要列出 XA 资源的标准 JTA 方法是将 XA 资源明确添加到当前 javax.transaction.Transaction 对象,它代表当前的事务。换句话说,每次新事务启动时,您必须明确列出 XA 资源。
5.4.1. 如何列出 XA 资源 复制链接链接已复制到粘贴板!
使用 事务 列出 XA 资源涉及在事务中调用 enlistResource () 方法。例如,给定一个 TransactionManager 对象和 XAResource 对象,您可以按如下方式列出 XAResource 对象:
列出资源的技巧方面是必须在 每个新事务上列出 资源,在开始使用资源前必须列出该资源。如果您明确列出资源,则可能会出现容易出错的代码,这些代码使用 enlistResource () 调用。此外,有时很难在正确的位置调用 enlistResource (),例如,如果您使用一个隐藏了一些事务详细信息的框架时,会出现这种情况。
5.4.2. 关于自动加入 复制链接链接已复制到粘贴板!
使用支持 XA 资源的自动连接的功能,而不是明确列出 XA 资源。例如,在使用 JMS 和 JDBC 资源的情况下,标准技术是使用支持自动加入的打包程序类。
JDBC 和 JMS 访问的常见模式是:
-
应用程序代码需要
javax.sql.DataSourcefor JDBC access 和javax.jms.ConnectionFactoryfor JMS 获取 JDBC 或 JMS 连接。 - 在应用程序/OSGi 服务器中,会注册这些接口的数据库或代理特定实现。
- application/OSGi 服务器将 database/broker 特定工厂嵌套成通用、池、清单工厂。
这样,应用程序代码仍然使用 javax.sql.DataSource 和 javax.jms.ConnectionFactory,但在访问它们时内部存在额外的功能,这通常涉及:
- 连接池 - 而不是在每次创建新连接时都创建新的连接,而是使用重新初始化的 连接池。池的另一个 方面可能是连接的定期验证。
-
JTA enlistment - 在返回
java.sql.Connection(JDBC)或javax.jms.Connection(JMS)的实例之前,如果实际连接对象是 true XA 资源,则会注册实际连接对象。注册会在 JTA 事务中发生(如果可用)。
通过自动协助,应用程序代码不必更改。
有关 JDBC 数据源和 JMS 连接工厂的池和配套打包程序的更多信息,请参阅 第 6 章 使用 JDBC 数据源 和 第 7 章 使用 JMS 连接工厂。
第 6 章 使用 JDBC 数据源 复制链接链接已复制到粘贴板!
以下主题讨论 Fuse OSGi 运行时中使用的 JDBC 数据源:
6.1. 关于连接接口 复制链接链接已复制到粘贴板!
用于执行数据操作的最重要 对象是 java.sql.Connection 接口的实现。从 Fuse 配置的角度来看,了解如何 获取 连接 对象非常重要。
包含相关对象的库有:
-
PostgreSQL:
mvn:org.postgresql/postgresql/42.2.5 -
MySQL:
mvn:mysql/mysql-connector-java/5.1.34
现有实现(包含在 驱动程序 JAR中)提供:
-
PostgreSQL:
org.postgresql.jdbc.PgConnection -
mysql:
com.mysql.jdbc. JDBC4Connection(请参阅com.mysql.jdbc.Driver)的各种connect*()方法。
这些实现包含特定于数据库的逻辑,用于执行 DML、DDL 和简单的事务管理。
在理论上,可以手动创建这些连接对象,但有两个 JDBC 方法用于隐藏详情以提供干净的 API:
-
java.sql.Driver.connect ()- 这个方法在独立应用程序中被长时间使用。 -
javax.sql.DataSource.getConnection ()- 这是使用 工厂 模式的首选方法。类似的方法用于从 JMS 连接工厂获取 JMS 连接。
此处不讨论 驱动程序管理器 方法。它足以说明此方法只是给定连接对象的普通构造器之上的小 层。
除了有效实现特定于数据库的通信协议的 java.sql.Connection 外,还有另外两个专用的 连接 接口:
-
javax.sql.PooledConnection代表物理连接。您的代码不会与这个池的连接直接交互。反之,使用getConnection ()方法获取的连接。这种间接允许管理应用服务器级别的连接池。使用getConnection ()获取的连接通常是代理。当此类代理连接关闭时,物理连接不会关闭,而是在受管连接池中再次可用。 -
javax.sql.XAConnection允许获取与javax.transaction.TransactionManager一起使用的 XA 感知连接的javax.transaction.xa.XAResource对象。由于javax.sql.XAConnection扩展javax.sql.PooledConnection,它还提供 'getConnection ()方法,它提供对具有典型 DML/DQL 方法的 JDBC 连接对象的访问。
6.2. JDBC 数据源概述 复制链接链接已复制到粘贴板!
JDBC 1.4 标准引入了 javax.sql.DataSource 接口,它充当 java.sql.Connection 对象的 工厂。通常,此类数据源绑定到 JNDI 注册表,并位于内或注入到 servlet 或 EJB 等 Java EE 组件中。主要方面是,这些数据源是在 应用服务器 内 配置的,并根据名称在部署的应用程序 中引用。
以下 连接 对象具有自己的 数据源 :
| 数据源 | 连接 |
|---|---|
|
|
|
|
|
|
|
|
|
以上每个 数据源 之间最重要的区别如下:
javax.sql.DataSource最重要的是一个 工厂-like 对象用于获取java.sql.Connection实例。大多数javax.sql.DataSource实施通常执行 连接池 的事实不应更改图片。这是应当供应用程序代码使用的唯一接口。您实施以下哪个项无关紧要:- 直接 JDBC 访问
-
JPA persistence 单元配置(<
jta-data-source>或 <non-jta-data-source>) - 流行的库,如 Apache Camel 或 Spring Framework
javax.sql.ConnectionPoolDataSource最重要的是一个通用(特定于数据库)连接池/数据源和特定于数据库的数据源之间的 桥接。它可能被视为 SPI 接口。应用程序代码通常处理从 JNDI 获取并由应用服务器(可能使用commons-dbcp2等库)实现的通用javax.sql.DataSource对象。在另一端,应用程序代码不直接与javax.sql.ConnectionPoolDataSource的接口。它用于应用服务器和特定于数据库的驱动程序。以下序列图显示了:
javax.sql.XADataSource是获取javax.sql.XAConnection和javax.transaction.xa.XAResource的方法。与javax.sql.ConnectionPoolDataSource相同,它在应用服务器和数据库特定驱动程序之间使用。以下是不同执行器的稍修改图表,这一次包括 JTA Transaction Manager:
如上两个图所示,您与 App Server 交互,它是一个通用的实体,您可以在其中配置 javax.sql.DataSource 和 javax.transaction.UserTransaction 实例。此类实例可以通过 JNDI 访问,也可以使用 CDI 或其他依赖项机制注入。
重要点是,即使应用使用 XA 事务和/或连接池,应用与 javax.sql.DataSource 交互,而不是另外两个 JDBC 数据源接口。
6.2.1. 特定于数据库和通用数据源 复制链接链接已复制到粘贴板!
JDBC 数据源实现分为两个类别:
通用
javax.sql.DataSource实现,例如:- Apache Commons DBCP (2)
- Apache Tomcat JDBC (基于 DBCP)
-
javax.sql.DataSource,javax.sql.XADataSource, 和javax.sql.ConnectionPoolDataSource的数据库特定实现
通用 javax.sql.DataSource 实现无法自行创建特定于数据库的连接,这可能会造成混淆。即使 通用 数据源可以使用 java.sql.Driver.connect () 或 java.sql.DriverManager.getConnection (),它通常最好使用特定于数据库的 javax.sql.DataSource 实现配置此 通用 数据源。
当 通用 数据源要与 JTA 交互时,必须将其配置为 javax.sql.XADataSource 的特定数据库实施。
为关闭图像,一般 数据源 通常不需要 javax.sql.ConnectionPoolDataSource 的特定数据库实现来执行连接池。现有池通常在没有标准 JDBC 接口的情况下处理池(javax.sql.ConnectionPoolDataSource 和 javax.sql.PooledConnection),而是使用自己的自定义实现。
6.2.2. 有些通用数据源 复制链接链接已复制到粘贴板!
考虑一个知名、通用数据源、Apache Commons DBCP (2) 示例。
javax.sql.XADataSource 实现
DBCP2 不包含 javax.sql.XADataSource 的任何实施,这是预期的。
javax.sql.ConnectionPoolDataSource implementations
DBCP2 包括了 javax.sql.ConnectionPoolDataSource 的实现:org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS。它通过调用 java.sql.DriverManager.getConnection () 来创建 javax.sql.PooledConnection 对象。这个池不应该直接使用,它应该被视为 驱动程序的适配器 :
-
不要提供自己的
javax.sql.ConnectionPoolDataSource实现 - 您需要根据 JDBC 建议对 连接池使用
如上图所示,驱动程序直接提供 javax.sql.ConnectionPoolDataSource,或提供 org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS 适配器的帮助,而 DBCP2 则通过以下之一实现 应用服务器 合同:
-
org.apache.commons.dbcp2.datasources.PerUserPoolDataSource -
org.apache.commons.dbcp2.datasources.SharedPoolDataSource
这两个池都会在配置阶段获取 javax.sql.ConnectionPoolDataSource 实例。
这是 DBCP2 中最重要的、最有趣的部分:
javax.sql.DataSource 实现
要实现连接池功能,您不必遵循 JDBC 建议 来使用 javax.sql.ConnectionPoolDataSource → javax.sql.PooledConnection SPI。
以下是 DBCP 2 的一般数据源列表:
-
org.apache.commons.dbcp2.BasicDataSource -
org.apache.commons.dbcp2.managed.BasicManagedDataSource -
org.apache.commons.dbcp2.PoolingDataSource -
org.apache.commons.dbcp2.managed.ManagedDataSource
这里有两个 axes:
基本 与 池
这个 axis 决定 池配置 方面。
两种类型的数据源都执行 java.sql.Connection 对象的 池。唯一的 区别是:
-
使用 bean 属性配置 基本 数据源,如
maxTotal或minIdle,用来配置org.apache.commons.pool2.impl.GenericObjectPool的内部实例。 -
池 数据源配置有外部创建的/配置
org.apache.commons.pool2.ObjectPool。
受管 与非管理
这个 axis 决定 连接创建 方面和 JTA 行为:
非管理的基本数据源通过使用 内部
java.sql.Driver.connect ()创建java.sql.Connection实例。非管理的池 数据源使用传递的
org.apache.commons.pool2.ObjectPool对象创建java.sql.Connection实例。受管池 数据源将
java.sql.Connection实例嵌套在org.apache.commons.dbcp2.managed.ManagedConnection对象内,确保在 JTA 上下文中调用javax.transaction.Transaction.enlistResource ()。但仍然从池配置的任何org.apache.commons.pool2.ObjectPool对象获取实际连接。受管的基本 数据源可为您配置专用的
org.apache.commons.pool2.ObjectPool。相反,配置特定于数据库的现有javax.sql.XADataSource对象就足够了。bean 属性用于创建org.apache.commons.pool2.impl.GenericObjectPool的内部实例,后者将传递到 受管池 数据源的内部实例(org.apache.commons.dbcp2.managed.ManagedDataSource)。
DBCP2 唯一无法执行的操作是 XA 事务恢复。DBCP2 在活跃的 JTA 事务中正确列出 XAResources,但它没有执行恢复。这应该单独完成,配置通常特定于所选的事务管理器实施(如 Narayana)。
6.2.3. 要使用的模式 复制链接链接已复制到粘贴板!
推荐的模式是:
-
创建或获取 特定于数据库的
javax.sql.DataSource或javax.sql.XADataSource实例,其中包含特定于数据库的配置(URL、凭证等),可以创建 connection/XA 连接。 -
创建或获取 特定于数据库的
javax.sql.DataSource实例(内部配置有上述、特定于数据库的数据源),以及特定于数据库的配置(连接池、事务管理器等)。 -
使用
javax.sql.DataSource获取java.sql.Connection的实例并执行 JDBC 操作。
以下是 规范 示例:
在 Fuse 环境中,有许多配置选项,且不需要使用 DBCP2。
6.3. 配置 JDBC 数据源 复制链接链接已复制到粘贴板!
如 OSGi 事务架构 中所述,必须在 OSGi 服务注册表中注册一些服务。正如您可以使用 javax.transaction.UserTransaction 接口(例如 javax.transaction.UserTransaction 接口) 找到 (查找)事务管理器实例一样,您可以使用 javax.sql.DataSource 接口与 JDBC 数据源执行相同的操作。要求是:
- 特定于数据库的数据源,可与目标数据库通信
- 通用数据源,您可以在其中配置池,以及可能进行事务管理(XA)
在 OSGi 环境中,如 Fuse,如果数据源作为 OSGi 服务注册,则可以从应用程序访问。从根本上讲,它按以下方式完成:
注册这些服务的方法有两种:
-
使用
jdbc:ds-createKaraf console 命令发布数据源。这是 配置方法。 -
使用 Blueprint、SOSOS Declarative Services (SCR)或仅
BundleContext.registerService ()API 调用等方法发布数据源。这个方法需要一个包含代码和/或元数据的专用 OSGi 捆绑包。这是 the_deployment method_。
6.4. 使用 OSGi JDBC 服务 复制链接链接已复制到粘贴板!
chapter 125 of the OSGi Enterprise R6 规范在 org.osgi.service.jdbc 软件包中定义了单一接口。这是 OSGi 处理数据源的方式:
如前文所述,可以从 java.sql.Driver 直接获取普通 java.sql.Connection 连接。
Generic org.osgi.service.jdbc.DataSourceFactory
org.osgi.service.jdbc.DataSourceFactory 的最简单实现是 org.ops4j.pax.jdbc.impl.DriverDataSourceFactory 由 mvn:org.ops4j.pax.jdbc/pax-jdbc/1.3.0 捆绑包提供。所有这些都跟踪捆绑包,其中可能包括标准 Java™ ServiceLoader 工具的 /META-INF/services/java.sql.Driver 描述符。如果您安装任何标准 JDBC 驱动程序,pax-jdbc 捆绑包注册一个 DataSourceFactory,它可以通过 java.sql.Driver.connect () 调用来获取连接。
在上述命令中,javax.sql.DataSource 服务仍未注册,但您更接近一个步骤。以上中间的 org.osgi.service.jdbc.DataSourceFactory 服务可用于获取:
-
java.sql.Driver -
javax.sql.DataSource通过将属性传递:url、user和passwordto thecreateDataSource ()方法。
您无法从非特定于数据库的 pax- 获取 jdbc 捆绑包创建的通用 org.sql.XADataSource 或 javax.sql. XADataSourcejavax.sql.ConnectionPoolDataSource 或 javax.sql.XADataSource。
mvn:org.postgresql/postgresql/42.2.5 捆绑包可以正确地实现 OSGi JDBC 规格,并使用实施的所有方法注册 org.osgi.service.jdbc.DataSourceFactory 实例,包括创建 XA 和 ConnectionPool 数据源的方法。
特定于数据库的 org.osgi.service.jdbc.DataSourceFactory 实现
还有其他捆绑包,例如:
-
mvn:org.ops4j.pax.jdbc/pax-jdbc-mysql/1.3.0 -
mvn:org.ops4j.pax.jdbc/pax-jdbc-db2/1.3.0 - …
这些捆绑包注册特定于数据库的 org.osgi.service.jdbc.DataSourceFactory 服务,它可以返回所有类型的 工厂,包括 javax.sql.ConnectionPoolDataSource 和 javax.sql.XADataSource。例如:
6.4.1. PAX- JDBC 配置服务 复制链接链接已复制到粘贴板!
使用 pax-jdbc (或 pax-jdbc-mysql,pax-jdbc-oracle, …)捆绑包时,您可以注册 org.osgi.service.jdbc.DataSourceFactory 服务,可用于获取给定数据库的数据源(请参阅 第 6.2.1 节 “特定于数据库和通用数据源”)。但是还没有实际的数据源。
mvn:org.ops4j.pax.jdbc/pax-jdbc-config/1.3.0 捆绑包提供了一个受管服务工厂,它有两个操作:
跟踪
org.osgi.service.jdbc.DataSourceFactoryOSGi 服务,以调用其方法:public DataSource createDataSource(Properties props); public XADataSource createXADataSource(Properties props); public ConnectionPoolDataSource createConnectionPoolDataSource(Properties props);
public DataSource createDataSource(Properties props); public XADataSource createXADataSource(Properties props); public ConnectionPoolDataSource createConnectionPoolDataSource(Properties props);Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
跟踪
org.ops4j.datasource工厂 PID,以收集以上方法所需的属性。如果您使用 Configuration Admin 服务的任何方法创建 工厂配置,例如,通过创建一个${karaf.etc}/org.ops4j.datasource-mysql.cfg文件,您可以执行最终步骤来公开特定于数据库的实际数据源。
以下是从全新的 Fuse 安装开始的详细 规范 逐步指南。
您明确安装捆绑包而不是功能,以精确显示需要哪些捆绑包。为方便起见,PAX JDBC 项目提供了多种数据库产品和配置方法的功能。
使用
/META-INF/services/java.sql.Driver安装 JDBC 驱动程序:karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34 Bundle ID: 223
karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34 Bundle ID: 223Copy to Clipboard Copied! Toggle word wrap Toggle overflow 安装 OSGi JDBC 服务捆绑包和
pax-jdbc-mysql捆绑包,该捆绑包注册 中介org.osgi.service.jdbc.DataSourceFactory:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 安装
pax-jdbc捆绑包和pax-jdbc-config捆绑包,该捆绑包跟踪org.osgi.service.jdbc.DataSourceFactory服务和org.ops4j.datasourcefactory PIDs:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 创建 工厂配置 (假设 MySQL 服务器正在运行):
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 检查
pax-jdbc-config是否将配置处理到javax.sql.DataSource服务中:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
您现在有一个特定于数据库的实际(还没有池)数据源。您可以已经注入需要它的位置。例如,您可以使用 Karaf 命令查询数据库:
在上例中,您可以看到 MySQL 警告。这不是一个问题。可以提供任何属性(不仅是 OSGi JDBC 特定属性):
6.4.2. 处理的属性摘要 复制链接链接已复制到粘贴板!
admin factory PID 配置中的属性传递给相关的 org.osgi.service.jdbc.DataSourceFactory 实现。
generic
org.ops4j.pax.jdbc.impl.DriverDataSourceFactory properties:
-
url -
user -
password
DB2
org.ops4j.pax.jdbc.db2.impl.DB2DataSourceFactory 属性包括这些实现类的所有 bean 属性:
-
com.ibm.db2.jcc.DB2SimpleDataSource -
com.ibm.db2.jcc.DB2ConnectionPoolDataSource -
com.ibm.db2.jcc.DB2XADataSource
PostgreSQL
Nnative org.postgresql.osgi.PGDataSourceFactory 属性包括 org.postgresql.PGProperty 中指定的所有属性。
HSQLDB
org.ops4j.pax.jdbc.hsqldb.impl.HsqldbDataSourceFactory properties:
-
url -
user -
password -
databaseName 的所有 bean 属性
-
org.hsqldb.jdbc.JDBCDataSource -
org.hsqldb.jdbc.pool.JDBCPooledDataSource -
org.hsqldb.jdbc.pool.JDBCXADataSource
-
SQL Server 和 Sybase
org.ops4j.pax.jdbc.jtds.impl.JTDSDataSourceFactory 属性包括 net.sourceforge.jtds.jdbcx.JtdsDataSource 的所有 bean 属性。
SQL Server
org.ops4j.pax.jdbc.mssql.impl.MSSQLDataSourceFactory 属性:
-
url -
user -
password -
databaseName -
serverName -
portNumber 的所有 bean 属性
-
com.microsoft.sqlserver.jdbc.SQLServerDataSource -
com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource -
com.microsoft.sqlserver.jdbc.SQLServerXADataSource
-
MySQL
org.ops4j.pax.jdbc.mysql.impl.MysqlDataSourceFactory properties:
-
url -
user -
password -
databaseName -
serverName -
portNumber 的所有 bean 属性
-
com.mysql.jdbc.jdbc2.optional.MysqlDataSource -
com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource -
com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
-
Oracle
org.ops4j.pax.jdbc.oracle.impl.OracleDataSourceFactory properties:
-
url -
databaseName -
serverName -
user -
password 的所有 bean 属性
-
oracle.jdbc.pool.OracleDataSource -
oracle.jdbc.pool.OracleConnectionPoolDataSource -
oracle.jdbc.xa.client.OracleXADataSource
-
SQLite
org.ops4j.pax.jdbc.sqlite.impl.SqliteDataSourceFactory properties:
-
url -
databaseName -
org.sqlite.SQLiteDataSource的所有 bean 属性
6.4.3. pax-jdb-config 捆绑包如何处理属性 复制链接链接已复制到粘贴板!
pax-jdbc-config 捆绑包处理前缀为 jdbc 的属性。所有这些属性都将删除这个前缀,剩余的名称将被传递。
下面是一个全新的 Fuse 安装开始的示例:
pax-jdbc-config 捆绑包需要这些属性:
-
osgi.jdbc.driver.name -
dataSourceName -
dataSourceType
查找并调用相关的 org.osgi.service.jdbc.DataSourceFactory 方法。前缀为 jdbc. 的属性会被传递(在删除前缀后),例如 org.osgi.service.jdbc.DataSourceFactory.createDataSource (properties)。但是,会添加这些属性,而不删除前缀,如 javax.sql.DataSource OSGi 服务的属性。
6.5. 使用 JDBC 控制台命令 复制链接链接已复制到粘贴板!
Fuse 提供 jdbc 功能,其中包含 jdbc:* 范围中的 shell 命令。前面的示例演示了使用 jdbc:query。另外,还有一些命令隐藏了创建配置管理员配置的需求。
从全新的 Fuse 实例开始,您可以使用通用 DataSourceFactory 服务注册数据库特定数据源,如下所示:
以下是注册 MySQL 特定 DataSourceFactory 服务的示例:
以上表可能会造成混淆,但如上所述,只有 pax-jdbc-database 捆绑包之一可以注册 org.osgi.service.jdbc.DataSourceFactory 实例,可以创建 不只是 委派至 java.sql.Driver.connect () 的 standard/XA/connection 池数据源。
以下示例创建并检查 MySQL 数据源:
可以看到,将为您创建 org.ops4j.datasource factory PID。但是,它不会自动存储在 ${karaf.etc} 中,可以使用 config:update。
6.6. 使用加密配置值 复制链接链接已复制到粘贴板!
pax-jdbc-config 功能能够处理加密值的配置管理配置。常用的解决方案是使用 Jasypt 加密服务,这些服务也供 Blueprint 使用。
如果任何 org.jasypt.encryption.StringEncryptor 服务在任何 alias service 属性中注册,您可以在数据源 工厂 PID 和使用加密的密码中拒绝它。下面是一个示例:
用于查找解密器服务的服务过滤器是 (& (objectClass=org.jasypt.encryption.StringEncryptor) (alias=<alias>),其中 < alias > 是数据源配置 工厂 PID 的 decryptor 属性的值。
6.7. 使用 JDBC 连接池 复制链接链接已复制到粘贴板!
本节介绍了使用 JDBC 连接池,然后演示如何使用这些连接池模块:
本章介绍了数据源管理内部的详细信息。虽然提供了关于 DBCP2 连接池功能的信息,但请记住,此连接池提供正确的 JTA 编码功能,但不提供 XA 恢复功能。
为确保 XA 恢复 已就位,请使用 pax-jdbc-pool-transx 或 pax-jdbc-pool-narayana 连接池模块。
6.7.1. 使用 JDBC 连接池简介 复制链接链接已复制到粘贴板!
前面的示例演示了如何注册特定于数据库的数据源 工厂。由于 数据源 本身是连接的工厂,org.osgi.service.jdbc.DataSourceFactory 可能会被视为元工厂,它应该能够生成三种类型的数据源,以及作为 bonus,一个 java.sql.Driver:
-
javax.sql.DataSource -
javax.sql.ConnectionPoolDataSource -
javax.sql.XADataSource
例如,pax-jdbc-mysql 注册一个 org.ops4j.pax.jdbc.mysql.impl.MysqlDataSourceFactory,它会产生:
-
javax.sql.DataSource→com.mysql.jdbc.jdbc2.optional.MysqlDataSource -
javax.sql.ConnectionPoolDataSource→com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource -
javax.sql.XADataSource→com.mysql.jdbc.jdbc2.optional.MysqlXADataSource -
java.sql.Driver→com.mysql.jdbc.Driver
PostgreSQL 驱动程序本身实施 OSGi JDBC 服务并生成:
-
javax.sql.DataSource→org.postgresql.jdbc2.optional.PoolingDataSource(如果指定了池相关的属性)或org.postgresql.jdbc2.optional.SimpleDataSource -
javax.sql.ConnectionPoolDataSource→org.postgresql.jdbc2.optional.ConnectionPool -
javax.sql.XADataSource→org.postgresql.xa.PGXADataSource -
java.sql.Driver→org.postgresql.Driver
如 规范数据源示例 所示,任何池、通用 数据源都要在 JTA 环境中工作,则需要一个 数据库特定 数据源来实际获取(XA)连接。
我们已有后者,我们需要实际的、通用、可靠的连接池。
规范数据源示例 演示了如何配置特定数据库数据源的通用池。pax-jdbc-pool indices 捆绑包与上述 org.osgi.service.jdbc.DataSourceFactory 服务平稳工作。
正如 OSGI Enterprise R6 JDBC 规范提供了 org.osgi.service.jdbc.DataSourceFactory 标准接口,pax-jdbc-pool-common 提供 专有 org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory 接口:
public interface PooledDataSourceFactory {
javax.sql.DataSource create(org.osgi.service.jdbc.DataSourceFactory dsf, Properties config)
}
public interface PooledDataSourceFactory {
javax.sql.DataSource create(org.osgi.service.jdbc.DataSourceFactory dsf, Properties config)
}
这个界面完全符合之前介绍的重要备注,值得重复:
即使应用使用 XA 事务和/或连接池,应用与 javax.sql.DataSource 交互,而不是另外两个 JDBC 数据源接口。
这个界面只是从特定于数据库且非池数据中创建池数据源。或者更精确地,它是一个 数据源工厂 (meta factory),它将特定于数据库数据源的工厂转换为池数据源的工厂。
没有这样可防止应用使用 org.osgi.service.jdbc.DataSource 对象为 已经为 javax.sql.DataSource 对象配置池,该服务javax.sql.DataSource 对象返回池。
下表显示了哪些捆绑包注册池数据源工厂。在表格中,o.o.p.j.p 的实例代表 org.ops4j.pax.jdbc.pool。
| 捆绑包(Bundle) | PooledDataSourceFactory | 池密钥 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
以上捆绑包只安装数据源,而不是数据源本身。应用需要调用 javax.sql.DataSource create (org.osgi.service.jdbc.DataSourceFactory dsf, Properties config) 方法的内容。
6.7.2. 使用 dbcp2 连接池模块 复制链接链接已复制到粘贴板!
有关通用数据源的部分提供了 如何使用和配置 Apache Commons DBCP 模块 的示例。本节演示了如何在 Fuse OSGi 环境中执行此操作。
考虑 第 6.4.1 节 “PAX- JDBC 配置服务” 捆绑包。除了跟踪以下内容外:
-
org.osgi.service.jdbc.DataSourceFactoryservices -
org.ops4j.datasourcefactory PIDs
该捆绑包还会跟踪 org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory 实例,它们由 pax-jdbc-pool reading 捆绑包之一注册。
如果 工厂配置包含 pool 属性,则 pax-jdbc-config 捆绑包注册的最终数据源是我们的数据库特定数据,但如果 pool=dbcp2则嵌套在以下之一:
-
org.apache.commons.dbcp2.PoolingDataSource -
org.apache.commons.dbcp2.managed.ManagedDataSource
这与 通用数据源示例 一致。除了 pool 属性和布尔值 xa 属性外,它选择非xa 或 xa 数据源,org.ops4j.datasource factory PID 可能会包含 前缀 属性:
-
pool.* -
factory.*
其中,每个属性都使用哪个 pax-jdbc-pool suppress 捆绑包被使用。对于 DBCP2,它是:
-
pool mdadm: bean 属性的org.apache.commons.pool2.impl.GenericObjectPoolConfig(xa 和非 xa 场景) -
factoryadtrust: bean 属性是org.apache.commons.dbcp2.managed.PoolableManagedConnectionFactory(xa)或org.apache.commons.dbcp2.PoolableConnectionFactory(non-xa)
以下是一个现实示例(除了使用 SSL=false)配置 DBCP2 池(org.ops4j.datasource-mysql factory PID),它使用 jdbc.-prefixed 属性的方便语法:
在上面的配置中,pool 和 xa 键是 提示 (服务过滤器属性)来选择一个注册的 org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory 服务。对于 DBCP2,这是:
为了完整性,以下是一个完整的示例,其连接池配置添加到 上例中。同样,这假设您是从全新的 Fuse 安装开始。
安装 JDBC 驱动程序:
karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34 Bundle ID: 223
karaf@root()> install -s mvn:mysql/mysql-connector-java/5.1.34 Bundle ID: 223Copy to Clipboard Copied! Toggle word wrap Toggle overflow 安装
jdbc、pax-jdbc-mysql和pax-jdbc-pool-dbcp2功能:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 创建 工厂配置 :
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 检查
pax-jdbc-config是否将配置处理到javax.sql.DataSource服务中:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 使用数据源:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
6.7.3. 使用 narayana 连接池模块 复制链接链接已复制到粘贴板!
pax-jdbc-pool-narayna 模块几乎执行所有操作为 pax-jdbc-pool-dbcp2。它为 XA 和非 XA 场景安装 DBCP2 特定的 org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory。唯一 的区别在于,XA 场景中有一个额外的集成点。org.jboss.tm.XAResourceRecovery OSGi 服务被注册为由 com.arjuna.ats.arjuna.recovery.RecoveryManager (这是 Narayana 事务管理器的一部分)。
6.7.4. 使用 transx 连接池模块 复制链接链接已复制到粘贴板!
pax-jdbc-pool-transx 捆绑包基础,它对 pax-transx-jdbc 捆绑包上的 org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory 服务实施。pax-transx-jdbc 捆绑包使用 org.ops4j.pax.transx.jdbc.ManagedDataSourceBuilder 工具创建 javax.sql.DataSource 池。这是一个 JCA (Java™ Connector Architecture)解决方案,稍后 进行介绍。
6.8. 将数据源部署为工件 复制链接链接已复制到粘贴板!
本章介绍了 OSGi JDBC 服务,显示 pax-jdbc 捆绑包帮助注册数据库特定和通用数据源,以及如何从 OSGi 服务和配置管理配置的角度来看。虽然配置 两种类型的数据源可以通过使用 Configuration Admin factory PID (来自 pax-jdbc-config 捆绑包的帮助)来完成,但通常最好使用 部署方法。
在 部署方法 中,javax.sql.DataSource 服务由应用程序代码直接注册,通常是在 Blueprint 容器中注册。蓝图 XML 可以是普通 OSGi 捆绑包的一部分,可通过使用 mvn: URI 进行安装,并存储在 Maven 存储库(本地或远程)。通过将捆绑包与 Configuration Admin 配置进行比较,更易于版本控制。
pax-jdbc-config 捆绑包版本 1.3.0 为数据源配置添加部署方法。应用程序开发人员注册 javax.sql. (XA) DataSource 服务(通常使用 Bluerpint XML)并指定服务属性。pax-jdbc-config 捆绑包检测到此类注册的数据库特定数据源,以及(使用服务属性)将服务嵌套在通用、非特定于数据库的连接池中。
为了完整起见,以下是使用 Blueprint XML 的三种 部署方法。Fuse 提供了 快速入门 下载,其中包含 Fuse 的不同方面的不同示例。您可以从 Fuse Software Downloads 页面下载 快速入门 zip 文件。
将快速入门 zip 文件的内容提取到本地文件夹。
在以下示例中,quickstarts/persistence 目录被称为 $PQ_HOME。
6.8.1. 手动部署数据源 复制链接链接已复制到粘贴板!
这个示例手动部署数据源使用基于 docker 的 PostgreSQL 安装。在此方法中,不需要 pax-jdbc-config。应用程序代码负责注册特定于数据库和通用数据源。
需要这三个捆绑包:
-
mvn:org.postgresql/postgresql/42.2.5 -
mvn:org.apache.commons/commons-pool2/2.5.0 -
mvn:org.apache.commons/commons-dbcp2/2.1.1
以上蓝图 XML 片段与 规范数据源示例 匹配。以下是显示如何使用它的 shell 命令:
如上表所示,Blueprint 捆绑包导出 javax.sql.DataSource 服务,该服务是一个通用的、非特定于数据库的连接池。特定于数据库的 javax.sql.XADataSource 捆绑包 没有 作为 OSGi 服务注册,因为 Blueprint XML 没有明确的 < service ref="postgresql"> 声明。
6.8.2. 数据源的工厂部署 复制链接链接已复制到粘贴板!
数据源的工厂部署以规范的方式使用 pax-jdbc-config 捆绑包。这与 Fuse 6.x 中推荐的方法有点不同,该方法需要指定池配置作为服务属性。
以下是 Blueprint XML 示例:
本例使用 工厂 Bean 使用数据源创建数据源。您不需要显式引用 javax.transaction.TransactionManager 服务,因为这由 XA 感知的 PooledDataSourceFactory 在内部进行跟踪。
以下是相同的示例,但在 Fuse/Karaf shell 中。
要让原生 org.osgi.service.jdbc.DataSourcFactory 捆绑包注册,请安装 mvn:org.osgi/org.osgi.service.jdbc/1.0.0,然后安装 PostgreSQL 驱动程序。
如上表所示,Blueprint 捆绑包导出 javax.sql.DataSource 服务,该服务是一个通用的、非特定于数据库的连接池。特定于数据库的 javax.sql.XADataSource 没有 作为 OSGi 服务注册,因为 Blueprint XML 没有明确的 < service ref="postgresql"> 声明。
6.8.3. 数据源的混合部署 复制链接链接已复制到粘贴板!
在数据源的混合部署中,pax-jdbc-config 1.3.0 捆绑包使用 服务属性 在池数据源中打包特定于数据库的数据源的另一个方法。这个方法与 Fuse 6.x 中的工作方式匹配。
以下是 Blueprint XML 示例:
在上例中,只会手动注册特定于数据库的数据源。pool=dbcp2 服务属性是数据源跟踪器的提示,它由 pax-jdbc-config 捆绑包管理。具有这个 service 属性的数据源服务嵌套在池数据源中,本例中为 pax-jdbc-pool-dbcp2。
以下是 Fuse/Karaf shell 中的相同示例:
如此列表中所示,jdbc:ds-list 输出中 有两个 数据源,即原始数据源和打包程序数据源。
javax.sql.XADataSource 从 Blueprint 捆绑包注册,并声明了 pool = dbcp2 属性。
javax.sql.DataSource 从 pax-jdbc-config 捆绑包中注册,并:
-
没有
pool = dbcp2属性(注册打包程序数据源时已被删除)。 -
具有
service.ranking = 1000属性,因此当它始终是根据名称查找数据源时的首选版本。 -
具有
pax.jdbc.managed = true属性,因此不会尝试再次打包它。 -
具有
pax.jdbc.service.id.ref = 336属性,以指示嵌套在连接池中的原始数据源服务。
6.9. 将数据源与 Java™ 持久性 API 搭配使用 复制链接链接已复制到粘贴板!
从事务管理的角度来看,了解数据源如何与 Java™ Persistence API (uildDefaults)搭配使用。本小节并不描述了 JPA 规范本身的详细信息,也没有有关 Hibernate 的详细信息,这是最已知的 JPA 实施。相反,本节演示了如何将 JPA 持久单位指向数据源。
6.9.1. 关于数据源引用 复制链接链接已复制到粘贴板!
META-INF/persistence.xml 描述符(请参阅 JPA 2.1 规格 8.2.1.5 jta-data-source, non-jta-data-source)定义了两种数据源引用:
-
<JTA-data-source> - 这是对支持 JTA 事务的JTA数据源的 JNDI 引用。 -
<non-jta-data-source> - 这是对支持 JTA 事务外的JTA数据源的引用。此数据源通常也用于初始化阶段,例如,使用hibernate.hbm2ddl.auto属性将 Hibernate 配置为自动创建数据库 schema。
这两个数据源与 javax.sql.DataSource 或 javax.sql.XADataSource! 这是开发 JPA 应用程序时常见的误解。这两个 JNDI 名称都必须引用 JNDI-bound javax.sql.DataSource 服务。
6.9.2. 引用 JNDI 名称 复制链接链接已复制到粘贴板!
当您注册带有 osgi.jndi.service.name 属性的 OSGi 服务时,它会在 OSGi JNDI 服务中 绑定。在 OSGi 运行时(如 Fuse/Karaf)中,JNDI 不是 name → value 对的简单字典。在 OSGi 中通过 JNDI 名称引用对象涉及服务查找和其他更为复杂的 OSGi 机制,如服务 hook。
在全新的 Fuse 安装中,以下列表演示了如何在 JNDI 中注册数据源:
正如您所见,数据源位于 osgi:service/jdbc/mysqlds JNDI 名称下。
但是,如果将 JPA 在 OSGi 中,您必须使用 完整的 JNDI 名称。以下是指定数据源引用的 META-INF/persistence.xml 片段示例:
如果没有上述配置,您可能会遇到这个错误:
Persistence unit "pu-name" refers to a non OSGi service DataSource
Persistence unit "pu-name" refers to a non OSGi service DataSource
第 7 章 使用 JMS 连接工厂 复制链接链接已复制到粘贴板!
本章论述了如何在 OSGi 中使用 JMS 连接工厂。从根本上,您使用以下方法实现它:
注册此类服务的方法有两种:
-
使用
jms:createKaraf console 命令发布连接工厂。这是 配置方法。 -
使用 Blueprint、SOSOS Declarative Services (SCR)或仅
BundleContext.registerService ()API 调用等方法发布连接工厂。这个方法需要一个包含代码和/或元数据的专用 OSGi 捆绑包。这是 部署方法。
详情包括在以下主题中:
7.1. 关于 OSGi JMS 服务 复制链接链接已复制到粘贴板!
处理 JDBC 数据源的 OSGi 方法与两个接口相关:
-
standard
org.osgi.service.jdbc.DataSourceFactory -
proprietary
org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory
对于 JMS,请考虑以下几点:
-
专有
org.ops4j.pax.jms.service.ConnectionFactoryFactory与标准 OSGi JDBCorg.osgi.service.jdbc.DataSourceFactory相同 -
专有
org.ops4j.pax.jms.service.PooledConnectionFactoryFactoryFactory 与专有 pax-jdbcorg.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory相同
对于专用的、特定于代理的 org.ops4j.pax.jms.service.ConnectionFactoryFactory 实现,有如下捆绑包:
-
mvn:org.ops4j.pax.jms/pax-jms-artemis/1.0.0 -
mvn:org.ops4j.pax.jms/pax-jms-ibmmq/1.0.0 -
mvn:org.ops4j.pax.jms/pax-jms-activemq/1.0.0
这些捆绑包注册特定于代理的 org.ops4j.pax.jms.service.ConnectionFactoryFactory 服务,它可以返回 javax.jms.ConnectionFactory 和 javax.jms.XAConnectionFactory 等 JMS 工厂。例如:
7.2. 关于 PAX-JMS 配置服务 复制链接链接已复制到粘贴板!
mvn:org.ops4j.pax.jms/pax-jms-config/1.0.0 捆绑包提供了一个 Managed Service Factory,它执行三个操作:
跟踪
org.ops4j.pax.jms.service.ConnectionFactoryFactoryOSGi 服务,以调用其方法:public ConnectionFactory createConnectionFactory(Map<String, Object> properties); public XAConnectionFactory createXAConnectionFactory(Map<String, Object> properties);
public ConnectionFactory createConnectionFactory(Map<String, Object> properties); public XAConnectionFactory createXAConnectionFactory(Map<String, Object> properties);Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
跟踪
org.ops4j.connectionfactoryfactory PIDs,以收集以上方法所需的属性。如果您使用可用于 Configuration Admin 服务的任何方法创建 工厂配置,例如,通过创建一个${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg文件,您可以执行最后一步来公开特定于代理的连接工厂。 -
跟踪
javax.jms.ConnectionFactory和javax.jms.XAConnectionFactory服务,将它们嵌套在 JMS 连接工厂内。
详情包括在以下主题中:
7.2.1. 为 AMQ 7.1 创建连接工厂 复制链接链接已复制到粘贴板!
以下是为 Artemis 代理创建连接因素的详细 规范、逐步指南。
使用
pax-jms-artemis功能和pax-jms-config功能安装 Artemis 驱动程序:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 创建 工厂配置 :
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 检查
pax-jms-config是否处理到javax.jms.ConnectionFactory服务中的配置:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 注意如果您指定了额外的 Artemis 配置,特别是
protocol=amqp,则将使用 QPID JMS 库而不是 Artemis JMS 客户端。还必须将amqp://协议用于jms.url属性。- 测试连接。
现在,您有一个特定于代理(还没有池)的连接工厂,您可以在需要时注入。例如,您可以使用 jms 功能中的 Karaf 命令:
以下列表显示了在切换协议时会发生什么:
7.2.2. 为 IBM MQ 8 或 IBM MQ 9 创建连接工厂 复制链接链接已复制到粘贴板!
本节介绍如何连接到 IBM MQ 8 和 IBM MQ 9。虽然 pax-jms-ibmmq 安装相关的 pax-jms 捆绑包,但由于许可原因,IBM MQ 驱动程序不会被安装。
- 进入 https://developer.ibm.com/messaging/mq-downloads/
- 登录。
- 点您要安装的版本,例如,点 IBM MQ 8.0 Client 或 IBM MQ 9.0 Client。
- 在出现的页面中,在底部,在下载版本表中点击您想要的版本。
-
在下一页中,选择具有
IBM-MQ-Install-Java-All后缀的最新版本。例如,下载8.0.0.10-WS-MQ-Install-Java-All或9.0.0.4-IBM-MQ-Install-Java-All。 - 提取下载的 JAR 文件的内容。
执行
bundle:install命令。例如,如果您将内容提取到/home/Downloads目录中,则输入如下命令:`bundle:install -s wrap:file:////home/Downloads/9.0.0.4-IBM-MQ-Install-Java-All/ibmmq9/wmq/JavaSE/com.ibm.mq.allclient.jar`.
`bundle:install -s wrap:file:////home/Downloads/9.0.0.4-IBM-MQ-Install-Java-All/ibmmq9/wmq/JavaSE/com.ibm.mq.allclient.jar`.Copy to Clipboard Copied! Toggle word wrap Toggle overflow 按如下所示创建连接工厂:
安装
pax-jms-ibmmq:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 创建 工厂配置 :
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 检查
pax-jms-config是否处理到javax.jms.ConnectionFactory服务中的配置:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 测试连接:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
您还可以检查消息是否从 IBM MQ Explorer 发送或从 Web 控制台发送。
7.2.3. 在 Apache Karaf 上的 Fuse 中使用 JBoss A-MQ 6.3 客户端 复制链接链接已复制到粘贴板!
您可以从 Fuse Software Downloads 页面下载 Fuse 快速入门。
将快速启动 zip 文件的内容提取到本地文件夹,例如一个名为 quickstarts 的文件夹。
您可以构建并安装 quickstarts/camel/camel-jms 示例作为 OSGi 捆绑包。此捆绑包包含 Camel 路由的蓝图 XML 定义,用于将消息发送到 JBoss A-MQ 6.3 JMS 队列。为 JBoss A-MQ 6.3 代理创建连接工厂的步骤如下:
7.2.3.1. 先决条件 复制链接链接已复制到粘贴板!
- 已安装 Maven 3.3.1 或更高版本。
- 您已在机器上安装了 Red Hat Fuse。
- 您已在计算机上安装了 JBoss A-MQ Broker 6.3。
- 您已从客户门户网站下载并解压缩了 Karaf quickstarts zip 文件中的 Fuse。
7.2.3.2. 流程 复制链接链接已复制到粘贴板!
-
导航到
quickstarts/camel/camel-jms/src/main/resources/OSGI-INF/blueprint/目录。 将以下 bean 替换为
camel-context.xml文件中的 id="jms" :Copy to Clipboard Copied! Toggle word wrap Toggle overflow 使用以下部分来实例化 JBoss A-MQ 6.3 连接工厂:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow JBoss A-MQ 6.3 连接工厂配置为连接到在
tcp://localhost:61616侦听的代理。默认情况下,JBoss A-MQ 使用 IP 端口值61616。连接工厂也配置为使用 userName/password 凭证 admin/admin。确保此用户已在代理协调中启用(或者您可以在此处自定义这些设置以匹配代理配置)。-
保存
camel-context.xml文件。 构建
camel-jms快速启动:cd quickstarts/camel/camel-jms mvn install
$ cd quickstarts/camel/camel-jms $ mvn installCopy to Clipboard Copied! Toggle word wrap Toggle overflow 成功安装 Quickstart 后,进入到
$FUSE_HOME/目录,并运行以下命令来在 Apache Karaf 服务器上启动 Fuse:./bin/fuse
$ ./bin/fuseCopy to Clipboard Copied! Toggle word wrap Toggle overflow 在 Apache Karaf 实例的 Fuse 上,安装
activemq-client功能和camel-jms功能:karaf@root()> feature:install activemq-client karaf@root()> feature:install camel-jms
karaf@root()> feature:install activemq-client karaf@root()> feature:install camel-jmsCopy to Clipboard Copied! Toggle word wrap Toggle overflow 安装
camel-jmsquickstart 捆绑包:karaf@root()> install -s mvn:org.jboss.fuse.quickstarts/camel-jms/{$fuseversion}karaf@root()> install -s mvn:org.jboss.fuse.quickstarts/camel-jms/{$fuseversion}Copy to Clipboard Copied! Toggle word wrap Toggle overflow 其中,将
{$fuseversion}替换为您刚才构建的 Maven 工件的实际版本(consult the camel-jms quickstart README 文件)。启动
JBoss A-MQ 6.3代理(您需要安装 JBoss A-MQ 6.3)。打开另一个终端窗口,再导航到 JBOSS_AMQ_63_INSTALLDIR 目录:cd JBOSS_AMQ_63_INSTALLDIR ./bin/amq
$ cd JBOSS_AMQ_63_INSTALLDIR $ ./bin/amqCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
Camel 路由启动后,您可以在 Fuse 安装中看到目录
work/jms/input。将您在这个 quickstart 的src/main/data目录中找到的文件复制到新创建的work/jms/input目录中。 稍等片刻,您将在
work/jms/output目录下找到国家按国家组织相同的文件:order1.xml, order2.xml and order4.xml in work/jms/output/others order3.xml and order5.xml in work/jms/output/us order6.xml in work/jms/output/frorder1.xml, order2.xml and order4.xml in work/jms/output/others order3.xml and order5.xml in work/jms/output/us order6.xml in work/jms/output/frCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用
log:display检查业务日志记录:Receiving order order1.xml Sending order order1.xml to another country Done processing order1.xmlReceiving order order1.xml Sending order order1.xml to another country Done processing order1.xmlCopy to Clipboard Copied! Toggle word wrap Toggle overflow
7.2.4. 处理的属性摘要 复制链接链接已复制到粘贴板!
Configuration Admin factory PID 中的属性传递到相关的 org.ops4j.pax.jms.service.ConnectionFactoryFactory 实现。
ActiveMQ
org.ops4j.pax.jms.activemq.ActiveMQConnectionFactoryFactory(JMS 1.1 only)传递给
org.apache.activemq.ActiveMQConnectionFactory.buildFromMap ()方法的属性artemis
org.ops4j.pax.jms.artemis.ArtemisConnectionFactoryFactory如果
protocol=amqp,属性将传递到org.apache.qpid.jms.util.util.PropertyUtil.setProperties ()方法,以配置org.apache.qpid.jms.JmsConnectionFactory实例。否则,
org.apache.activemq.artemis.utils.uri.BeanSupport.setData ()为org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory实例调用。IBM MQ
org.ops4j.pax.jms.ibmmq.MQConnectionFactoryFactory处理
com.ibm.mq.jms.MQConnectionFactory或com.ibm.mq.jms.MQXAConnectionFactory的 bean 属性。
7.3. 使用 JMS 控制台命令 复制链接链接已复制到粘贴板!
Apache Karaf 提供 jms 功能,它包括 jms:* 范围内的 shell 命令。您已看到一些使用这些命令检查手动配置的连接工厂的示例。另外,还有一些命令隐藏了创建配置管理员配置的需求。
从全新的 Fuse 实例开始,您可以注册特定于代理的连接工厂。以下列表显示了从 Karaf 安装 jms 功能,以及从 pax-jms -jms-jms 安装 pax-jms-artemis 的 jms 功能:
下表显示了如何创建和检查 Artemis 连接工厂:
正如您所见,将为您创建 org.ops4j.connectionfactory factory PID。但是,它不会自动存储在 ${karaf.etc} 中,可以使用 config:update。无法指定其他属性,但您可以在稍后添加它们。
7.4. 使用加密配置值 复制链接链接已复制到粘贴板!
与 pax-jdbc-config 捆绑包一样,您可以使用 Jasypt 来加密属性。
如果有任何 org.jasypt.encryption.StringEncryptor 服务在任何 alias service 属性中注册,您可以在 连接工厂 PID 中引用它,并使用加密的密码。以下是一个示例:
用于查找解密器服务的服务过滤器是 (& (objectClass=org.jasypt.encryption.StringEncryptor) (alias=<alias>),其中 < alias > 是连接工厂配置 工厂 PID 的 decryptor 属性的值。
7.5. 使用 JMS 连接池 复制链接链接已复制到粘贴板!
本节讨论 JMS 连接/会话池选项。少于 JDBC 的选择数量要少。这些信息被组织到以下主题:
要使用 XA 恢复,您应该使用 pax-jms-pool-transx 或 pax-jms-pool-narayana 连接池模块。
7.5.1. 使用 JMS 连接池简介 复制链接链接已复制到粘贴板!
目前,您已注册了一个特定于代理的 连接工厂。由于 连接工厂 本身是连接工厂的工厂,org.ops4j.pax.jms.service.ConnectionFactoryFactory 服务可能会被视为 meta factory。它应该能够生成两种类型的连接工厂:
-
javax.jms.ConnectionFactory -
javax.jms.XAConnectionFactory
pax-jms-pool busybox 捆绑包与 org.ops4j.pax.jms.service.ConnectionFactoryFactory 服务平稳工作。这些捆绑包提供了 org.ops4j.pax.jms.service.PooledConnectionFactoryFactory 的实现,它们可用于使用一组属性和原始 org.ops4j.pax.jms.service.ConnectionFactoryFactory 来创建池连接工厂。例如:
public interface PooledConnectionFactoryFactory {
ConnectionFactory create(ConnectionFactoryFactory cff, Map<String, Object> props);
}
public interface PooledConnectionFactoryFactory {
ConnectionFactory create(ConnectionFactoryFactory cff, Map<String, Object> props);
}
下表显示了哪些捆绑包注册池连接工厂。在表中,o.o.p.j.p 代表 org.ops4j.pax.jms.pool。
| 捆绑包(Bundle) | PooledConnectionFactoryFactory | 池密钥 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
pax-jms-pool-narayana 工厂名为 PooledJms (XA) PooledConnectionFactoryFactory,因为它基于 pooled-jms 库。它为 XA 恢复添加与 Narayana 事务管理器的集成。
以上捆绑包只安装连接工厂。不安装连接工厂的捆绑包。因此,需要一些操作来调用 javax.jms.ConnectionFactory org.ops4j.pax.jms.service.PooledConnectionFactoryFactory.create () 方法。
7.5.2. 使用 pax-jms-pool-pooledjms 连接池模块 复制链接链接已复制到粘贴板!
了解如何使用 pax-jms-pool-pooledjms 捆绑包可帮助您仅使用 pax-jms-pool-pooledjms 捆绑包,以及 pax-jms-pool-pool-pool -pool-narayna 捆绑包,几乎执行几乎所有操作为 pax-jms-pool-pooledjms。
pax-jms-config 捆绑包跟踪以下内容:
-
org.ops4j.pax.jms.service.ConnectionFactoryFactoryservices -
org.ops4j.connectionfactoryfactory PIDs -
org.ops4j.pax.jms.service.PooledConnectionFactoryFactory的实例由pax-jms-pool114 捆绑包之一注册。
如果 工厂配置包含 池 属性,则由 pax-jms-config 捆绑包注册的最终连接工厂是特定于代理的连接工厂。如果 pool=pooledjms,则连接工厂将嵌套在以下之一:
-
org.messaginghub.pooled.jms.JmsPoolConnectionFactory(xa=false) -
org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory(xa=true)
除了 pool 属性(以及布尔值 xa 属性外,它选择其中一个非xa/xa 连接工厂),org.ops4j 的属性。
.connectionfactory factory PID 可能会包含前缀为 pool
对于 pooled-jms 库,将使用这些前缀属性(在删除前缀后)来配置实例:
-
org.messaginghub.pooled.jms.JmsPoolConnectionFactory, 或 -
org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory
以下列表是 pooled-jms 池(org.ops4j.connectionfactory-artemis factory PID)的真实配置,它使用带有 jms.-prefixed 属性的便捷语法:
在上述配置中,池和 xa 键是 提示 (服务过滤器属性)来选择一个注册的 org.ops4j.pax.jms.service.PooledConnectionFactoryFactory 服务。对于 pooled-jms 库,它是:
以下是创建和配置连接池的步骤的完整示例:
安装所需的功能:
karaf@root()> feature:install -v pax-jms-pool-pooledjms pax-jms-artemis Adding features: pax-jms-pool-pooledjms/[1.0.0,1.0.0] ...
karaf@root()> feature:install -v pax-jms-pool-pooledjms pax-jms-artemis Adding features: pax-jms-pool-pooledjms/[1.0.0,1.0.0] ...Copy to Clipboard Copied! Toggle word wrap Toggle overflow 安装
jms功能:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 创建 工厂配置 :
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 检查
pax-jms-config是否处理到javax.jms.ConnectionFactory服务中的配置:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 使用连接工厂:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
7.5.3. 使用 pax-jms-pool-narayana 连接池模块 复制链接链接已复制到粘贴板!
pax-jms-pool-narayna 模块几乎执行所有操作为 pax-jms-pool-pooledjms。它安装特定于池的 org.ops4j.pax.jms.service.PooledConnectionFactoryFactory,适用于 XA 和非 XA 场景。唯一的 区别是在 XA 场景中,有一个额外的集成点。org.jboss.tm.XAResourceRecovery OSGi 服务被注册为由 com.arjuna.ats.arjuna.recovery.RecoveryManager 获取。
7.5.4. 使用 pax-jms-pool-transx 连接池模块 复制链接链接已复制到粘贴板!
pax-jms-pool-transx 模块提供了 org.ops4j.pax.jms.service.PooledConnectionFactoryFactory 服务的实现,它基于 pax-transx-jms 捆绑包。pax-transx-jms 捆绑包使用 org.ops4j.pax.transx.jms.ManagedConnectionFactoryBuilder 工具来创建 javax.jms.ConnectionFactory 池。这是 第 8.3 节 “关于 pax-transx 项目” 中讨论的 JCA (Java™ Connector Architecture)解决方案。
7.6. 以工件的形式部署连接工厂 复制链接链接已复制到粘贴板!
本主题讨论了真实建议。
在 部署方法 中,javax.jms.ConnectionFactory 服务由应用代码直接注册。通常,此代码位于 Blueprint 容器中。蓝图 XML 可以是普通 OSGi 捆绑包的一部分,可通过使用 mvn: URI 进行安装,并存储在 Maven 存储库(本地或远程)。与 Configuration Admin 配置相比,版本控制(如捆绑包)更容易。
pax-jms-config 版本 1.0.0 捆绑包为 连接工厂 配置添加部署方法。应用程序开发人员注册 javax.jms. (XA) ConnectionFactory 服务(通常使用 Bluerpint XML)并指定服务属性。然后 pax-jms-config 检测到已注册的、特定于代理的连接工厂,以及(使用服务属性)将服务嵌套在通用的、特定于代理的连接池中。
以下是使用 Blueprint XML 的三种 部署方法 :
7.6.1. 手动部署连接工厂 复制链接链接已复制到粘贴板!
在此方法中,不需要 pax-jms-config 捆绑包。应用程序代码负责注册特定于代理和通用连接池。
以下是显示如何使用它的 shell 命令:
如上方列表所示,Blueprint 捆绑包导出 javax.jms.ConnectionFactory 服务,它是一个通用的、非特定于代理的连接池。特定于代理的 javax.jms.XAConnectionFactory 没有 作为 OSGi 服务注册,因为 Blueprint XML 没有明确的 < service ref="artemis"> 声明。
7.6.2. 连接工厂部署 复制链接链接已复制到粘贴板!
此方法演示了以规范的方式使用 pax-jms-config。这与推荐用于 Fuse 6.x 的方法有点不同,其要求是将池配置指定为服务属性。
以下是 Blueprint XML 示例:
上例使用 factory beans,通过使用连接工厂(…)创建连接工厂。不需要明确引用 javax.transaction.TransactionManager 服务,因为这由 XA 感知的池 ConnectionFactoryFactory 在内部跟踪。
以下是在 Fuse/Karaf shell 中查找它的方式:
如上方列表所示,Blueprint 捆绑包导出 javax.jms.ConnectionFactory 服务,它是一个通用的、非特定于代理的连接池。特定于代理的 javax.jms.XAConnectionFactory 没有 作为 OSGi 服务注册,因为 Blueprint XML 没有明确的 < service ref="artemis"> 声明。
7.6.3. 连接工厂的混合部署 复制链接链接已复制到粘贴板!
pax-jms-config 1.0.0 捆绑包使用服务属性在池连接工厂中添加其他特定于代理的连接工厂。这个方法与用于在 Fuse 6.x 中工作的方式匹配。
以下是 Blueprint XML 示例:
在上例中,您可以看到只手动注册特定于代理的连接工厂。pool=pooledjms 服务属性是连接工厂跟踪器的提示,它由 pax-jms-config 捆绑包管理。使用这个服务属性的连接工厂服务嵌套在池连接工厂中,在本例中为 pax-jms-pool-pooledjms。
以下是在 Fuse/Karaf shell 中查找它的方式:
在上例中,jms:connectionfactories 仅显示一个服务,因为此命令会删除重复的名称。两个服务由数据源混合部署中的 jdbc:ds-list 出示。
javax.jms.XAConnectionFactory 从 Blueprint 捆绑包注册,它声明了 pool = pooledjms 属性。
javax.jms.ConnectionFactory 从 pax-jms-config 捆绑包中注册,并:
-
它没有
pool = pooledjms属性。在注册 wrapper 连接工厂时,它已被删除。 -
它具有
service.ranking = 1000属性,因此当它始终是首选的版本,例如,根据名称查找连接工厂。 -
它具有
pax.jms.managed = true属性,因此不会尝试再次打包它。 -
它具有
pax.jms.service.id.ref = 349属性,它指示嵌套在连接池中的原始连接工厂服务。
第 8 章 关于 Java 连接器架构 复制链接链接已复制到粘贴板!
创建 JCA 规格是为了(有其他事项) 一般是 拥有这三位参与者的场景:
- 一个外部系统,如数据库或 通常是 EIS 系统
- developer1 应用服务器
- 部署的应用程序
8.1. 简单的 JDBC 模拟 复制链接链接已复制到粘贴板!
在最简单的场景中,只有应用程序和数据库,您有:
添加公开 javax.sql.DataSource 的应用服务器,您有以下几项(无需重新调用 XA 等数据源的不同方面):
8.2. 使用 JCA 概述 复制链接链接已复制到粘贴板!
JCA 常规化 数据库驱动程序 的概念,方法是在驱动程序和应用服务器 之间添加 双向通信。驱动程序成为由 javax. resource.spi.ResourceAdapter 表示的资源适配器。
有两个重要接口:
-
javax.resource.spi.ManagedConnectionFactory由资源适配器实施。 -
javax.resource.spi.ConnectionManager由应用服务器实现。
ManagedConnectionFactory 接口有两个目的:
Object createConnectionFactory (ConnectionManager cxManager)方法可用于为给定 EIS (或数据库或消息代理)生成 连接工厂,供应用程序代码使用。返回的对象可以是:-
通用
javax.resource.cci.ConnectionFactory(此处未描述,请参阅 JCA 1.6,第 17 章: 常见客户端接口) -
EIS 特定的连接工厂,如知名
javax.sql.DataSource或javax.jms.ConnectionFactory。这是由 pax-transx-jdbc 和捆绑包使用的 连接工厂 类型。pax-transx-jms
-
通用
-
javax.resource.spi.ManagedConnection ManagedConnectionFactory.createManagedConnection ()方法由 应用服务器 使用,创建与 EIS/database/broker 的实际物理连接。
ConnectionManager 由 应用服务器 实现,并由资源适配器 使用。它是首先执行 QoS 操作(池、安全性、事务管理),最后委托资源适配器的 ManagedConnectionFactory 来创建 ManagedConnection 实例的 应用服务器。流程类似如下:
-
应用程序代码使用从
ManagedConnectionFactory.createConnectionFactory ()返回的对象创建并公开的 连接工厂。它可以是通用的 CCI 接口,如javax.sql.DataSource。 -
此 连接工厂 不自行创建 连接,而是委托给
ConnectionManager.allocateConnection ()传递 资源适配器- 特定的ManagedConnectionFactory -
由 应用服务器 实现的
ConnectionManager创建支持 对象、管理事务、池等,最终从传递ManagedConnectionFactory获取 物理(管理)连接。 - 应用程序代码获得 连接,通常是 应用服务器 创建的 wrapper/proxy,最终委托给 资源适配器 的特定 物理连接。
下图中,应用服务器 创建了特定于 EIS 的非CCI 连接工厂。只需 - 访问 EIS (这里:数据库)是使用 javax.sql.DataSource 接口完成的,驱动程序的任务是提供 物理连接,而 应用服务器 则将其嵌套在(通常)执行池/备份的代理内(通常)
8.3. 关于 pax-transx 项目 复制链接链接已复制到粘贴板!
pax-transx 项目在 OSGi 中提供对 JTA/JTS 事务管理的支持,以及 JDBC 和 JMS 的资源池。它将关闭 pax-jdbc 和 pax-jms 之间的差距。
-
Pax-jdbc为javax.sql. (XA) ConnectionFactory服务添加配置选项和发现,并附带一些 JDBC 池实施 -
Pax-jms对javax.jms. (XA) ConnectionFactory服务执行相同的操作,并提供了一些 JMS 池实施 -
Pax-transx为 javax.transaction.TransactionManager 实施和(最终)为javax.transaction.TransactionManager实现添加配置选项和发现,提供基于 JCA 的 JDBC/JMS 连接管理,支持池和交易器。
关于 JDBC 连接池 和 JMS 连接池 的部分仍然有效。使用基于 JCA 的池的唯一更改是在注册 JDBC 数据源和 JMS 连接工厂时使用 pool=transx 属性。
-
Pax-jdbc-pool-transx使用org.ops4j.pax.transx.jdbc.ManagedDataSourceBuilderfrompax-transx-jdbc -
Pax-jms-pool-transx使用org.ops4j.pax.transx.jms.ManagedConnectionFactoryBuilderfrompax-transx-jms
虽然池化数据源/连接工厂是以 构建器样式 创建的(没有 Java™ bean 属性),但 JDBC 支持这些属性:
-
name -
userName -
password -
commitBeforeAutocommit -
preparedStatementCacheSize -
transactionIsolationLevel -
minIdle -
maxPoolSize -
aliveBypassWindow -
houseKeepingPeriod -
connectionTimeout -
idleTimeout -
maxLifetime
JMS 支持这些属性:
-
name -
userName -
password -
clientID -
minIdle -
maxPoolSize -
aliveBypassWindow -
houseKeepingPeriod -
connectionTimeout -
idleTimeout -
maxLifetime
XA 恢复需要 用户名和密码 属性才能工作(就像使用 aries.xa.username 和 aries.xa.password 属性)在 Fuse 6.x 中。
在蓝图中使用此 JDBC 配置(mind pool=transx):
使用蓝图中的这个 JMS 配置(mind pool=transx):
您有一个 JDBC 数据源和 JMS 连接工厂,该工厂利用基于 JCA 的资源管理。基于 transx 的池可以正确地与 pax-transx-tm-narayana 与 XA 恢复有关。
所需功能有:
-
pax-jdbc-pool-tranx -
pax-jms-pool-tranx -
pax-transx-jdbc -
pax-transx-jms -
Pax-jms-artemis(使用 A-MQ 7 时)
第 9 章 编写使用事务的 Camel 应用程序 复制链接链接已复制到粘贴板!
在配置了三个、可用的引用类型后,您可以编写一个应用程序。三种类型的服务有:
一个事务管理器,它是以下接口之一:
-
javax.transaction.UserTransaction -
javax.transaction.TransactionManager -
org.springframework.transaction.PlatformTransactionManager
-
-
至少有一个 JDBC 数据源实施
javax.sql.DataSource. 接口。通常,有多个数据源。 -
至少一个实施
javax.jms.ConnectionFactory接口的 JMS 连接工厂。通常,有多个。
这部分论述了与管理事务、数据源和连接工厂相关的特定于 Camel 的配置。
本节论述了几个与 Spring 相关的概念,如 SpringTransactionPolicy。Spring XML DSL 和 Blueprint XML DSL 之间存在明显区别,它们是定义 Camel 上下文的 XML 语言。Spring XML DSL 现在在 Fuse 中被弃用。但是,Camel 事务机制仍然在内部使用 Spring 库。
这里的大部分信息都不依赖于所使用的 PlatformTransactionManager 类型。如果 PlatformTransactionManager 是 Narayana 事务管理器,则使用完整的 JTA 事务。如果 PlatformTransactionManager 定义为本地 Blueprint < bean>,例如 org.springframework.jms.connection.JmsTransactionManager,则使用本地事务。
事务处理指的是启动、提交和回滚事务的步骤。本节介绍通过编程和配置控制事务处理的机制。
9.1. 通过标记路由进行事务处理 复制链接链接已复制到粘贴板!
Apache Camel 提供了在路由中启动事务的简单机制。在 Java DSL 中插入 transacted () 命令,或者在 XML DSL 中插入 <transacted /> 标签。
图 9.1. 通过标记路由来划分
翻译处理器取消处理,如下所示:
- 当交换进入转换处理器时,转换的处理器调用默认事务管理器来开始事务,并将事务附加到当前线程。
- 当交换到达剩余的路由结束时,转换的处理器调用事务管理器来提交当前的事务。
9.1.1. 使用 JDBC 资源的路由示例 复制链接链接已复制到粘贴板!
图 9.1 “通过标记路由来划分” 显示通过向路由中添加 transacted () DSL 命令进行事务的路由示例。遵循 transacted () 节点的所有路由节点都包含在事务范围内。在本例中,以下两个节点访问 JDBC 资源:
9.1.2. Java DSL 中的路由定义 复制链接链接已复制到粘贴板!
以下 Java DSL 示例演示了如何通过使用 transacted () DSL 命令标记路由来定义事务路由:
在本例中,file 端点读取一些 XML 格式文件,这些文件描述了资金从一个帐户传输到另一个帐户的传输。第一个 bean () 调用将指定资金总和计入 beneficiary 帐户,然后第二个 bean () 调用从发送者的帐户中减去指定资金总和。两个 bean () 调用都会导致对数据库资源的更新。假设数据库资源通过事务管理器绑定到事务,例如: 第 6 章 使用 JDBC 数据源。
9.1.3. Blueprint XML 中的路由定义 复制链接链接已复制到粘贴板!
前面的路由也可以在 Blueprint XML 中表示。& lt;transacted /> 标签将路由标记为事务,如以下 XML 所示:
9.1.4. 默认事务管理器和转换的策略 复制链接链接已复制到粘贴板!
为分离事务,转换的处理器必须与特定的事务管理器实例关联。要保存您必须在每次调用 transacted () 时都指定事务管理器,转换的处理器会自动选择一个可靠的默认值。例如,如果您的配置中只有一个事务管理器实例,则转换的处理器隐式选择此事务管理器,并使用它来处理事务。
转换器的处理器也可以配置有 transacted 策略,该策略是 TransactedPolicy 类型,它封装了传播策略和事务管理器(详情请参阅 第 9.4 节 “事务传播策略” )。以下规则用于选择默认的事务管理器或事务策略:
如果只有一个 bean 是
org.apache.camel.spi.TransactedPolicy类型,则使用此 bean。注意TransactedPolicy类型是SpringTransactionPolicy类型的基本类型,如 第 9.4 节 “事务传播策略” 中所述。因此,这里引用的 bean 可以是SpringTransactionPolicybean。-
如果存在类型为
org.apache.camel.spi.TransactedPolicy的 bean,其ID为 ,PROPAGATION_REQUIRED,则使用此 bean。 -
如果只有一个 bean of
org.springframework.transaction.PlatformTransactionManager类型,则使用此 bean。
您还可以通过向 transacted () 提供 bean ID 作为参数来显式指定 bean。请参阅 第 9.4.4 节 “Java DSL 中带有 PROPAGATION_NEVER 策略的示例路由”。
9.1.5. 事务范围 复制链接链接已复制到粘贴板!
如果您将转换的处理器插入到路由中,则事务管理器每次通过此节点时都会创建一个新的事务。事务的范围定义如下:
- 事务仅与当前线程关联。
- 事务范围包括所有遵循转换处理器的路由节点。
任何在 transacted 处理器之前的路由节点都不在事务中。但是,如果路由以事务端点开头,则路由中的所有节点都位于事务中。请参阅 第 9.2.5 节 “路由开始时的事务端点”。
考虑以下路由:它不正确,因为 transacted () DSL 命令错误地出现在访问数据库资源的 first bean () 调用后:
9.1.6. 事务路由中没有线程池 复制链接链接已复制到粘贴板!
务必要了解,给定的事务只与当前线程关联。您不能在事务路由中间创建线程池,因为新线程中的处理不会参与当前的事务。例如,以下路由被绑定到导致问题:
由于 threads () DSL 命令与 transacted 路由不兼容,如前面的路由是一定的破坏。即使 threads () 调用在 transacted () 调用前面,路由也不会按预期工作。
9.1.7. 将路由拆分为片段 复制链接链接已复制到粘贴板!
如果您想要将路由拆分为片段,并且每个路由片段都参与当前事务,您可以使用 direct: 端点。例如,要将交换发送到单独的路由片段,具体取决于传输量大(不等于 100)还是小(不等于 100),您可以使用 choice () DSL 命令和直接端点,如下所示:
以 direct:txbig 和以 direct:txsmall 开头的片段都参与当前的事务,因为直接端点是同步的。这意味着,片段在与第一个路由片段相同的线程中执行,因此它们包含在相同的事务范围内。
您不能使用 seda 端点来加入路由片段。seda 消费者端点创建一个新的线程(或线程),以执行路由片段(异步处理)。因此,片段不会参与原始的事务。
9.1.8. 资源端点 复制链接链接已复制到粘贴板!
当 Apache Camel 组件显示为路由的目标时,以下 Apache Camel 组件充当资源端点,例如,如果它们出现在 to () DSL 命令中。也就是说,这些端点可以访问事务资源,如数据库或持久队列。资源端点可以参与当前的事务,只要它们与启动当前事务的转换处理器相同的事务管理器关联。
- ActiveMQ
- AMQP
- 休眠
- iBatis
- JavaSpace
- JBI
- JCR
- JDBC
- JMS
- JPA
- LDAP
9.1.9. 使用资源端点的示例路由 复制链接链接已复制到粘贴板!
以下示例显示了具有资源端点的路由。这将向两个不同的 JMS 队列发送资金传输订单。信用 队列处理了接收方帐户的顺序。解封 队列处理以取消发送者帐户的顺序。只有存在对应的 debit 时才应有学分。因此,您要将 enqueueing 操作放在单个事务中。如果事务成功,则信用订购和下序都将排队。如果发生错误,则既不会排队任何顺序。
from("file:src/data?noop=true")
.transacted()
.to("jmstx:queue:credits")
.to("jmstx:queue:debits");
from("file:src/data?noop=true")
.transacted()
.to("jmstx:queue:credits")
.to("jmstx:queue:debits");
9.2. 按事务端点划分 复制链接链接已复制到粘贴板!
如果路由开始时的消费者端点访问资源,则 transacted () 命令不使用,因为它在交换轮询后启动事务。换句话说,事务开始太晚,以在事务范围内包括消费者端点。在这种情况下,正确的方法是使端点本身负责启动事务。能够管理事务的端点被称为 事务端点。
事务端点有两种不同的模型,如下所示:
常规情况 - 通常,事务端点取消处理事务,如下所示:
- 当交换到达端点时,或者端点成功轮询交换时,端点调用其关联的事务管理器以开始事务。
- 端点会将新事务附加到当前线程。
- 当交换到达路由结束时,事务端点调用事务管理器来提交当前的事务。
- 带有 InOut 交换的 JMS 端点 - 当 JMS 消费者端点收到 InOut 交换,并且此交换路由到另一个 JMS 端点,这必须被视为特殊情况。问题是,如果您尝试在单个事务中包括整个请求/回复交换,则路由可能会死锁。
9.2.1. 带有 JMS 端点的路由示例 复制链接链接已复制到粘贴板!
第 9.2 节 “按事务端点划分” 显示通过路由开始时存在事务端点(在 from () 命令中)存在的路由示例。所有路由节点都包含在事务范围内。在本例中,路由中的所有端点访问 JMS 资源。
9.2.2. Java DSL 中的路由定义 复制链接链接已复制到粘贴板!
以下 Java DSL 示例演示了如何通过启动使用事务端点的路由来定义事务路由:
from("jmstx:queue:giro")
.to("jmstx:queue:credits")
.to("jmstx:queue:debits");
from("jmstx:queue:giro")
.to("jmstx:queue:credits")
.to("jmstx:queue:debits");
在上例中,事务范围包含端点 jmstx:queue:giro,jmstx:queue:credits, 和 jmstx:queue:debits。如果事务成功,则会从上级队列永久删除交换,并推送到信用 队列和 去位 队列。如果交易失败,交换不会放入信用队列,并且将交换推送回巨队列。 默认情况下,JMS 会自动尝试更新消息。JMS 组件 bean jmstx 必须明确配置为使用事务,如下所示:
在上例中,jmsTransactionManager 的事务管理器实例 与 JMS 组件关联,transacted 属性设为 true,以启用 InOnly Exchanges 的事务处理。
9.2.3. Blueprint XML 中的路由定义 复制链接链接已复制到粘贴板!
前面的路由可以在 Blueprint XML 中表示,如下所示:
9.2.4. 不需要 DSL transacted () 命令 复制链接链接已复制到粘贴板!
在以事务端点开头的路由中不需要 transacted () DSL 命令。但是,假设默认事务策略是 PROPAGATION_REQUIRED (请参阅 第 9.4 节 “事务传播策略”),通常不会损害包含 transacted () 命令,如下例所示:
from("jmstx:queue:giro")
.transacted()
.to("jmstx:queue:credits")
.to("jmstx:queue:debits");
from("jmstx:queue:giro")
.transacted()
.to("jmstx:queue:credits")
.to("jmstx:queue:debits");
但是,这个路由可能会以意外的方式的行为,例如,如果在 Blueprint XML 中创建了具有非默认传播策略的单一 TransactedPolicy bean。请参阅 第 9.1.4 节 “默认事务管理器和转换的策略”。因此,通常不要将 transacted () DSL 命令包含在以事务端点开头的路由中。
9.2.5. 路由开始时的事务端点 复制链接链接已复制到粘贴板!
以下 Apache Camel 组件在路由启动时显示为事务端点(例如,如果它们出现在 from () DSL 命令中)。也就是说,这些端点可以配置为充当事务客户端,它们也可以访问事务资源。
- ActiveMQ
- AMQP
- JavaSpace
- JMS
- JPA
9.3. 声明事务的划分 复制链接链接已复制到粘贴板!
使用 Blueprint XML 时,您还可以通过在 Blueprint XML 文件中声明事务策略来分离事务。通过将适当的事务策略应用到 bean 或 bean 方法,例如 Required 策略,您可以确保在调用该特定 bean 或 bean 方法时启动事务。在 bean 方法的末尾,事务被提交。这个方法类似于事务在企业 Java Beans 中处理的方式。
OSGi 声明性事务允许您在 Blueprint 文件中的以下范围中定义事务策略:
另请参阅: 第 9.3.3 节 “tx:transaction 属性的描述”。
9.3.1. bean-level 声明 复制链接链接已复制到粘贴板!
要在 bean 级别上声明事务策略,请插入 tx:transaction 元素作为 bean 元素的子级,如下所示:
在前面的示例中,所需的事务策略应用于 accountFoo bean 的所有方法,以及 accountBar bean,其中 method 属性指定通配符 *,以匹配所有 bean 方法。
9.3.2. 顶级声明 复制链接链接已复制到粘贴板!
要在顶层声明事务策略,请插入 tx:transaction 元素作为 blueprint 元素的子,如下所示:
在上例中,Required 事务策略应用到 ID 与模式 匹配的每个 bean 的所有方法。
9.3.3. tx:transaction 属性的描述 复制链接链接已复制到粘贴板!
tx:transaction 元素支持以下属性:
Bean(仅限顶级)指定事务策略应用到的 bean ID (命名空间或空格)列表。例如:
<blueprint ...> <tx:transaction bean="accountFoo,accountBar" value="..." /> </blueprint><blueprint ...> <tx:transaction bean="accountFoo,accountBar" value="..." /> </blueprint>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 您还可以使用通配符字符
*,它在每个列表条目中最多可能会出现一次。例如:<blueprint ...> <tx:transaction bean="account*,jms*" value="..." /> </blueprint><blueprint ...> <tx:transaction bean="account*,jms*" value="..." /> </blueprint>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 如果省略 bean 属性,则默认为
*(匹配蓝图文件中所有非syntic Bean)。方法(顶级和 bean-level)指定事务策略应用到的方法名称列表(comma 或空格分开)。例如:
<bean id="accountFoo" class="org.jboss.fuse.example.Account"> <tx:transaction method="debit,credit,transfer" value="Required" /> <property name="accountName" value="Foo" /> </bean><bean id="accountFoo" class="org.jboss.fuse.example.Account"> <tx:transaction method="debit,credit,transfer" value="Required" /> <property name="accountName" value="Foo" /> </bean>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 您还可以使用通配符字符
*,它在每个列表条目中最多可能会出现一次。如果省略了 method 属性,则默认为
*(匹配适用 Bean 中的所有方法)。- value
(顶级和 bean-level)指定事务策略。策略值与 EJB 3.0 规格中定义的策略具有相同的语义,如下所示:
-
必需- 支持当前事务 ; 如果不存在,创建一个新事务。 -
必需- 支持当前事务;如果没有当前事务,则抛出异常。 -
RequiresNew- 创建新的事务,挂起当前事务(如果存在)。 -
支持- 支持当前事务;如果没有存在,则非事务执行。 -
NotSupported- 不支持当前事务,而是始终以非事务执行。 -
Never- 不支持当前事务 ; 如果当前事务存在,抛出异常。
-
9.4. 事务传播策略 复制链接链接已复制到粘贴板!
如果要影响事务客户端创建新事务的方式,您可以使用 JmsTransactionManager 并为它指定一个事务策略。特别是,Spring 事务策略允许您为事务指定传播行为。例如,如果事务客户端是创建新的事务,它检测到已与当前线程关联的事务,如果它继续并创建新的事务,请暂停旧的事务?或者应该让现有交易接管?通过指定事务策略上的传播行为,可以控制这些类型的行为。
事务策略在蓝图 XML 中作为 Bean 进行实例化。然后,您可以通过将其 bean ID 作为 transacted () DSL 命令的参数来引用事务策略。例如,如果要启动受行为的事务( PROPAGATION_REQUIRES_NEW ),您可以使用以下路由:
from("file:src/data?noop=true")
.transacted("PROPAGATION_REQUIRES_NEW")
.bean("accountService","credit")
.bean("accountService","debit")
.to("file:target/messages");
from("file:src/data?noop=true")
.transacted("PROPAGATION_REQUIRES_NEW")
.bean("accountService","credit")
.bean("accountService","debit")
.to("file:target/messages");
其中 PROPAGATION_REQUIRES_NEW 参数指定使用 PROPAGATION_REQUIRES_NEW 行为配置的事务策略 bean 的 bean ID。请参阅 第 9.4.3 节 “在蓝图 XML 中定义策略 Bean”。
9.4.1. 关于 Spring 事务策略 复制链接链接已复制到粘贴板!
Apache Camel 可让您使用 org.apache.camel.spring.spi.SpringTransactionPolicy 类来定义 Spring 事务策略,这基本上是一个围绕原生 Spring 类的打包程序。SpringTransactionPolicy 类封装了两部分数据:
-
对
PlatformTransactionManager类型的事务管理器的引用 - 传播行为
例如,您可以使用 PROPAGATION_MANDATORY 行为实例化 Spring 事务策略 bean,如下所示:
9.4.2. propagation 行为的描述 复制链接链接已复制到粘贴板!
Spring 支持以下传播行为。这些值最初在 JavaeEE 支持的传播行为上建模:
- PROPAGATION_MANDATORY
- 支持当前的事务。如果没有当前事务,则抛出异常。
- PROPAGATION_NESTED
如果存在当前的事务,请在嵌套的事务中执行,否则行为类似于
PROPAGATION_REQUIRED。注意所有事务管理器都不支持嵌套事务。
- PROPAGATION_NEVER
- 不支持当前的事务。如果当前事务存在,则抛出异常。
- PROPAGATION_NOT_SUPPORTED
不支持当前的事务。始终以非事务方式执行。
注意此策略需要暂停当前的事务,此功能不受所有事务管理器的支持。
- PROPAGATION_REQUIRED
- (默认)支持当前事务。如果不存在,则创建一个新名称。
- PROPAGATION_REQUIRES_NEW
创建新的事务,并暂停当前事务(如果存在)。
注意所有事务管理器都不支持挂起事务。
- PROPAGATION_SUPPORTS
- 支持当前的事务。如果不存在,则以非事务方式执行。
9.4.3. 在蓝图 XML 中定义策略 Bean 复制链接链接已复制到粘贴板!
以下示例演示了如何为所有支持的传播行为定义事务策略 Bean。为方便起见,每个 bean ID 与传播行为值的指定的值匹配,但实践中,您可以使用您要用于 bean ID 的任何值。
如果要将任何这些 bean 定义粘贴到您自己的蓝图 XML 配置中,请记得自定义对事务管理器的引用。也就是说,将对 txManager 的引用替换为您的事务管理器 bean 的实际 ID。
9.4.4. Java DSL 中带有 PROPAGATION_NEVER 策略的示例路由 复制链接链接已复制到粘贴板!
演示该事务策略对事务有一些影响的简单方式是将 PROPAGATION_NEVER 策略插入到现有事务的中间,如以下路由所示:
from("file:src/data?noop=true")
.transacted()
.bean("accountService","credit")
.transacted("PROPAGATION_NEVER")
.bean("accountService","debit");
from("file:src/data?noop=true")
.transacted()
.bean("accountService","credit")
.transacted("PROPAGATION_NEVER")
.bean("accountService","debit");
以这种方式使用,PROPAGATION_NEVER 策略可避免中止每个事务,从而导致回滚。您应该能轻松查看对应用的影响。
请记住,传递给 transacted () 的字符串值是一个 bean ID,而不是传播行为名称。在本例中,选择 bean ID 与传播行为名称相同,但这并非总是如此。例如,如果您的应用程序使用多个事务管理器,则最终可能会有多个具有特定传播行为的策略 Bean。在这种情况下,您无法在传播行为后简单地命名 Bean。
9.4.5. 蓝图 XML 中带有 PROPAGATION_NEVER 策略的路由示例 复制链接链接已复制到粘贴板!
前面的路由可以在 Blueprint XML 中定义,如下所示:
9.5. 错误处理和回滚 复制链接链接已复制到粘贴板!
虽然您可以在事务路由中使用标准 Apache Camel 错误处理技术,但了解例外和事务处理之间的交互非常重要。特别是,您需要考虑抛出异常通常会导致事务回滚。请参见以下主题:
9.5.1. 如何回滚事务 复制链接链接已复制到粘贴板!
您可以使用以下方法之一回滚事务:
9.5.1.1. 使用运行时例外来触发回滚 复制链接链接已复制到粘贴板!
回滚 Spring 事务的最常见方法是抛出运行时(未检查)异常。换句话说,例外是 java.lang.RuntimeException 的一个实例或子类。java.lang.Error 类型的 Java 错误也会触发事务回滚。另一方面,检查异常不会触发回滚。
下图总结了 Java 错误和异常对事务的影响,其中触发回滚的类是灰色的。
Spring 框架还提供 XML 注解系统,允许您指定应或不应触发回滚的例外。详情请参阅 Spring 参考指南中的"回滚"。
如果在事务中处理运行时异常,也就是说,在异常有机会被仔细处理事务时,事务会被回滚。详情请查看 第 9.5.2 节 “如何定义死信队列”。
9.5.1.2. 使用 rollback () DSL 命令 复制链接链接已复制到粘贴板!
如果要在 transacted 路由的中间触发回滚,您可以通过调用 rollback () DSL 命令进行此操作,它会抛出 org.apache.camel.RollbackExchangeException 异常。换句话说,rollback () 命令使用标准方法抛出运行时异常来触发回滚。
例如,假设您确定在帐户服务应用程序中应有关于资金传输大小的绝对限制。您可以使用以下示例中的代码超过 100 时触发回滚:
如果在前面的路由中触发回滚,它将在无限循环中捕获。这是因为 rollback () 抛出 RollbackExchangeException 异常会在路由开始时传播到 文件 端点。File 组件有一个内置的可靠性功能,可导致它重新发送抛出异常的任何交换。在重新发送课程后,交换仅触发另一个回滚,从而导致无限循环。下一个示例演示了如何避免这种无限循环。
9.5.1.3. 使用 markRollbackOnly () DSL 命令 复制链接链接已复制到粘贴板!
markRollbackOnly () DSL 命令允许您在不抛出异常的情况下强制当前事务回滚。当抛出异常具有不必要的副作用时,这很有用,比如 第 9.5.1.2 节 “使用 rollback () DSL 命令” 中的示例。
以下示例演示了如何使用 markRollbackOnly () 命令替换 rollback () 命令来修改 第 9.5.1.2 节 “使用 rollback () DSL 命令” 中的示例。此版本的路由解决了无限循环的问题。在这种情况下,当资金传输量超过 100 时,当前的交易将被回滚,但不会抛出异常。由于文件端点没有收到异常,所以它不会重试交换,因此失败的事务会被静默丢弃。
以下代码回滚异常,但 markRollbackOnly () 命令:
上述路由实施不是理想选择。虽然路由会完全回滚事务(以一致状态保存数据库),并避免无限循环的缺陷,但它不会保留失败事务的任何记录。在现实应用程序中,您通常希望跟踪任何失败的事务。例如,您可能希望向相关客户写入一个字母,以解释事务无法成功的原因。跟踪失败事务的便捷方法是向路由添加死信队列。
9.5.2. 如何定义死信队列 复制链接链接已复制到粘贴板!
要跟踪失败的事务,您可以定义一个 onException () 子句,该子句将相关的交换对象分解为死信队列。但是,当在事务上下文中使用时,您需要注意如何定义 onException () 子句,因为处理异常和事务处理之间的潜在交互。以下示例显示了定义 onException () 子句的正确方法,假设您需要抑制重新增长异常。
在前面的示例中,onException () 配置为捕获 IllegalArgumentException 异常,并将终止交换发送到死信文件 deadLetters.xml。当然,您可以更改此定义,以捕获应用程序出现的任何异常。改进行为和事务回滚行为的异常由 onException () 子句中的以下特殊设置控制:
-
handled (true)- 阻止重新增长异常。在这个特定示例中,rethrown 异常不可取,因为它会在重新传播到文件端点时触发无限循环。请参阅 第 9.5.1.3 节 “使用markRollbackOnly ()DSL 命令”。然而,在某些情况下,可能可以接受重新增长异常(例如,如果路由开始时的端点没有实现重试功能)。 -
markRollbackOnly ()- 在不抛出异常的情况下标记当前回滚的事务。请注意,在将交换路由到死信队列的to ()命令后插入此 DSL 命令非常重要。否则,交换永远不会到达死信队列,因为markRollbackOnly ()中断了处理链。
9.5.3. 捕获事务的例外 复制链接链接已复制到粘贴板!
处理事务路由中的异常不是使用 onException () 的简单方法是使用路由上的 doTry () 和 doCatch () 子句。例如,以下代码演示了如何捕获和处理事务路由中的 IllegalArgumentException,而无需在无限循环中捕获的风险。
在本例中,路由被分成两个片段。第一个片段(来自 file:src/data 端点)接收传入的交换,并使用 doTry () 和 doCatch () 执行异常处理。第二个片段(来自 direct:split 端点)执行所有事务处理。如果在这个事务片段中发生异常,它会首先将所有事务传播到 transacted () 命令,从而导致当前事务被回滚,然后由第一个路由段中的 doCatch () 子句捕获。doCatch () 子句不会重新增长异常,因此可以避免文件端点不会执行任何重试和无限循环。