我们正在尝试从 Spring 2.5.2 升级到 4.0.5.RELEASE,但发现 Spring 的事务管理不再起作用。
在我们的生产应用程序中,所有数据库操作都通过一个标有 @Transactional 注释(使用默认设置)的 Spring bean。几年来,这一直按预期工作,如果在事务边界内引发 RuntimeException,则会发生回滚。但是,当我们升级到 Spring 4.0.5.RELEASE 时,它的作用就像 autocommit 设置为 true。
通过查看堆栈跟踪,我们验证了问题代码仍在事务代理内部运行。但是当我们在进入事务后对表执行简单的一行更新时,更新是提交的。这令人费解,因为我们所做的只是更改 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 并不是真正的单例。谷歌显示了一些有类似问题的人,但没有说明如何解决它。这会和任何人一起敲响警钟吗?