HTTP Status 500 - Write operations are not allowed in read-only mode (FlushMode.MANUAL)

本文介绍了一个关于SSH框架整合时出现的常见错误——在只读模式下进行写操作,并给出了详细的错误信息及堆栈跟踪。同时,文章还提供了具体的解决办法,包括在配置文件中设置事务管理器和在Service层使用注解来开启事务。

SSH错误:网页错误信息如下

HTTP Status 500 - Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.

这个错误是在用ssh框架时经常碰到的一个问题,错误信息如下:

网页信息:

HTTP Status 500 - Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
type Exception report
message Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
description The server encountered an internal error that prevented it from fulfilling this request.
exception
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
org.springframework.orm.hibernate5.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1126)
org.springframework.orm.hibernate5.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:619)
org.springframework.orm.hibernate5.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:616)
org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:341)
org.springframework.orm.hibernate5.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:309)
org.springframework.orm.hibernate5.HibernateTemplate.save(HibernateTemplate.java:616)
cn.fpxty.dao.LinkManDaoImpl.addLinkMan(LinkManDaoImpl.java:11)
cn.fpxty.service.LinkManService.addLinkMan(LinkManService.java:14)
cn.fpxty.action.LinkManAction.addLinkMan(LinkManAction.java:49)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
ognl.OgnlRuntime.invokeMethod(OgnlRuntime.java:870)
ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1293)
ognl.ObjectMethodAccessor.callMethod(ObjectMethodAccessor.java:68)
com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor.callMethodWithDebugInfo(XWorkMethodAccessor.java:117)
com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor.callMethod(XWorkMethodAccessor.java:108)
ognl.OgnlRuntime.callMethod(OgnlRuntime.java:1369)
ognl.ASTMethod.getValueBody(ASTMethod.java:90)
ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
ognl.SimpleNode.getValue(SimpleNode.java:258)
ognl.Ognl.getValue(Ognl.java:494)
ognl.Ognl.getValue(Ognl.java:458)
com.opensymphony.xwork2.ognl.OgnlUtil$2.execute(OgnlUtil.java:309)
com.opensymphony.xwork2.ognl.OgnlUtil.compileAndExecute(OgnlUtil.java:340)
com.opensymphony.xwork2.ognl.OgnlUtil.getValue(OgnlUtil.java:307)
com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:423)
com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:287)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:250)
org.apache.struts2.interceptor.DeprecationInterceptor.intercept(DeprecationInterceptor.java:41)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:256)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:167)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:265)
org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:76)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:138)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:191)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:73)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
org.apache.struts2.interceptor.DateTextFieldInterceptor.intercept(DateTextFieldInterceptor.java:125)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:91)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:253)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:100)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:141)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:145)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:139)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:193)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:189)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54)
org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:564)
org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81)
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)

控制台信息:

12:05:26,678 ERROR DefaultDispatcherErrorHandler:42 - Exception occurred during processing request: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
at org.springframework.orm.hibernate5.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1126)
at org.springframework.orm.hibernate5.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:619)
at org.springframework.orm.hibernate5.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:616)
at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:341)
at org.springframework.orm.hibernate5.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:309)
at org.springframework.orm.hibernate5.HibernateTemplate.save(HibernateTemplate.java:616)
at cn.fpxty.dao.LinkManDaoImpl.addLinkMan(LinkManDaoImpl.java:11)
at cn.fpxty.service.LinkManService.addLinkMan(LinkManService.java:14)
at cn.fpxty.action.LinkManAction.addLinkMan(LinkManAction.java:49)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at ognl.OgnlRuntime.invokeMethod(OgnlRuntime.java:870)
at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1293)
at ognl.ObjectMethodAccessor.callMethod(ObjectMethodAccessor.java:68)
at com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor.callMethodWithDebugInfo(XWorkMethodAccessor.java:117)
at com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor.callMethod(XWorkMethodAccessor.java:108)
at ognl.OgnlRuntime.callMethod(OgnlRuntime.java:1369)
at ognl.ASTMethod.getValueBody(ASTMethod.java:90)
at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
at ognl.SimpleNode.getValue(SimpleNode.java:258)
at ognl.Ognl.getValue(Ognl.java:494)
at ognl.Ognl.getValue(Ognl.java:458)
at com.opensymphony.xwork2.ognl.OgnlUtil$2.execute(OgnlUtil.java:309)
at com.opensymphony.xwork2.ognl.OgnlUtil.compileAndExecute(OgnlUtil.java:340)
at com.opensymphony.xwork2.ognl.OgnlUtil.getValue(OgnlUtil.java:307)
at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:423)
at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:287)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:250)
at org.apache.struts2.interceptor.DeprecationInterceptor.intercept(DeprecationInterceptor.java:41)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:256)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:167)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:265)
at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:76)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:138)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:191)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:73)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at org.apache.struts2.interceptor.DateTextFieldInterceptor.intercept(DateTextFieldInterceptor.java:125)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:91)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:253)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:100)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:141)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:145)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:139)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:193)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:189)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244)
at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54)
at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:564)
at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81)
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:670)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)

