Java Spring和Hibernate突然将事务设置为只读

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运行,没有很多问题,但每隔几天,有时一天中有几次,整个系统就会进入一个糟糕的状态,无法再向数据库写入任何内容。这些堆栈跟踪显示在日志中:

我们有一个应用程序运行在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

我强烈地 不过,我不推荐后者。 如果你正在使用 OpenSessionInViewFilter,合并它 与中间层事务相比 然后根据要求冲洗过滤器 完成

这些有什么值得注意的吗


您的代码或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>