框架学习之Hibernate 第十节 事务原理与分析
1.事務
兩種事務:
① JDBC事務:單個數據庫的事務
一個SesisonFactory對應一個數據庫,使用 JDBC 實現
常用代碼的模板:
Session session = null;Transaction tx =null;try {session = sessionFactory.openSession();tx = session.beginTransaction();//processtx.commit();} catch(HibernateException e){if(tx != null)tx.rollback();throw e;}finally {if (session != null)session.close();}connection.setAutoCommit(false);connection.commit();conn.rollback();?
②JTA 事務:跨數據庫的事務
使用JTATransaction需要配置hibernate.transaction.factory_class參數,該參數缺省值是org.hibernate.transaction. JDBCTransactionFactory,
當使用JTATransaction時需要將該參數改成org.hibernate.transaction.JTATransactionFactory,并配置jta.UserTransaction參數JNDI名
[JNDI:Java Naming and Directory Interface,Java命名和目錄接口,是一組在Java應用中訪問命名和目錄服務的API。
命名服務將名稱和對象聯系起來,使得我們可以用名稱訪問對象。目錄服務是一種命名服務,在這種服務里,對象不但有名稱,還有屬性。]
(Hibernate在啟動JTATransaction時要用該值到JNDI的上下文Context中去找javax.transaction.UserTransaction)。
javax.transaction.UserTransactin tx = context.lookup(“jndiName”);
javax.transaction.UserTransactin tx = context.lookup(“jndiName”); try{tx.begin();//多個數據庫的session操作;//session1….//session2….tx.commit(); }catch(Exception e){tx.rollback(); throw e; }?
2.Open Session in View
優點:實現了數據訪問層 和 業務邏輯層 的脫離,更好的實現了三層架構
缺點:延長了Session和事務的時間,容易造成內存不夠或者數據鎖死的現象
Open session in view:在生成(渲染)頁面時保持 session打開。
session context和事務邊界[事務邊界就是和事務有關的處理,包括事務打開,提交和回滾]
用current_session_context_class屬性來定義context(用sessionFactory.getCurrentSession()來獲得session),其值為:
1.thread:ThreadLocal來管理Session實現多個操作共享一個Session,避免反復獲取Session,并控制事務邊界,
此時session不能調用close,當commit或rollback的時候session會自動關閉(connection.release_mode:after_transaction)。
2.jta:由JTA事務管理器來管理事務(connection.release_mode:after_statement)。
?
測試代碼:[ 注:一下代碼經常是放在 J2EE 項目中,而不是J2SE,故下面代碼在沒有引入JavaEE包時會報錯的 ]
package cn.itcast.hibernate;import java.io.IOException;import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse;import org.hibernate.Session; import org.hibernate.Transaction;public class OpenSessionInView implements Filter {public void destroy() {// TODO Auto-generated method stub}public void doFilter(ServletRequest arg0, ServletResponse arg1,FilterChain arg2) throws IOException, ServletException {Session session = null;Transaction tx = null;try {session = HibernateUtil.getThreadLocalSession();tx = session.beginTransaction();arg2.doFilter(arg0, arg1); // 控制事務邊界,避免反復獲取session,這里tx.commit();} catch (Exception e) {if (tx != null)tx.rollback();throw new RuntimeException(e.getMessage(), e);} finally {HibernateUtil.closeSession();}}public void init(FilterConfig arg0) throws ServletException {// TODO Auto-generated method stub}}?
注意 HibernateUtils中要添加:
private static ThreadLocal session = new ThreadLocal();public static Session getThreadLocalSession() {if (s == null) {s = getSession();session.set(s);}return s;}public static void closeSession() {Session s = (Session) session.get();if (s != null) {s.close();session.set(null);}}
ThreadLocal? 就是當前線程,通過它可以得到 當前線程的 session 對象
?
3.事務中的悲觀鎖和樂觀鎖
問題:A和B同時打開了 一個編輯頁面,然后A將原來的名稱name改為了name1,并提交,保存到數據庫中了
接著B也將原來的名稱改了,改成了name2,并提交,也保存到數據庫中了。經過了兩個人的操作,A的操作沒有任何作用,他的操作被B覆蓋掉了
更有甚者,如果他們修改的是不同的地方,B修改名稱,A修改內容,那么A的操作完全是白費的
簡單地說就是:兩個不同的線程的session同時操作了同一條數據,并進行了修改,而且修改后的結果不同,那么誰后提交誰的操作才是有效的操作
悲觀鎖由數據庫來實現;樂觀鎖hibernate用version和timestamp來實現
悲觀鎖:當A打開編輯時,就把這條數據加上鎖
缺點:鎖住了之后其他人就不能操作了,一定要等到A提交了才可以
樂觀鎖:給數據加上一個版本號,每當數據有了更新,就增加版本號。? Hibernate實現了樂觀鎖定功能,只需要在映射文件中配置一下version就好了
優點:有效的防止了上面的情況,如果提交時檢測到提交的數據的版本號低于數據庫中的數據的版本號,那么提交就會失敗
這種情況下,誰先提交那么誰的提交就會成功!后提交的只能重新打開,重新修改
還有另外一種樂觀鎖的實現方式就是時間戳,version 中 type 是 timestamp,但是存在時間精度的問題,不如使用version(integer)的效果好
使用version,測試代碼:
首先在People類中添加 version 屬性,并提供get和set方法
然后再people映射文件中添加: 以integer方式為例
<version name="version" type=""></version>最后,測試類:VersionTest
package com.yinger.main;import org.hibernate.Session; import org.hibernate.Transaction;import com.yinger.domain.Name; import com.yinger.domain.People; import com.yinger.util.HibernateUtils;public class VersionTest {public static void main(String[] args) {People p = addPeople();System.out.println("---------");testTransaction(p.getId());System.out.println("---------");p = getPeople(p.getId());System.out.println(p.getName().getFirstName());}private static void testTransaction(int id) {Session s1 = HibernateUtils.getSession();Transaction tx1 = s1.beginTransaction();//一個事務開啟了People p1 = (People)s1.get(People.class, id);Session s2 = HibernateUtils.getSession();;Transaction tx2 = s2.beginTransaction();//又開啟了另一個事務,并且操作的是同一條數據People p2 = (People)s2.get(People.class, id);p2.getName().setFirstName("firstName 2");p1.getName().setFirstName("firstName 1");tx2.commit();tx1.commit();s1.close();s2.close();}public static People getPeople(int id) {People p= (People)HibernateUtils.get(People.class, id);return p;}public static People addPeople() {People p = new People();Name n = new Name();n.setFirstName("firstName");n.setLastName("lastName");p.setName(n);HibernateUtils.add(p);return p;}}
測試結果:
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: insert into People (version, first_name, last_name, id) values (?, ?, ?, ?) --------- Hibernate: update People set version=?, first_name=?, last_name=? where id=? and version=? Hibernate: update People set version=?, first_name=?, last_name=? where id=? and version=? Exception in thread "main" org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.yinger.domain.People#1]at org.hibernate.persister.entity.BasicEntityPersister.check(BasicEntityPersister.java:1456)at org.hibernate.persister.entity.BasicEntityPersister.update(BasicEntityPersister.java:1999)at org.hibernate.persister.entity.BasicEntityPersister.updateOrInsert(BasicEntityPersister.java:1923)at org.hibernate.persister.entity.BasicEntityPersister.update(BasicEntityPersister.java:2163)at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:75)at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:239)at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:223)at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:137)at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:274)at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:675)at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:293)at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:86)at com.yinger.main.VersionTest.testTransaction(VersionTest.java:34)at com.yinger.main.VersionTest.main(VersionTest.java:15)?
數據庫中數據:
id? version first_name? last_name
1??? 1??? firstName 2??? lastName
改變一下兩個事務提交的順序,重新測試
tx1.commit(); tx2.commit();數據庫中的數據:id version first_name last_name
1 1 firstName 1?? lastName
?這個就是樂觀鎖的方式解決上述問題
總結
以上是生活随笔為你收集整理的框架学习之Hibernate 第十节 事务原理与分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Wear OS软件安装指南
- 下一篇: 漫谈IBM Power VM历史及其特点