Java Spring和Hibernate突然将事务设置为只读
我们有一个应用程序运行在JBoss4.2.3上,使用Spring2.5.2和Hibernate3.2.6.ga。它使用自己的用户在Linux JEE01 2.6.16.60-0.54.5-smp上运行。正在写入另一台计算机上的Oracle 10G数据库 我们使用的是标准视图->服务->dao分层。其中每个dao都用@Repository注释 这一切都是24/7运行,没有很多问题,但每隔几天,有时一天中有几次,整个系统就会进入一个糟糕的状态,无法再向数据库写入任何内容。这些堆栈跟踪显示在日志中:Java Spring和Hibernate突然将事务设置为只读,java,hibernate,spring,jboss,spring-transactions,Java,Hibernate,Spring,Jboss,Spring Transactions,我们有一个应用程序运行在JBoss4.2.3上,使用Spring2.5.2和Hibernate3.2.6.ga。它使用自己的用户在Linux JEE01 2.6.16.60-0.54.5-smp上运行。正在写入另一台计算机上的Oracle 10G数据库 我们使用的是标准视图->服务->dao分层。其中每个dao都用@Repository注释 这一切都是24/7运行,没有很多问题,但每隔几天,有时一天中有几次,整个系统就会进入一个糟糕的状态,无法再向数据库写入任何内容。这些堆栈跟踪显示在日志中:
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not
allowed in read-only mode (FlushMode.NEVER/MANUAL):
Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction
definition.
我们扫描了整个系统,在系统中有一个地方,flushmode临时设置为MANUAL,之后finally块将其恢复为原始值。这是因为我们不希望在此查询运行之前刷新数据库的状态。所以我们不能轻易改变这一点。正常刷新模式被设置为自动,在一些地方我们临时将其设置为提交,并再次将其切换回默认模式
只有重新启动服务器才能将系统恢复到正常工作状态
问题是:为什么系统将所有事务设置为只读/手动刷新模式?我在谷歌上搜索了一下,但找不到解决办法
这是我们的spring和hibernate配置(仅显示相关部分):
这一切都很好我猜是春天为你做的。我似乎记得这是由OpenSessionInViewFilter在春季完成的。您正在使用它吗?此异常来自Spring的HibernateTemplate类中的以下代码:
protected void checkWriteOperationAllowed(Session session) throws InvalidDataAccessApiUsageException {
if (isCheckWriteOperations() && getFlushMode() != FLUSH_EAGER &&
session.getFlushMode().lessThan(FlushMode.COMMIT)) {
throw new InvalidDataAccessApiUsageException(
"Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): "+
"Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.");
}
}
该检查的基本原理如下:
这是一个新的一致性检查
在Spring1.1中引入
调用HibernateTemplate的
在服务器上保存/更新/删除方法
中的Spring管理会话
FlushMode.NEVER可能是
危险:这意味着你:
- 要么是在春天里做这件事 只读事务,它将 永远不要刷新Hibernate会话, i、 永远不要冲洗你的脸 保存/更新/删除通话。新的 检查会让你意识到你不会 坚持你的改变 情况
- 或者和其他人一起工作 中的线程绑定会话 FlushMode.NEVER,例如 OpenSessionInViewFilter。如果你是 覆盖要刷新的关闭会话 在视图渲染之后,您应该 也覆盖getSession,设置 会话到FlushMode.AUTO
您的代码或Spring ORM中可能存在一些bug。要禁用此检查,您可以调用 这是从内存中提取的,所以不太详细。有两种设置刷新模式的方法。我认为是春天的方式和冬眠的方式。因为我们只使用了一次(Spring?)方式,而其他时间都使用Hibernate方式,所以不知何故,Hibernate的内部状态是错误的。这造成了我的问题。当我们使所有内容保持一致时,问题就消失了。因为您在实现中使用了执行切入点,并且使用了通用DAO,可能这并不像您预期的那样处理事务,这只是真正问题的一个表现
请确保Spring正在代理您的DAO,正如您在该调用中所期望的那样。您可能需要使用
target(beanid)
语法来为您的通用DAO启用正确的事务。因此,我检查您的配置。在spring上下文部分中,您使用
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*" read-only="true" propagation="REQUIRED" />
通常,只有一些访问是只读类型,如get/find/query等。我建议使用
<!--default is required transaction-->
<tx:method name="*"/>
另一件事是,您是否使用opensessioninview模式?可以在opensessioninview中正确设置刷新模式。您可以在web.xml中使用过滤器,也可以在应用程序上下文配置中使用spring拦截器。我在从1个bean使用2个“baseTransactionProxy”时遇到了这个问题:
第一:
<bean id="generalDao" parent="baseTransactionProxy">
<property name="target">
<bean class="com...GeneralDao" parent="baseDAO" />
</property>
</bean>
不确定是否有帮助,但在同一个代理调用中混合使用两个不同的“baseTransactionProxy”似乎不太好…在web.xml中放置:
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>flushMode</param-name>
<param-value>AUTO</param-value>
</init-param>
</filter>
openSessionInViewFilter
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
冲洗模式
自动的
请发布InvalidDataAccessApiUsageException的stacktrace
。我将在再次访问时发布。您好,我们已将服务器迁移到windows计算机(出于其他原因),但问题仍然存在。它仍然无法复制。我在问题中添加了一个例外。谢谢你提供的信息。我会尽快调查代码。我会再打给你。代码中的某个错误是否会导致所有线程和会话都处于“只读”模式?因为这就是我们目前看到的。我发现通过openSessionInView HibernateFilter获得的所有会话都设置为“从不”。似乎是最奇怪的违约选择。你在我的团队需要答案的四个小时前发布了这个消息。谢谢
<bean id="generalDao" parent="baseTransactionProxy">
<property name="target">
<bean class="com...GeneralDao" parent="baseDAO" />
</property>
</bean>
<bean id="ruleDao" parent="baseTransactionProxy">
<property name="target">
<bean class="com...RuleDao" parent="baseDAO">
<constructor-arg ref="generalDao"></constructor-arg>
</bean>
</property>
</bean>
class ruleDao{
generalDao.generalSaveOrUpdateAll(hbms); // OK HERE
saveOrUpdateAll(otherHbms); //Exception here
}
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>flushMode</param-name>
<param-value>AUTO</param-value>
</init-param>
</filter>