gpt4 book ai didi

sql-server - 是否可以在没有 XA 的事务中拥有两个 MSSQL 持久性单元?

转载 作者:行者123 更新时间:2023-12-02 10:24:02 24 4
gpt4 key购买 nike

我们有一个应用程序,它有许多实体类,必须有两个表。这些表是相同的,唯一的区别是名称。 SO 上提供的常见解决方案是使用继承(映射的父类(super class)和每类表策略)或具有不同映射的两个持久性单元。我们使用后一种解决方案,并且应用程序建立在这种方法之上,因此现在认为它是给定的。

有一些 EJB 方法将对两个持久性上下文进行更新,并且必须在一个事务中进行。两个持久性上下文具有相同的数据源,即到 Microsoft SQL Server 数据库(2012 版)的启用 XA 的连接。上下文之间的唯一区别是有一个映射 XML 来更改某些实体类的表名称,从而在这些表上工作。

架构负责人之一希望看到 XA 事务被消除,因为它们会导致数据库的大量开销,并且显然还会使执行的查询的日志记录和分析更加困难,还可能阻止一些准备好的语句缓存。我不知道所有细节,但对于许多应用程序,我们已经设法消除了 XA。然而,对于这个,我们目前不能,因为有两个持久性上下文。

在这种情况下,是否有某种方法可以在没有 XA 的情况下以事务方式更新两个上下文?如果是这样,如何?如果不是,是否有一些架构或配置更改可以使用一个持久性上下文而不必转向两个表的子类?

我知道这些问题:Is it possible to use more than one persistence unit in a transaction, without it being XA?
XA transaction for two phase commit

在投票将其作为重复关闭之前,请注意情况是 不同 .我们不像第一个问题那样处于只读状态,两个上下文都在同一个数据库上运行,我们只使用 MSSQL,我们使用的是 GlassFish,而不是 Weblogic。

最佳答案

经过一些试验,我发现实际上可以在容器管理的事务中拥有两个使用非 XA 资源的持久性单元。然而,它可能依赖于实现。 TL; DR 在底部。

如果多个资源参与一个事务,JTA 应该需要 XA 资源。它使用 X/Open XA 来允许分布式事务,例如跨多个数据库或数据库和 JMS 队列。显然有一些优化(它可能是 GlassFish 特定的,我不确定)允许最后一个参与者是非 XA。然而,在我的用例中,两个持久性单元都用于同一个数据库(但一组不同的表,可能有一些重叠)并且都不是 XA。这意味着我们希望在第二个资源不支持 XA 时抛出异常。

假设这是我们的persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="playground" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/playground</jta-data-source>
<properties>
<property name="hibernate.dialect" value="be.dkv.hibernate.SQLServer2012Dialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
<persistence-unit name="playground-copy" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/playground</jta-data-source>
<mapping-file>META-INF/orm-playground-copy.xml</mapping-file>
<properties>
<property name="hibernate.dialect" value="be.dkv.hibernate.SQLServer2012Dialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
</persistence>

有两个持久化单元,一个名为 playground ,另一个名字是 playground-copy .后者有一个 ORM 映射文件,但这不是重点。重要的是两者具有相同的 <jta-data-source>指定的。

在应用程序服务器(在本例中为 GlassFish)中,我们将有一个 JDBC 连接池,其中包含一个名为 playground 的 JDBC 资源。使用这个池。

connection pool and resource

现在,如果将两个持久性上下文注入(inject)到一个 EJB 中,并且调用了一个被认为是在容器管理的事务中的方法,那么您会期望事情看起来像这样。

persistence unit connections

两个持久化上下文都使用相同的数据源,但事务管理器和 JPA 层都不应该真正关心这一点。毕竟,它们可能具有不同的数据源。由于数据源无论如何都由连接池支持,因此您希望两个单元都获得自己的连接。 XA 将允许工作以事务方式运行,因为启用 XA 的资源将实现两阶段提交。

