使用 Oracle JDBC 驱动程序的 Spring XA 事务

发布于 2024-11-09 23:01:04 字数 3027 浏览 0 评论 0原文

我正在尝试使用 bitronix 事务管理器启用分布式事务。配置详细信息包括

  1. 使用 OCI JDBC 驱动程序和 oracle.jdbc.xa.client.OracleXADataSource。该数据源由 UCP 连接池数据源 - oracle.ucp.jdbc.PoolDataSourceImpl 包装,
  2. 使用 spring JdbcTemplate 执行查询。
  3. 使用 Bitronix 事务管理器处理分布式事务
  4. 使用注释的 Spring 声明式事务

我面临的问题是使用 JDBCTemplate 执行的查询没有在事务中执行。我的测试用例使用 JDBCTemplate 执行两个查询,并且当该方法在查询执行后引发运行时异常时,它们不会回滚。我还可以看到连接的自动提交状态设置为 true。

<tx:annotation-driven transaction-manager="distributedTransactionManager"/>

<bean id="distributedTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionManager" ref="bitronixTransactionManager"/>
    <property name="userTransaction" ref="bitronixTransactionManager"/>
    <property name="allowCustomIsolationLevels" value="true"/>
</bean>

<bean id="bitronixTransactionManager" factory-method="getTransactionManager"
      class="bitronix.tm.TransactionManagerServices" depends-on="bitronixConfiguration"
      destroy-method="shutdown">
</bean>

数据源创建如下

    PoolDataSourceImpl pds = new PoolDataSourceImpl();
    try {
        pds.setConnectionPoolName(dataSourceName);
        pds.setConnectionFactoryClassName("oracle.jdbc.xa.client.OracleXADataSource");
        pds.setConnectionFactoryProperties(getOracleDataSourceProperties());
        pds.setDataSourceName(dataSourceName);
        pds.setServerName("v-in-sd-tst-12");
        pds.setPortNumber(1521);
        pds.setUser("ForTestCasesAmit");
        pds.setPassword("adept");
        pds.setMinPoolSize(10);
        pds.setMaxPoolSize(100);
        pds.setMaxIdleTime(1800);

        pds.startPool();
    } catch (SQLException e) {
        throw new RuntimeException("Cannot create project datasource " + dataSourceName, e);
    }
    return pds;

对于查询不在事务中执行的原因有什么建议吗?

更新1

添加使用jdbcTemplate执行查询并在末尾抛出异常的方法

@Transactional(propagation = Propagation.REQUIRED)
public void execute() {
    System.out.println("Starting with the service method");
    jdbcTemplateForDSOne.update("UPDATE T1 SET COL1 = 'Date1' WHERE COL2 = 1");

    jdbcTemplateForDSOne.update("UPDATE T1 SET COL1 = 'Start Date1' WHERE COL2 = 2");

    waitForUserInput();
    throw new RuntimeException("Rollback Now");
}

更新2

Oracle JDBC 开发人员指南提到

“在 Oracle 数据库 10g 之前的所有版本中,从 XAConnection 获取的连接上的默认自动提交状态为 false。从 Oracle 数据库 10g 开始,默认状态为真的。”

我使用的是 Oracle 11g r2。知道使用分布式事务时应该进行哪些配置更改才能将自动提交状态设置为 false?

更新 3

如果我使用 bitronix 池数据源而不是 oracle ucp PoolDataSource,则事务可以正常工作。使用 bitronix PoolingDataSource 使 bitronix 有机会将自动提交状态设置为 false。将进行更多调查以找出两者之间的差异。

I am trying to enable distributed transactions using bitronix transaction manager. Configuration Details include

  1. Using OCI JDBC Driver and oracle.jdbc.xa.client.OracleXADataSource. This datasource is wrapped around by UCP connection pooling datasource - oracle.ucp.jdbc.PoolDataSourceImpl
  2. Using spring JdbcTemplate to execute queries.
  3. Using Bitronix Transaction Manager for handling distributed transactions
  4. Spring Declarative Transactions using Annotations

The issue I am facing is that the queries executed using the JDBCTemplate are not being executed in a transaction. My test case executes two queries using a JDBCTemplate and they do not rollback when the method throws a runtime exception after the query execution. I could also see the auto commit status of the connection is set to true.

<tx:annotation-driven transaction-manager="distributedTransactionManager"/>

<bean id="distributedTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionManager" ref="bitronixTransactionManager"/>
    <property name="userTransaction" ref="bitronixTransactionManager"/>
    <property name="allowCustomIsolationLevels" value="true"/>
</bean>

<bean id="bitronixTransactionManager" factory-method="getTransactionManager"
      class="bitronix.tm.TransactionManagerServices" depends-on="bitronixConfiguration"
      destroy-method="shutdown">
</bean>

