为什么 iBATIS 会给出过时的结果,即使禁用了缓存?
Why is iBATIS giving stale results, even with caching disabled?
我有一个 Web 应用程序,我一直在用 Spring 数据慢慢地从 iBATIS 2 迁移到 JPA。
在大多数情况下,一切进展顺利,我一次只是为一个域对象迁移 DAO。但是,最近引起我注意的一个问题是网站的某些部分显示了过时的结果列表。
例如,我有一个 "ticket" 部分,它显示了未结工单列表,并允许您在单独的页面上查看特定工单。当我创建新工单时,我可以在其特定页面上正确查看该工单。但是,打开的工单列表似乎要过一段时间才会显示这张新工单。
我试图排除的事情:
- 即使在 MySQL 的查询缓存被禁用的系统上我也看到这个问题
- 即使我在 iBATIS 配置中设置了
cacheModelsEnabled="false"
,我也看到了这个问题。
- 即使我从我的 sqlMap 文件中完全删除了
<cacheModel>
元素和 cacheModel="x"
属性,我仍然看到这个问题。
- 只要我重新启动应用程序,我就会看到最新的结果。
- 当我在 MySQL 客户端中执行查询时,iBATIS 应该是 运行,我 do 看到 iBATIS 中缺少的新票证'结果。
- 当我使用 Spring MVC 和 Spring Data JPA 模拟一个简单的工单列表时,我 看到了新工单。
我也尝试用 iBATIS 排除某种奇怪的事务状态,但似乎这里根本没有使用任何事务。
我错过了什么?还有什么我应该想办法解决的吗?或者,我是否应该优先将 iBATIS 层完全替换为 Spring Data JPA,这似乎不受此问题的影响?
更新
我现在已经对 git bisect
进行了很多更改,并且我将其缩小到引入 Spring 的 org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
的更改。
因此,似乎某些事务的寿命比它应该的长。我将添加更多日志记录以查看是否可以确认这一点,然后寻找避免使用该过滤器的方法。
如果你在同一个SqlSession中做select、插入、select,
那么 SqlSession 缓存导致了这个问题。你将需要
插入后手动清除缓存:sqlSession.clearCache().
所以,这里似乎发生了一系列事情:
- 我的大部分代码都没有明确使用事务。
- 我在某些时候更改为使用 Tomcat's JDBC Connection Pool,默认情况下,当连接返回池时,它不会重置
autocommit
。不过,我希望我的旧版基于 DBCP 的东西会隐式地执行此操作。
OpenEntityManagerInViewFilter
的引入可能导致在某个时候调用 SET autocommit=0
,如果没有任何变化,则之后没有相应的 SET autocommit=1
。
- 偶然,或者也许是某种设计,将新记录插入数据库然后立即检索并显示它的代码似乎得到了与显示我的记录列表的代码不同的
Connection
.
REPEATABLE-READ
的默认 MySQL 事务隔离级别意味着我的列表显示的是旧结果。
我发现的修复方法(到目前为止似乎在我的测试中有效)是将这些 defaultAutoCommit
和 jdbcInterceptors
属性添加到我的连接池配置中:
<Resource name="jdbc/DB" auth="Container" type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
...
defaultAutoCommit="true" jdbcInterceptors="ConnectionState;StatementFinalizer" />
我有一个 Web 应用程序,我一直在用 Spring 数据慢慢地从 iBATIS 2 迁移到 JPA。
在大多数情况下,一切进展顺利,我一次只是为一个域对象迁移 DAO。但是,最近引起我注意的一个问题是网站的某些部分显示了过时的结果列表。
例如,我有一个 "ticket" 部分,它显示了未结工单列表,并允许您在单独的页面上查看特定工单。当我创建新工单时,我可以在其特定页面上正确查看该工单。但是,打开的工单列表似乎要过一段时间才会显示这张新工单。
我试图排除的事情:
- 即使在 MySQL 的查询缓存被禁用的系统上我也看到这个问题
- 即使我在 iBATIS 配置中设置了
cacheModelsEnabled="false"
,我也看到了这个问题。 - 即使我从我的 sqlMap 文件中完全删除了
<cacheModel>
元素和cacheModel="x"
属性,我仍然看到这个问题。 - 只要我重新启动应用程序,我就会看到最新的结果。
- 当我在 MySQL 客户端中执行查询时,iBATIS 应该是 运行,我 do 看到 iBATIS 中缺少的新票证'结果。
- 当我使用 Spring MVC 和 Spring Data JPA 模拟一个简单的工单列表时,我 看到了新工单。
我也尝试用 iBATIS 排除某种奇怪的事务状态,但似乎这里根本没有使用任何事务。
我错过了什么?还有什么我应该想办法解决的吗?或者,我是否应该优先将 iBATIS 层完全替换为 Spring Data JPA,这似乎不受此问题的影响?
更新
我现在已经对 git bisect
进行了很多更改,并且我将其缩小到引入 Spring 的 org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
的更改。
因此,似乎某些事务的寿命比它应该的长。我将添加更多日志记录以查看是否可以确认这一点,然后寻找避免使用该过滤器的方法。
如果你在同一个SqlSession中做select、插入、select, 那么 SqlSession 缓存导致了这个问题。你将需要 插入后手动清除缓存:sqlSession.clearCache().
所以,这里似乎发生了一系列事情:
- 我的大部分代码都没有明确使用事务。
- 我在某些时候更改为使用 Tomcat's JDBC Connection Pool,默认情况下,当连接返回池时,它不会重置
autocommit
。不过,我希望我的旧版基于 DBCP 的东西会隐式地执行此操作。 OpenEntityManagerInViewFilter
的引入可能导致在某个时候调用SET autocommit=0
,如果没有任何变化,则之后没有相应的SET autocommit=1
。- 偶然,或者也许是某种设计,将新记录插入数据库然后立即检索并显示它的代码似乎得到了与显示我的记录列表的代码不同的
Connection
. REPEATABLE-READ
的默认 MySQL 事务隔离级别意味着我的列表显示的是旧结果。
我发现的修复方法(到目前为止似乎在我的测试中有效)是将这些 defaultAutoCommit
和 jdbcInterceptors
属性添加到我的连接池配置中:
<Resource name="jdbc/DB" auth="Container" type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
...
defaultAutoCommit="true" jdbcInterceptors="ConnectionState;StatementFinalizer" />