Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): Turn your Session into

本文介绍了解决Spring框架中因只读模式导致的写操作异常问题。通过调整OpenSessionInViewFilter的配置和设置正确的事务管理策略,确保应用程序在进行数据库操作时不会遇到Writeoperationsarenotallowedinread-onlymode的错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个异常产生的主要原因是DAO采用了Spring容器的事务管理策略,如果操作方法的名称和事务策略中指定的被管理的名称不能够匹配上,spring 就会采取默认的事务管理策略(PROPAGATION_REQUIRED,read only).如果是插入和修改操作,就不被允许的,所以包这个异常



一般这个错误是事务引起的,如果确定事务没有问题,还是有这个错,可以重写OpenSessionInViewFilter的2个方法

在myfaces的wiki里提供了OpenSessionInViewFilter的一个子类如下:
public class OpenSessionInViewFilter extends org.springframework.orm.hibernate3.support.OpenSessionInViewFilter {
      
        /**
         * we do a different flushmode than in the codebase
         * here
         */
        protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
                Session session = SessionFactoryUtils.getSession(sessionFactory, true);
                session.setFlushMode(FlushMode.COMMIT);
                return session;
        }
        /**
         * we do an explicit flush here just in case
         * we do not have an automated flush
         */
        protected void closeSession(Session session, SessionFactory factory) {
                session.flush();
                super.closeSession(session, factory);
        }
}

************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************

 Write operations are not allowed in read-only mode 只读模式下(FlushMode.NEVER/MANUAL)写操作不允
Write operations are not allowed in read-only mode 只读模式下(FlushMode.NEVER/MANUAL)写操作不允

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.NEVER/MANUAL)写操作不被允许:把你的Session改成FlushMode.COMMIT/AUTO或者清除事务定义中的readOnly标记。

 

错误原因:
          OpenSessionInViewFilter在getSession的时候,会把获取回来的session的flush mode 设为FlushMode.NEVER。然后把该sessionFactory绑定到TransactionSynchronizationManager,使request的整个过程都使用同一个session,在请求过后再接除该sessionFactory的绑定,最后closeSessionIfNecessary根据该session是否已和transaction绑定来决定是否关闭session。在这个过程中,若HibernateTemplate 发现自当前session有不是readOnly的transaction,就会获取到FlushMode.AUTO Session,使方法拥有写权限。也即是,如果有不是readOnly的transaction就可以由Flush.NEVER转为Flush.AUTO,拥有insert,update,delete操作权限,如果没有transaction,并且没有另外人为地设flush model的话,则doFilter的整个过程都是Flush.NEVER。所以受transaction(声明式的事务)保护的方法有写权限,没受保护的则没有。

解决方法:

web.xml配置里添加
<filter>
   <filter-name>OpenSessionInViewFilter</filter-name>
   <filter-class>
    org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
   </filter-class>
   <init-param>
    <param-name>sessionFactoryBeanName</param-name>
    <param-value>sessionFactory</param-value>
   </init-param>
   <init-param>
            <param-name>singleSession</param-name>
            <param-value>true</param-value>           
        </init-param>
        <init-param>
        <param-name> flushMode </param-name>
   <param-value>AUTO </param-value>        
        </init-param>
</filter>
  //   。。。。


<filter-mapping>
   <filter-name>OpenSessionInViewFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

 

如果在交给spring 管理的情况下,在beans.xml 里的配置

 <bean id="txManager"
  class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
 </bean>

 <aop:config>
  <aop:pointcut id="bussinessService"
   expression="execution(* com.fan.service.base.*.*(..))" />
  <aop:advisor pointcut-ref="bussinessService"
   advice-ref="txAdvice" />
 </aop:config>

 <tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
   <tx:method name="get*" read-only="false" propagation="NOT_SUPPORTED"/>
   <tx:method name="find*" read-only="false" propagation="NOT_SUPPORTED"/>
   <tx:method name="save*" propagation="REQUIRED"/> // 如果不把save update delete都配置上,
   <tx:method name="update*" propagation="REQUIRED"/> //这些操作会无效
   <tx:method name="delete*" propagation="REQUIRED"/>
  </tx:attributes>
 </tx:advice>