The data source is created as follows

    PoolDataSourceImpl pds = new PoolDataSourceImpl();
    try {
        pds.setConnectionPoolName(dataSourceName);
        pds.setConnectionFactoryClassName("oracle.jdbc.xa.client.OracleXADataSource");
        pds.setConnectionFactoryProperties(getOracleDataSourceProperties());
        pds.setDataSourceName(dataSourceName);
        pds.setServerName("v-in-sd-tst-12");
        pds.setPortNumber(1521);
        pds.setUser("ForTestCasesAmit");
        pds.setPassword("adept");
        pds.setMinPoolSize(10);
        pds.setMaxPoolSize(100);
        pds.setMaxIdleTime(1800);

        pds.startPool();
    } catch (SQLException e) {
        throw new RuntimeException("Cannot create project datasource " + dataSourceName, e);
    }
    return pds;

Any suggestions on what could be the reason why the queries are not executed in a transaction?

Update 1

Adding the method which executes queries using jdbcTemplate and throws an exception at the end

@Transactional(propagation = Propagation.REQUIRED)
public void execute() {
    System.out.println("Starting with the service method");
    jdbcTemplateForDSOne.update("UPDATE T1 SET COL1 = 'Date1' WHERE COL2 = 1");

    jdbcTemplateForDSOne.update("UPDATE T1 SET COL1 = 'Start Date1' WHERE COL2 = 2");

    waitForUserInput();
    throw new RuntimeException("Rollback Now");
}

Update 2

Oracle JDBC Developer Guide mentions that

"The default auto-commit status on a connection obtained from XAConnection is false in all releases prior to Oracle Database 10g. Starting from Oracle Database 10g, the default status is true."

I am using Oracle 11g r2. Any idea what configuration changes should be done while using Distributed Transactions to have the auto commit status as false?

Update 3

The transactions work if I use the bitronix pooling data source instead of oracle ucp PoolDataSource. Using bitronix PoolingDataSource gave an opportunity to bitronix to set the autocommit status to false. Will investigate more to figure the difference between the two.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

暮年慕年 2024-11-16 23:01:04

您是否有包含此代码的方法,并用 @Transactional 进行注释或定义了任何可以告诉 Spring 在事务中执行此方法的方面?

Do you have the method, which contains this code, annotated with @Transactional or defined any Aspects which would tell Spring to execute this method in a transaction?

楠木可依 2024-11-16 23:01:04

据我所知,它们是在事务中执行的,但事务不是您所期望的。当 autoCommittrue 时,每个查询都成为一个事务。您必须正确配置连接以避免这种情况。

也就是说,关于 XA 的评论:XA 在网络问题和超时等极端情况下不起作用。也就是说,它的运行率为 99.9995%,但在一些关键情况下,它不会运行,而这些正是您所关心的情况。

确保当 XA 最终失败时,您的数据结构不会被损坏。

我建议评估允许再次运行事务的方法,而不是 XA。示例:您想要将一些记录从数据库 A 传输到 B。因此,您使用 FOR UPDATE 读取行,并且对于每个传输的行,将 processed 列设置为 true。

另一方面,您只添加尚未存在的行。

提交 B 中的交易后,删除 A 中其中processed = 'true'的行或以其他方式标记它们。

这意味着您可以根据需要经常运行它。

[编辑]

要禁用自动提交,您必须调用 con.setAutoCommit(false); 当然,问题是您正在使用 Spring,因此您从不明确在任何地方请求连接。

我的建议:扩展 PoolDataSourceImpl 并覆盖各种 get*Connection() 方法。在返回它们之前,将自动提交设置为 false。您还可以反编译 JDBC 驱动程序代码以查看 PoolDataSourceImpl 是否已经包含类似的内容,但显然它没有。

As far as I can see, they are executed in a transaction but the transaction isn't what you expect. When autoCommit is true, each query becomes a transaction. You have to configure the connection properly to avoid that.

That said, a comment about XA: XA doesn't work in corner cases like network problems and timeouts. That is, it will work 99.9995% times but in a few critical cases, it won't and those are the cases which you care for.

Make sure that your data structures don't get corrupted when XA eventually fails you.

Instead of XA, I suggest to evaluate methods that allow to run transactions again. Example: You want to transfer some records from database A to B. So you read the rows with FOR UPDATE and for each transmitted row, you set the processed column to true.

On the other side, you only add rows which aren't already present.

After the tx in B has been committed, delete the rows in A where processed = 'true' or mark them in another way.

That means you can run this as often as you need.

[EDIT]

To disable auto commit, you must call con.setAutoCommit(false); The problem is, of course, that you're using Spring, so you never explicitly ask for a connection anywhere.

My suggestion: Extend PoolDataSourceImpl and override the various get*Connection() methods. Before you return them, set auto commit to false. You could also decompile the JDBC driver code to see whether PoolDataSourceImpl already contains something like that but apparently it doesn't.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文