關於Hibernate與spring整合更新無效的分析
Hibernate是一個ORM持久化框架,也是主流的ORM框架之一。使用它,程式設計師能夠利用OOP思想操作資料庫資料。但是新手剛開始學習Hibernate的時候,由於對於Hibernate的工作原理和機制理解不透徹,在使用過程中,將會遇到許多問題和掉入許多深坑(好幾天出不來,比如樓主我,唉~~~~),大大的降低了工作效率。最近使用Hibernte做個人專案時,又掉進了深坑,為了避免更多小白入坑難以自拔,就有了樓樓這篇漏作。
在使用Hibernate的時候,使用框架的介面方法,能夠簡化開發程式碼,樓樓在做個人專案時為了省點時間擼,就想嘗試使用Hibernate的介面方法,像什麼save()、delete()、update()、saveorUpdate()的,不過確實很方便。在使用過程中就遇到問題,就行update()更新無效,資料更新不完全等等。
一、 使用Update更新資料無效。
在呼叫update方法更新資料時,更新操作無效,也不丟擲任何異常。樓樓複習了hibernate知識和查閱了相關資料,並經過了自身測試,找到了問題所在。
在使用Hibernate與spring整合時,將Hibernate事務交給spring管理。配置檔案如:
<!-- 定義事務 --> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 配置詳細的事務定義 --> <tx:attributes> <!-- 所有以查詢資料庫的方法是read-only的 --> <tx:method name="get*" 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="add*" propagation="REQUIRED" /> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="del*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut expression="execution(* com.hal.bms..*.services..*.*Services.*(..))" id="allpoint" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="allpoint" /> </aop:config>
在配置檔案中,配置了事務管理的,其他方法呼叫不存在任何問題,但是update方法卻出了問題。也就是說並不是spring事務管理的事。
情況一:
測試程式碼:
public void updateSelf(User user){
System.out.println("進入本方法!");
getSession().update(user);
getSession().flush();
}
注意圖片中的第四條資料,待會就是測試這條了。
然後將使用者的地址改為“中國北京”,看看測試結果。
測試成功。說明更新無效是由於只更新了快取中的資料,如果不呼叫flush,則資料庫並不會更新。
能成功的前提是因為在web.xml配置了OSIV
<!-- Hibeinate OSIV 配置 -->
<filter>
<filter-name>OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate4.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>
情況二:
就是情況一下面要說的FlushMode屬性,該屬性有五中狀態(這個網上一查就知道了),分別是:
1、NEVEL:被MANUAL取代了
2 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;
解決辦法:配置事務,spring會讀取事務中的各種配置來覆蓋hibernate的session中的FlushMode;
3 AUTO
設定成auto之後,當程式進行查詢、提交事務或者呼叫session.flush()的時候,都會使快取和資料庫進行同步,也就是重新整理資料庫
4 COMMIT
提交事務或者session.flush()時,重新整理資料庫;查詢不重新整理
5 ALWAYS:
每次進行查詢、提交事務、session.flush()的時候都會刷資料庫
ALWAYS和AUTO的區別:當hibernate快取中的物件被改動之後,會被標記為髒資料(即與資料庫不同步了)。當 session設定為FlushMode.AUTO時,hibernate在進行查詢的時候會判斷快取中的資料是否為髒資料,是則刷資料庫,不是則不刷,而always是直接重新整理,不進行任何判斷。很顯然auto比always要高效得多。
所以如果沒有配置OSIV,就需要自己手動設定FlushMode屬性:session.setFlushMode(FlushMode.xxx)。
情況三:就是事務並沒有管理update的類或方法,這時就需要自己手動新增事務管理。在方法上新增事務註解屬性@Transactional。然後參考情況一二。
以上屬個人對於Hibernate的理解,如有偏差,還望指正。