org.springframework.dao.InvalidDataAccessApiUsageException: Write operations
在配置 springmvc+hibernate+MySQL 的時候,出現如下問題:
異常信息:
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.hibernate3.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1175)org.springframework.orm.hibernate3.HibernateTemplate$25.doInHibernate(HibernateTemplate.java:839)org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:406)org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374)org.springframework.orm.hibernate3.HibernateTemplate.delete(HibernateTemplate.java:837)org.springframework.orm.hibernate3.HibernateTemplate.delete(HibernateTemplate.java:833)com.longxia.springmvc.dao.HibernateBaseDao.deleteObject(HibernateBaseDao.java:94)com.longxia.springmvc.manager.usermanager.UserManagerImpl.deleteUser(UserManagerImpl.java:88)com.longxia.springmvc.controller.UserController.deleteAllUser(UserController.java:52)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)org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:436)org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:424)org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:669)org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:574)javax.servlet.http.HttpServlet.service(HttpServlet.java:617)javax.servlet.http.HttpServlet.service(HttpServlet.java:717)org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)問題原因分析:
為了解決session closed 錯誤而是用 openSessionInViewInterceptor 或者 openSessionInViewFilter 延遲加載的錯誤,但是在我們開啟OpenSessionInViewFilter這個過濾器的時候FlushMode就已經被默認設置為了MANUAL,如果FlushMode是MANUAL或NEVEL,在操作過程中 hibernate會將事務設置為readonly,所以在增加、刪除或修改操作過程中會出現如下錯誤
解決方案:
1、在執行操作之前 插入getHibernateTemplate().setFlushMode(2) 或者 在方法執行之后 getHibernateTemplate().flush();? 這也能夠明白為什么會出現這個原因的。但是本人不推薦這種解決方式
2、使用 hibernateFilter 來解決:
<filter> <filter-name>hibernateFilter</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> <init-param> <param-name>singleSession</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>sessionFactoryBeanName</param-name> <param-value>sessionFactory</param-value> </init-param> </filter> <filter-mapping> <filter-name>hibernateFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
?
3、配置spring 事務,讓spring 來管理hibernate session,有 aop, 有基于注解的;(本人推薦這種方式,因為項目都會有事務的操作)
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" p:sessionFactory-ref="sessionFactory"> </bean> <!-- 用注解來實現事務管理 將所有具有@Transactional 注解的文件自動配置為聲明式事務支持--> <!--tx:annotation-driven transaction-manager="transactionManager" /--> <!-- xml配置事務 --> <tx:advice id="txAdviceHibernate" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="*" read-only="true" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="serviceMethodsHibnerate" expression="execution(* com.longxia.springmvc.manager..*.*(..))"/> <aop:advisor advice-ref="txAdviceHibernate" pointcut-ref="serviceMethodsHibnerate" /> </aop:config> <!-- xml配置事務-->
?
99.9%的人配置了事務之后,都會解決這個問題。但是還有0.1%的人還是悲催的人,哥就是那0.1%的異類。
第一,檢查事務的傳播級別對不對,
例如 你配的
<tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="*" read-only="true" />但是你的方法名稱是 delInfo(...)..;那么你對應的事務傳播級別就是 <tx:method name="*" read-only="true" /> ;還是read-only;
所以你要給你的方法改為 deleteInfo(...);或者在事務的傳播級別加上一個<tx:method name="del*" propagation="REQUIRED" />
第二、一個十分蛋疼的問題;你的 manager 到底有沒有以bean的形式注入到sping容器里面去,這也是導致我這次問題的原因
由于我使用的是springmvc模式,所以我就用這種方式一起注入 controller,manager,dao
<!-- 激活@Controller模式 --> <mvc:annotation-driven /> <!-- 對包中的所有類進行掃描,以完成Bean創建和自動依賴注入的功能 --> <context:component-scan base-package="com.longxia.**.controller" /> <context:component-scan base-package="com.longxia.**.manager" /> <context:component-scan base-package="com.longxia.**.dao" />
雖然這樣訪問沒有什么問題,在controller里面訪問manager,在manager里面訪問dao沒有什么問題,
但是spring api 上說 :
This tag registers the DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter beans that are required for Spring MVC to dispatch requests to Controllers.
這個標簽注冊了Spring MVC分發請求到控制器所必須的DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter實例
個人認為這個只是單純的對controller進行進行分發。并沒有將service以bean的形式注入的spring容器中;
所以我將 manager和dao 放在 applicationDataSource.xml文件中以下面這種形式注入spring容器中(<context : annotation-config />):
<context:annotation-config/> <context:component-scan base-package="com.longxia.**.manager" /> <context:component-scan base-package="com.longxia.**.dao" />如果覺得放在applicationDataSource.xml里面比較臃腫,可以獨立一個applicationContext.xml 將代碼復制進去
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:annotation-config/> <context:component-scan base-package="com.longxia.**.manager" /> <context:component-scan base-package="com.longxia.**.dao" /> </beans>
?
然后再web.xml文件中配置applicationContext.xml,引入進去
<!-- 設定配置文件列表 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> <!-- classpath*: 指定編譯后的class目錄 --> classpath*:config/application*.xml </param-value> </context-param>
這樣就可以解決問題了。起碼我是這樣解決的,哎,困擾了3天。瘋掉了。還好今天突發想法。所以分享我的經驗,希望后來人早點解決問題。
?我的項目結構(只是自己練習的小項目,嘿嘿)
題外話:
關于 mvc:annotation-driven 和 context:annotation-config 這兩個區別,我在網上找了一點資料,順便貼出來供大家分享,(文章出處http://mushiqianmeng.blog.51cto.com/3970029/723880)
在基于主機方式配置Spring的配置文件中,你可能會見到<context:annotation-config/>這樣一條配置,他的作用是式地向?Spring?容器注冊
AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、
PersistenceAnnotationBeanPostProcessor?以及?RequiredAnnotationBeanPostProcessor?這?4?個BeanPostProcessor。
注冊這4個BeanPostProcessor的作用,就是為了你的系統能夠識別相應的注解。
例如:
如果你想使用@Autowired注解,那么就必須事先在?Spring?容器中聲明?AutowiredAnnotationBeanPostProcessor Bean。傳統聲明方式如下:
如果想使用@ Resource?、@ PostConstruct、@ PreDestroy等注解就必須聲明CommonAnnotationBeanPostProcessor
如果想使用@PersistenceContext注解,就必須聲明PersistenceAnnotationBeanPostProcessor的Bean。
如果想使用?@Required的注解,就必須聲明RequiredAnnotationBeanPostProcessor的Bean。同樣,傳統的聲明方式如下:
一般來說,這些注解我們還是比較常用,尤其是Antowired的注解,在自動注入的時候更是經常使用,所以如果總是需要按照傳統的方式一條一條配置顯得有些繁瑣和沒有必要,于是spring給我們提供<context:annotation-config/>的簡化配置方式,自動幫你完成聲明。
? ?不過,呵呵,我們使用注解一般都會配置掃描包路徑選項
? ??該配置項其實也包含了自動注入上述processor的功能,因此當使用?<context:component-scan/>?后,就可以將?<context:annotation-config/>?移除了。
總結
以上是生活随笔為你收集整理的org.springframework.dao.InvalidDataAccessApiUsageException: Write operations的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java.lang.IllegalArg
- 下一篇: Eclipse中写jsp文件时,发现里面