主要代码

这次是在action中获取jsp页面的参数,封装到实体类对象中,然后调用service对象的add方法,service中又调用dao的add方法,最终将数据存储到数据库中。主要代码如下:
LinkManAction.java
package cn.fpxty.action;

import java.util.List;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

import cn.fpxty.entity.Customer;
import cn.fpxty.entity.LinkMan;
import cn.fpxty.service.CustomerService;
import cn.fpxty.service.LinkManService;

public class LinkManAction extends ActionSupport implements ModelDriven<LinkMan>{
	//模型驱动
	private LinkMan linkMan=new LinkMan();
	public LinkMan getModel() {
		return linkMan;
	}
	
	private LinkManService linkManService;
	public void setLinkManService(LinkManService linkManService) {
		this.linkManService = linkManService;
	}
	//注入customerService
	private CustomerService customerService;
	public void setCustomerService(CustomerService customerService) {
		this.customerService = customerService;
	}

	/**
	 * 1.到新增联系人页面
	 */
	public String toAddPage(){
		List<Customer> listCustomer=customerService.findAll();
		ServletActionContext.getRequest().setAttribute("listCustomer", listCustomer);
		return "toAddPage";
	}
	
	/**
	 * 2.添加联系人到数据库
	 */
	public String addLinkMan(){
		/*获取页面数据,封装到LinkMan对象中(模型驱动)
		 * 页面cid数据要封装到LinkMan对象的Customer属性对象中
		 * 必须在jsp页面中name值写成customer.cid
		 */
		linkManService.addLinkMan(linkMan);
		return "addLinkMan";
	}

}

LinkManService.java
package cn.fpxty.service;

import org.springframework.transaction.annotation.Transactional;

import cn.fpxty.dao.LinkManDao;
import cn.fpxty.entity.LinkMan;

public class LinkManService {
	private LinkManDao linkManDao;
	public void setLinkManDao(LinkManDao linkManDao) {
		this.linkManDao = linkManDao;
	}
	public void addLinkMan(LinkMan linkMan) {
		linkManDao.addLinkMan(linkMan);
		
	}
	
	
}
LinkManDaoImpl.java
package cn.fpxty.dao;

import org.springframework.orm.hibernate5.support.HibernateDaoSupport;

import cn.fpxty.entity.LinkMan;

public class LinkManDaoImpl extends HibernateDaoSupport implements LinkManDao {

	
	public void addLinkMan(LinkMan linkMan) {
		this.getHibernateTemplate().save(linkMan);
		
	}	
}

分析原因

这个错误主要是因为用了Spring框架并且三个框架整合后,dao层主要用HibernateTemplate的方法对数据库进行操作,这样必须得添加事务才能不出错。

解决方案

这次用在配置文件中配置,然后在service中采用注解的方式添加事务:
Bean.xml中部分代码:
<!--配置事务管理器  -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
	<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 开启事务扫描,采用注解方式 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
LinkManService.java中











<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 Session与Spring事务管理器的正确关联 | --- ### 典型错误排查流程 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、付费专栏及课程。

余额充值