但是,当尝试将数据源指向具有非 XA 实现的连接池(并执行一些实际的持久性工作)时,没有异常(exception),一切正常! MSSQL 服务器中的 XA 支持甚至被禁用,尝试使用 XA 驱动程序会导致错误,直到启用它,所以这不是我在不知情的情况下意外使用 XA。

使用调试器进入代码显示,作为不同实体管理器的两个持久性上下文(它们应该如此)实际上使用相同的连接。一些进一步的挖掘表明连接没有设置为在 XA 事务中,并且在 JDBC 级别具有相同的事务标识符。于是情况变成了这样:

shared connection

如果为同一事务创建多个单元,我只能假设 JPA 提供程序具有使用相同连接的优化。那么,为什么会这样呢?在 JDBC 级别,事务是在连接上提交的。据我所知,JDBC 规范没有提供在单个连接上运行多个事务的方法。这意味着如果一个持久化上下文的工作被提交,那么另一个持久化上下文的提交也会发生。

但这实际上是 为什么有用。分布式事务的提交点应该像所有部分形成一个整体一样(假设在投票阶段都投了"is")。在这种情况下,两个持久性上下文都在同一个连接上运行,因此它们隐含地是一个工作单元。由于事务由容器管理,因此无论如何都无法立即访问它,这意味着您无法提交一个上下文而不是另一个上下文。并且只有一个连接实际注册到事务,它不必是 XA,因为从事务管理器的角度来看,它不被认为是分布式的。

请注意,这不会违反持久性上下文的局部性。从数据库中获取实体会在两种上下文中产生一个单独的对象。它们仍然可以彼此独立运行,就像它们使用单​​独的连接一样。在上图中,获取的具有相同主键的相同类型的实体代表相同的数据库行,但它们是由各自的实体管理器管理的单独对象。

为了验证这确实是 JPA 提供者的一些优化,我创建了第二个连接池(到同一个数据库)和一个单独的 JDBC 资源,将它设置为第二个持久性单元并进行测试。这会导致预期的异常:
Caused by: java.sql.SQLException: Error in allocating a connection. 
Cause: java.lang.IllegalStateException: Local transaction already has 1 non-XA Resource: cannot add more resources.

如果您创建了两个 JDBC 资源,但都指向同一个连接池,那么它仍然可以正常工作。这甚至在明确使用类 com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource 时也有效。 ,确认这可能是 JPA 级别的优化,而不是意外地为相同的数据源获得相同的连接两次(这会破坏 GlassFish 池)。使用 XA 数据源时,它确实是启用 XA 的连接,但 JPA 提供程序仍将使用相同的连接用于两个持久性上下文。仅当使用单独的池时,它实际上是两个完全独立的启用 XA 的连接,并且您将不再遇到上述异常。

那么,有什么收获呢?首先,我在 JPA 或 JTA 规范中没有发现任何描述(或强制)这种行为的内容。这意味着这可能是特定于实现的优化。移动到不同的 JPA 提供程序,甚至不同的版本,它可能不再有效。

其次,可能会出现死锁。如果您在两个上下文中都获取了上面示例中的实体,然后将其更改为一个并刷新,就可以了。在一个上下文中获取它,调用flush 方法,然后尝试在另一个上下文中获取它,这可能会导致死锁。如果您允许读取未提交的事务隔离,则可以避免这种情况,但是您在一个上下文中看到的内容将取决于您何时针对另一个上下文中的刷新获取它。所以手动刷新调用可能会很棘手。

作为引用,使用的 GlassFish 版本是 3.1.2.2 . JPA 提供程序是 Hibernate 版本 3.6.4.Final .

TL;博士

,您可以在 JavaEE 容器管理的事务中使用具有相同非 XA 资源的两个持久性上下文,并保留 ACID 属性。然而,这要归功于当使用相同的数据源为同一事务创建多个 EntityManager 时可能是 Hibernate 优化。由于 JPA 或 JTA 规范似乎没有强制要求,因此您可能不能跨 JPA 实现、版本或应用程序服务器依赖此行为。所以进行测试,不要期望完全可移植性。

关于sql-server - 是否可以在没有 XA 的事务中拥有两个 MSSQL 持久性单元?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41868714/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com