<think>好的,用户现在遇到了Hibernate的“Write operations are not allowed in read-only mode (FlushMode.MANUAL)”错误,需要解决。根据之前的对话,用户已经了解过如何解决LazyInitializationException,现在这个问题涉及事务的只读模式和FlushMode设置。我需要结合用户提供的引用内容,特别是引用[1]、[2]、[3],来给出解决方案。 首先,引用中提到问题是由于事务被标记为只读,导致无法进行写操作。解决办法包括调整FlushMode或移除只读标记。我需要详细说明如何修改事务的FlushMode,比如设置为COMMIT或AUTO,或者移除只读属性。 接下来,用户可能是在使用Spring的事务管理,比如通过@Transactional注解。需要检查事务注解中的readOnly设置是否正确。如果方法需要写操作,应该确保readOnly=false,或者不设置该属性。 另外,可能需要手动设置Hibernate的FlushMode,例如在代码中使用HibernateTemplate或直接操作Session。如果用户使用的是HibernateTemplate,可能需要调整其配置,或者在特定操作前临时更改FlushMode。 还要考虑事务管理器的配置是否正确,是否与Hibernate集成良好。例如,HibernateTransactionManager是否正确配置,是否支持读写事务。 此外,用户可能在服务层的方法上错误地标记了只读事务,导致在内部调用写操作时出现异常。需要提醒用户检查事务的传播行为,确保写操作在正确的事务上下文中执行。 最后,可能需要对比不同的解决方案,帮助用户选择最适合他们场景的方法,比如推荐优先使用事务注解调整,再考虑手动设置FlushMode,或者修改全局配置。同时,要指出潜在的问题,比如性能影响或配置错误的风险。 总结步骤应该是:1. 检查并调整@Transactional的readOnly属性;2. 手动设置FlushMode;3. 配置事务管理器;4. 检查事务传播设置;5. 全局配置默认FlushMode。每个步骤需要具体代码示例和配置说明,确保用户能够操作。</think>### 解决Hibernate中"Write operations are not allowed in read-only mode"错误 该错误的核心原因是**事务被标记为只读(readOnly=true)时,Hibernate的Session会强制使用FlushMode.MANUAL**,而此模式下禁止所有写操作(如save/update/delete)。以下是5种针对性解决方案: --- #### 1. 修改事务注解声明(推荐方案) 在需要写操作的Service方法上显式设置`readOnly=false`: ```java @Service public class UserService { @Transactional(readOnly = false) // 明确关闭只读模式 public void updateUser(User user) { userRepository.save(user); } } ``` **注意**:若父类/接口的`@Transactional`注解已定义`readOnly=true`,需使用`@Transactional(propagation = REQUIRES_NEW)`创建新事务[^1]。 --- #### 2. 动态调整FlushMode 在需要写操作的代码段中手动修改Session的刷新模式: ```java @Autowired private SessionFactory sessionFactory; public void batchUpdate() { Session session = sessionFactory.getCurrentSession(); session.setFlushMode(FlushMode.AUTO); // 临时切换刷新模式 // 执行写操作 session.createQuery("update User u set u.active = true").executeUpdate(); session.setFlushMode(FlushMode.MANUAL); // 恢复原模式(可选) } ``` --- #### 3. 配置事务管理器(Spring Boot) 在`application.properties`中全局设置默认FlushMode: ```properties # 设置Hibernate默认刷新模式为AUTO spring.jpa.properties.org.hibernate.flushMode=AUTO ``` 或通过JavaConfig配置: ```java @Bean public HibernateJpaVendorAdapter jpaVendorAdapter() { HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter(); adapter.getJpaPropertyMap().put("hibernate.flushMode", "AUTO"); return adapter; } ``` --- #### 4. 检查事务传播链 当存在嵌套事务时,确保写操作不在被只读事务包裹的上下文中: ```java @Transactional(readOnly = true) public void parentMethod() { // 此方法标记为只读事务 childMethod(); // 子方法需要新事务 } @Transactional(propagation = Propagation.REQUIRES_NEW) public void childMethod() { // 此处创建新事务,不受父方法只读限制 } ``` --- #### 5. 修复事务管理器配置 若同时配置了多个事务管理器,需指定默认事务管理器: ```java @Configuration @EnableTransactionManagement public class PersistenceConfig { @Bean(name = "transactionManager") public PlatformTransactionManager transactionManager(EntityManagerFactory emf) { return new JpaTransactionManager(emf); // 明确使用JPA事务管理器 } } ``` --- ### 原理对照表 | 错误场景 | 解决方案 | 作用机制 | |-------------------------------|-----------------------------|-------------------------------------------------------------------------| | 方法级事务注解冲突 | 调整@Transactional参数 | 覆盖父类/接口的事务定义 | | 全局配置导致模式冲突 | 修改hibernate.flushMode | 改变Hibernate的默认刷新策略 | | 嵌套事务传播异常 | 使用REQUIRES_NEW传播属性 | 创建独立事务上下文避免继承只读标记 | | 事务管理器选择错误 | 指定JpaTransactionManager | 确保Hibernate SessionSpring事务管理器的正确关联 | --- ### 典型错误排查流程 1. 检查所有调用链路上的`@Transactional`注解 2. 在DAO层方法添加断点验证SessionflushMode 3. 查看事务管理器日志确认事务边界 4. 使用Hibernate的统计信息监控SQL执行情况: ```properties spring.jpa.properties.hibernate.generate_statistics=true ``` --- ### 扩展建议 - 对于批量写操作,建议结合`@Modifying`注解与`clearAutomatically`参数: ```java @Modifying(clearAutomatically = true) @Query("update User u set u.loginCount = u.loginCount + 1") int incrementLoginCounts(); ``` - 使用Spring Data JPA 2.7+的`@QueryHints`控制会话状态: ```java @QueryHints(@QueryHint(name = "org.hibernate.flushMode", value = "COMMIT")) List<User> findActiveUsers(); ``` [^1]: 事务传播级别需根据业务需求谨慎选择,错误使用可能导致事务失效或死锁 [^2]: Hibernate官方建议优先通过事务声明控制刷新模式,而非手动修改Session状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值