SSH Write operations are not allowed in read-only mode 错误解决

本文讨论了在使用Spring、Structs和Hibernate进行整合时,配置OpenSessionInViewFilter并设置FlushMode为AUTO解决InvalidDataAccessApiUsageException问题的方法。同时介绍了通过配置事务边界来替代修改过滤器配置的解决方案。
 

最近在配置 Structs, Spring 和Hibernate整合的问题:

开启OpenSessionInViewFilter来阻止延迟加载的错误的时候抛出了这个异常:

    org.springframework.dao.InvalidDataAccessApiUsageException错误
但是在我们开启OpenSessionInViewFilter这个过滤器的时候FlushMode就已经被默认设置为了MANUAL!

如果FlushMode是MANUAL或NEVEL,在操作过程中 hibernate会将事务设置为readonly,所以在增加、删除或修改操作过程中会出现如下错误:

org.springframework.dao.InvalidDataAccessApiUsageException:Write operations are not allowed in read-only mode (FlushMode.NEVER) turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition


解决办法1:

    直接修改OpenSessionInViewFilter过滤器的配置,配置过滤器的时候配置就是在一般的配置里面加上下面蓝色部分就可以了,直接指定flushMode的配置就OK了:

下面是配置文件:(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>
<filter-mapping>
	<filter-name>OpenSessionInViewFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

 

解决方法2:

    就是配置事务的边界,在你方法的执行时配置事务边界!

下面是sessionFactor.xml配置:

<!-- 事务的配置 -->    
	<!-- sessionFactory 为自己配置 sessionFactory 的 bean-->
	<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>
	<aop:config>
		<!-- execution(public * *.*.*..*.*(..)) 为自己项目中操作数据库中的方法 -->
		<aop:pointcut id="**" expression="execution(public * *.*.*..*.*(..))" />
		<aop:advisor pointcut-ref="**" advice-ref="txAdvice" />
		
		<!-- 可以设置两个pointcut,id不能相同 -->
		<aop:pointcut id="**" expression="execution(public * *.*.*..*.*(..))" />
		<!-- 同时将第二个pointcut 也关联 txAdvice -->
		<aop:advisor pointcut-ref="**" advice-ref="txAdvice" />
		
		
	</aop:config>
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
		    <!-- name 为 方法名 -->
			<tx:method name="**" read-only="true" />
			<tx:method name="**" propagation="REQUIRED" />
		</tx:attributes>
	</tx:advice>

 

 

下面是总结:

原理:因为配置openSessionInView时,启动后他默认是给没有配置事务边界的方法都默认为只读的,所以在插入数据时就会报上面的错

 

转载自:http://www.cnblogs.com/clinglin/archive/2011/04/05/2005542.html

 

 

@

<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)时,HibernateSession会强制使用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层方法添加断点验证Session的flushMode 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、付费专栏及课程。

余额充值