【问题标题】:Spring Committing Before Transaction Exits事务退出前的 Spring 提交
【发布时间】:2014-07-31 13:57:57
【问题描述】:

我们正在尝试从 Spring 2.5.2 升级到 4.0.5.RELEASE,但发现 Spring 的事务管理不再起作用。

在我们的生产应用程序中,所有数据库操作都通过一个标有 @Transactional 注释的 Spring bean(使用默认设置)。几年来,这一直按预期工作,如果在事务边界内抛出 RuntimeException,则会发生回滚。但是,当我们升级到 Spring 4.0.5.RELEASE 时,它的作用就像 autocommit 设置为 true。

我们通过查看堆栈跟踪验证了问题代码仍在事务代理中运行。但是当我们在进入事务之后对表执行简单的一行更新时,更新是committed。这令人费解,因为我们所做的只是更改 Spring 版本。

在后来的 Spring 版本中,自动提交行为是否发生了变化?在 4.05 下我们需要做一些额外的配置吗?


这是我们事务管理器的配置(在 2.52 和 4.05 版本中相同):

<tx:annotation-driven transaction-manager="transactionManager" />
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="efmsDataSource" />
</bean>

<bean id="efmsDataSource" class="com.uprr.eni.commons.dao.OracleDataSource">
    <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
    <property name="url" value="jdbc:oracle:thin:@${db_instance}.oracle.uprr.com:1521:${db_instance}" />
    <property name="username" value="${db_user}" />
    <property name="password" value="${db_password}" />
    <property name="maxActive" value="8" />
</bean>

这是在我们的代码中具有@Transactional 方法的bean:

<bean id="efmsExecutor" class="com.uprr.eni.commons.dao.exec.DataSourceExecutorBean">
    <property name="dataSource" ref="efmsDataSource" />
</bean>

以下是我们如何创建应用程序上下文并获取事务 bean,然后将其保存为静态变量:

context = new ClassPathXmlApplicationContext(CONFIG_FILES);
efmsExecutorBean = (DataSourceExecutorBean)context.getBean(EFMS_EXECUTOR_BEAN);

我们将该 bean 保存在一个静态变量中,并在我们执行事务时引用它。我们应该做些不同的事情吗?


当我们要开始一个事务时,我们调用:

efmsExecutorBean.executeTransaction(executor);

这是我们在 DataSourceExecutorBean 中调用以启动事务的方法。它基本上只是将其内部数据源传递给执行器(实际执行数据库工作的代码)。

@Transactional public void executeTransaction(final DataSourceExecutor executor) {

    executor.execute(source);                                   // Perform the unit of work
    final StringList errors = executor.getResult().getErrors(); // Did any errors occur?
    if (!errors.isEmpty()) {                                    // If so
        for (final String error : errors) {
            ApiLog.error(error);                                // Record them
        }
        throw new ExecutorError(ERROR, executor.getName());     // Abort
    }
}

当我重新检查两个版本的日志时,我注意到 4.0.5(损坏的)版本有一些 2.5.2 版本没有的条目。这些条目在事务开始时出现:

2014-07-31 09:34:43,576 [btpool0-0] 调试 - 创建单例 bean 'efmsDataSource' 的共享实例 2014-07-31 09:34:43,576 [btpool0-0] 调试 - 创建 bean 'efmsDataSource' 的实例 2014-07-31 09:34:43,576 [btpool0-0] 调试 - 急切缓存 bean 'efmsDataSource' 以允许解决潜在的循环引用

也许这可以解释发生了什么(但不能解释为什么)。看起来单例数据源 bean 并不是真正的单例。谷歌显示了一些有类似问题的人,但没有说明如何解决它。这对任何人都有影响吗?

【问题讨论】:

  • 看来事务有相同的行为,阅读documentation。可以把事务管理器的配置放上去吗?
  • 没有额外的配置或已知的事务支持中断。您是否只更改了 Spring 版本或也进行了其他更改?可以出示一下代码吗?
  • 新信息:通过 Spring 代码跟踪后,我们发现两个版本之间的行为存在差异。在损坏的(4.0.5)版本中,Spring 正在从应用程序上下文中创建一个新的事务管理器和新的数据源。因此,当它设置 autocommit 为 false 时,它​​设置的连接不同于我们的代码使用的连接

标签: spring transactions spring-transactions autocommit


【解决方案1】:

您的代码是否直接使用 Oracle 数据源(而不是通过例如 JdbcTemplate?)如果是这样,您需要将其包装在 TransactionAwareDataSourceProxy 中并将其注入您的数据访问 bean。

【讨论】:

  • 终于找到原因了:在初始化过程中,我们的应用在提取bean之后调用了ClassPathXmlApplicationContext.close()。这个 close() 调用是在几年前 Eclipse 发出关于潜在资源泄漏的警告时发出的,因为上下文没有关闭。在 Spring 2.5.2 中调用 close() 是无害的,但在更高版本中则不然。遗憾的是,到目前为止,Spring Javadocs 并没有警告调用 context.close() 可能带来的灾难性后果。感谢您花时间阅读我们冗长的帖子!
猜你喜欢
  • 1970-01-01
  • 2011-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-30
  • 2015-12-17
  • 2022-06-15
相关资源
最近更新 更多