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

本文详细解释了在Spring框架中,当Session处于只读模式(FlushMode.NEVER/MANUAL)时,为何不允许进行写操作,并提供了通过配置web.xml和beans.xml来解决此问题的方法。同时,介绍了如何通过事务管理和配置flushMode来确保Session在正确的时间点具有写权限。

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 fromtransaction definition.

问题只读模式下(FlushMode.NEVER/MANUAL)写操作不被允许:把你的Session改成FlushMode.COMMIT/AUTO或者清除事务定义中的readOnly标记。

 

错误原因:
 
         OpenSessionInViewFiltergetSession的时候,会把获取回来的sessionflush mode 设为FlushMode.NEVER。然后把该sessionFactory绑定到TransactionSynchronizationManager,使request的整个过程都使用同一个session,在请求过后再接除该sessionFactory的绑定,最后closeSessionIfNecessary根据该session是否已和transaction绑定来决定是否关闭session。在这个过程中,若HibernateTemplate发现自当前session有不是readOnlytransaction,就会获取到FlushMode.AUTOSession,使方法拥有写权限。也即是,如果有不是readOnlytransaction就可以由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 里的配置

 <beanid="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:adviceid="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"/> //
如果不把saveupdate 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状态
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值