解决Spring4+Hibernate4遇到的 Write operations are not allowed in read-only mode (FlushMode.MANUAL)

本文介绍了解决Spring框架中DAO层出现的Writeoperationsarenotallowedinread-onlymode错误的方法。通过正确配置事务属性,特别是针对读写操作的不同场景。

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

全部的错误stack类似:

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.hibernate4.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1135)
    at org.springframework.orm.hibernate4.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:620)
    at org.springframework.orm.hibernate4.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:617)
    at org.springframework.orm.hibernate4.HibernateTemplate.doExecute(HibernateTemplate.java:340)
    at org.springframework.orm.hibernate4.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:308)
    at org.springframework.orm.hibernate4.HibernateTemplate.save(HibernateTemplate.java:617)
    at com.sac.capital.dao.GenericDaoImpl.create(GenericDaoImpl.java:36)
    at com.sac.capital.dao.test.TestDaoImplTest.test(TestDaoImplTest.java:35)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)


我相信很多人应该都遇到了吧,我也搜了google, 不过给的答案都不尽人意。其实原因正如stack所写:当FlushMode为Manual时,不能在readonly情况下进行写操作。


如果你在写DAO层,然后又对DAO进行test,DAO的class的事务声明都是默认的话,就会遇到上面这个错误。


那么我们先把service和dao全部写好,然后把service层的声明式事务也配置好: 通常如下:

    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED" />
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="create*" propagation="REQUIRED" />
            <tx:method name="insert*" propagation="REQUIRED" />
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="merge*" propagation="REQUIRED" />
            <tx:method name="del*" propagation="REQUIRED" />
            <tx:method name="remove*" propagation="REQUIRED" />
            <tx:method name="put*" propagation="REQUIRED" />
            <tx:method name="use*" propagation="REQUIRED" />
            
            <tx:method name="get*" propagation="REQUIRED" read-only="true" />
            <tx:method name="count*" propagation="REQUIRED" read-only="true" />
            <tx:method name="find*" propagation="REQUIRED" read-only="true" />
            <tx:method name="list*" propagation="REQUIRED" read-only="true" />
            <tx:method name="*" read-only="true" />
        </tx:attributes>
    </tx:advice>
    <aop:config expose-proxy="true">
        <aop:pointcut id="txPointcut" expression="execution(* com.sac.capital.service..*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
    </aop:config>


然后再对Service的类进行测试,数据库操作完全成功。  但单独对Dao的类测试, 数据库写操作还是会报这个错误。

(这里我是觉得Service和DAO两层, 没必要都写unit test啦,但如果你项目非要全都写, 请继续看下去)


到了这里,你也知道这错误是怎么出来地, 就是因为DAO层完全没有事务的声明,  所以给DAO也加上事务声明把:

    <aop:config expose-proxy="true">
        <aop:pointcut id="txPointcut" expression="execution(* com.sac.capital.dao..*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
    </aop:config>


后来深入研究发现, 上面那段copy来的声明式事务 其实写的也不好, 应该改成:

    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="count*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="list*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="*" read-only="false" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>

是不是更好点???



<think>好的,我现在需要解决用户的问题:“White spaces are not allowed in the ESP-IDF Tools path.”。用户可能是在安装或配置ESP-IDF开发环境时遇到了路径包含空格的错误提示。 首先,我得回想一下ESP-IDF的相关知识。ESP-IDF是乐鑫为ESP32系列芯片提供的开发框架,安装过程中需要设置工具链的路径。通常,这类开发工具对路径中的空格比较敏感,尤其是像Windows系统下的Program Files这样的默认安装路径,里面包含空格,可能导致问题。 接下来,用户的问题明确指出工具路径中不允许有空格。这意味着用户可能将ESP-IDF或相关工具安装在了含有空格的目录下,比如“C:\Program Files\...”,而这样的路径会被系统或脚本错误解析,导致安装失败或运行异常。 解决方案的第一步应该是建议用户检查当前的安装路径,确认是否存在空格。如果有,需要将整个ESP-IDF工具目录移动到一个不含空格的路径下,例如“C:\esp-idf”或用户主目录下的某个路径。同时,用户需要更新环境变量,比如IDF_TOOLS_PATH,确保指向新的路径。 此外,可能还需要考虑其他相关配置,比如在安装脚本或配置文件中指定正确的路径。对于使用Windows的用户,可能还需要注意反斜杠的转义问题,或者在命令行中使用引号包裹路径。不过,根据错误提示,最根本的解决办法还是避免路径中的空格。 还需要提醒用户,修改路径后可能需要重新运行安装脚本或重新配置环境,以确保所有工具都能正确识别新的路径。如果用户使用的是Visual Studio Code或其他IDE,可能需要更新工作区设置中的相关路径。 最后,应该给出具体的步骤示例,帮助用户一步步操作。例如,移动文件夹,设置环境变量,重新安装工具等。同时,提醒用户在操作前备份数据,避免意外丢失。</think>关于 "White spaces are not allowed in the ESP-IDF Tools path" 错误的解决方案: **问题原因:** ESP-IDF 工具链路径包含空格(如 `C:\Program Files\...`),而 ESP-IDF 的安装脚本无法解析带空格的路径。 **解决步骤:** 1. 📂 检查当前路径 - 打开 ESP-IDF 安装目录 - 确认路径是否包含空格(如 `Program Files`, `My Documents` 等) 2. 🚚 迁移安装目录 ```bash # 错误示例路径 C:\Users\Your Name\esp-idf # 包含空格 D:\Program Files\esp-idf # 包含空格 # 正确示例路径 C:\esp-idf D:\ESP32\esp-idf %USERPROFILE%\esp-idf # Windows用户目录 ``` 3. 🔧 修改环境变量(以 Windows 为例): - 打开系统属性 → 高级 → 环境变量 - 修改或新建 `IDF_TOOLS_PATH` 变量 - 设置为不含空格的路径(如 `C:\esp-idf\tools`) 4. ⚡ 重新安装工具链 ```bash # 在 ESP-IDF 安装目录执行 install.bat # 或 export.sh ``` 5. 💻 验证安装 ```bash get_idf # 进入开发环境 idf.py set-target esp32 # 测试命令 ``` **注意:** - macOS/Linux 用户建议使用 `~/esp-idf` 路径 - 避免使用系统保护目录(如 `Program Files`) - 修改路径后需重新运行安装脚本 - 已创建的项目无需移动,只需确保 IDF 路径正确 **常见问题延伸:** 若使用 VSCode 插件,需同步修改: 1. 打开 `.vscode/settings.json` 2. 更新 `idf.espIdfPath` 路径 3. 重启 IDE
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值