7.hibernate事務(wù)隔離

  1. 事務(wù)具有ACID原則屋厘,各個(gè)數(shù)據(jù)庫實(shí)現(xiàn)了這些原則(用日志實(shí)現(xiàn)原子性坏逢,一致性高诺,持久性踱讨,用鎖實(shí)現(xiàn)隔離性)
  2. 隔離級別有四種(Read UnCommitted,Read Committed,Repeated Read, Serializable)惹谐,在不同的隔離級別下持偏,有可能會出現(xiàn)臟讀驼卖,不可重復(fù)讀,幻讀等現(xiàn)象
隔離級別 是否存在臟讀 是否存在不可重復(fù)讀 是否存在幻讀
Read UnCommitted Y Y Y
Read Committed N Y Y
Repeated Read N N Y
Serializable N N N
臟讀舉例:
  1. Mary的原工資為1000, 財(cái)務(wù)人員將Mary的工資改為了8000(但未提交事務(wù))
  2. Mary讀取自己的工資 ,發(fā)現(xiàn)自己的工資變?yōu)榱?000鸿秆,歡天喜地酌畜!
  3. 而財(cái)務(wù)發(fā)現(xiàn)操作有誤,回滾了事務(wù),Mary的工資又變?yōu)榱?000像這樣,Mary記取的工資數(shù)8000是一個(gè)臟數(shù)據(jù)卿叽。
不可重復(fù)讀:
  1. 在事務(wù)1中桥胞,Mary 讀取了自己的工資為1000,操作并沒有完成
  2. 在事務(wù)2中,這時(shí)財(cái)務(wù)人員修改了Mary的工資為2000,并提交了事務(wù).
  3. 在事務(wù)1中考婴,Mary 再次讀取自己的工資時(shí)贩虾,工資變?yōu)榱?000
幻讀

目前工資為1000的員工有10人。

  1. 事務(wù)1,讀取所有工資為1000的員工沥阱。
  2. 這時(shí)事務(wù)2向employee表插入了一條員工記錄缎罢,工資也為1000
  3. 事務(wù)1再次讀取所有工資為1000的員工 共讀取到了11條記錄,

本文就是用hibernate考杉,然后修改mysql數(shù)據(jù)庫中的隔離級別來重現(xiàn)上述現(xiàn)象策精,以此來深刻體會事務(wù)的隔離性

實(shí)體類:

    package entity;
    
    import java.util.Date;
    
    public class User {
        private int id;
        private String userName;
        private Date addtime;
    
        public Date getAddtime() {
            return addtime;
        }
    
        public void setAddtime(Date addtime) {
            this.addtime = addtime;
        }
    
        public User(){}
    
        
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }   
    }

映射文件:

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    <hibernate-mapping>
        <class name="entity.User" table="_user" >
            <id name="id">
                <generator class="native" />
            </id>
            <property name="userName" />
            <property name="addtime" type="time" />
        </class>    
    </hibernate-mapping>

單元測試:

package entity;

import java.util.Date;
import java.util.List;

import org.hibernate.classic.Session;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;

import junit.framework.TestCase;
import util.hibernateUtil;

/**
 * 測試目的: 1. 不同的數(shù)據(jù)庫實(shí)現(xiàn)鎖(隔離)的機(jī)制不同,在不同機(jī)制下崇棠,重現(xiàn)臟讀咽袜,不可重復(fù)讀,幻讀等現(xiàn)象 2. 利用phpunit實(shí)現(xiàn)多事務(wù)爭搶同一數(shù)據(jù)現(xiàn)象
 * 3. 修改mysql數(shù)據(jù)庫隔離級別
 * 
 * @author arkulo
 *
 */

public class TestTransaction extends TestCase {

    // 1. 改變數(shù)據(jù)庫隔離級別枕稀,至Read UnCommitted
    // 查詢mysql數(shù)據(jù)庫的隔離級別:select @@global.tx_isolation,@@tx_isolation;
    // 修改mysql數(shù)據(jù)庫當(dāng)前會話隔離級別:set session transaction isolation level read
    // uncommitted;
    //
    // 問題:在數(shù)據(jù)庫中修改的時(shí)候询刹,該session會話級別的隔離是不行的,只能改global級別的隔離
    // 這個(gè)函數(shù)是查詢當(dāng)前數(shù)據(jù)庫的隔離級別
    // 給user表中初始化一條數(shù)據(jù)
    public void getACID() {
        Session session = null;
        try {
            session = hibernateUtil.getSession();
            session.beginTransaction();

            List res = session.createSQLQuery("select @@tx_isolation").list();
            for (Object i : res) {
                System.out.println("隔離級別:" + i.toString());
            }

            User user = new User();
            user.setUserName("王蕊");
            user.setAddtime(new Date());
            // id策略是native抽莱,在save之后就立刻發(fā)出sql語句范抓,這里設(shè)斷點(diǎn)停住骄恶,不提交commit
            session.save(user);

            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        } finally {
            hibernateUtil.closeSession(session);
        }

    }

    // 重現(xiàn)臟讀:在一個(gè)事務(wù)未提交的時(shí)候食铐,另外一個(gè)事務(wù)就可以讀取到該事務(wù)中的新增數(shù)據(jù)
    // 2. 啟動第一個(gè)進(jìn)程做斷點(diǎn),到save后停止僧鲁,啟動另外一個(gè)進(jìn)程虐呻,直接執(zhí)行,看看能不能讀到數(shù)據(jù)庫中的數(shù)據(jù)
    public void test1() {
        // getACID();
        System.out.println("-------------------------------------");
        Session session = null;
        try {
            session = hibernateUtil.getSession();
            session.beginTransaction();

            User user = (User) session.load(User.class, 1);
            System.out.println("用戶名:" + user.getUserName());
            user.setUserName("李四");
            session.update(user);

            session.flush();
            // 單步斷點(diǎn)寞秃,停在這里不提交斟叼,但是需要flush
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        } finally {
            hibernateUtil.closeSession(session);
        }
    }

    // 再啟動一個(gè)進(jìn)程,運(yùn)行這個(gè)函數(shù)去讀取user表中的第一條數(shù)據(jù)春寿,這時(shí)候就會發(fā)現(xiàn)
    // 1. 隔離級別是READ UNCOMMITED朗涩,讀取事務(wù)并沒有被阻塞
    // 2. 這里讀出的用戶名是李四,屬于臟讀
    public void test1_1() {
        Session session = null;
        try {
            session = hibernateUtil.getSession();
            session.beginTransaction();

            User user = (User) session.get(User.class, 1);
            System.out.println("用戶名:" + user.getUserName());

            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        } finally {
            hibernateUtil.closeSession(session);
        }
    }

    // ------------------------------------------------------

    // 這里展示不可重復(fù)讀問題
    public void test2() {
        Session session = null;
        try {
            session = hibernateUtil.getSession();
            session.beginTransaction();

            User user = (User) session.get(User.class, 1);
            System.out.println("用戶名:" + user.getUserName());

            // 因?yàn)間et函數(shù)會緩存對象到一級緩存绑改,這里需要把緩存清理掉
            // 如果不清理谢床,系統(tǒng)會采用緩存中的數(shù)據(jù)兄一,而下面的get函數(shù)就不會發(fā)出sql語句查詢
            session.evict(user);

            // 在這里斷點(diǎn)停住,當(dāng)另外一個(gè)用戶的事務(wù)結(jié)束之后识腿,這里再繼續(xù)
            User user1 = (User) session.get(User.class, 1);
            // 這里讀出的用戶名是李四3龈铩!
            System.out.println("用戶名:" + user1.getUserName());

            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        } finally {
            hibernateUtil.closeSession(session);
        }
    }

    // 當(dāng)test2第一個(gè)查詢執(zhí)行完畢之后,test2_1修改用戶名渡讼,然后再執(zhí)行test2中的第二個(gè)查詢
    public void test2_1() {
        Session session = null;
        try {
            session = hibernateUtil.getSession();
            session.beginTransaction();

            User user = (User) session.get(User.class, 1);
            user.setUserName("李四");

            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        } finally {
            hibernateUtil.closeSession(session);
        }
    }

    // ------------------------------------------------------

    // 以下部分是幻讀的重現(xiàn)
    public void test3() {
        Session session = null;
        try {
            session = hibernateUtil.getSession();
            session.beginTransaction();

            // 以下是兩種查詢總數(shù)的方式,都會用到uniqueResult()
            // int acount = ((Long)session.createQuery("select count(*) from
            // User").uniqueResult()).intValue();
            int acount = ((Number) session.createCriteria("entity.User").setProjection(Projections.rowCount())
                    .uniqueResult()).intValue();
            System.out.println("數(shù)量:" + acount);

            // 因?yàn)椴樵兊牟皇菍ο蟪审铮虼瞬粫灰患壘彺嬲瓜洌@里斷點(diǎn)停住
            // 這里打印的數(shù)據(jù)庫記錄比上面多了1條,好像是幻覺一樣
            int acount1 = ((Number) session.createCriteria("entity.User").setProjection(Projections.rowCount())
                    .uniqueResult()).intValue();
            System.out.println("數(shù)量:" + acount1);

            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        } finally {
            hibernateUtil.closeSession(session);
        }
    }

    // 新起一個(gè)事務(wù)做插入
    public void test3_1() {
        Session session = null;
        try {
            session = hibernateUtil.getSession();
            session.beginTransaction();

            User user = new User();
            user.setUserName("李四");
            user.setAddtime(new Date());
            session.save(user);

            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        } finally {
            hibernateUtil.closeSession(session);
        }
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蹬昌,一起剝皮案震驚了整個(gè)濱河市析藕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凳厢,老刑警劉巖账胧,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異先紫,居然都是意外死亡治泥,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門遮精,熙熙樓的掌柜王于貴愁眉苦臉地迎上來居夹,“玉大人,你說我怎么就攤上這事本冲∽贾” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵檬洞,是天一觀的道長狸膏。 經(jīng)常有香客問我,道長添怔,這世上最難降的妖魔是什么湾戳? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮广料,結(jié)果婚禮上砾脑,老公的妹妹穿的比我還像新娘。我一直安慰自己艾杏,他們只是感情好韧衣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般畅铭。 火紅的嫁衣襯著肌膚如雪萧求。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天顶瞒,我揣著相機(jī)與錄音夸政,去河邊找鬼。 笑死榴徐,一個(gè)胖子當(dāng)著我的面吹牛守问,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坑资,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼耗帕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了袱贮?” 一聲冷哼從身側(cè)響起仿便,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎攒巍,沒想到半個(gè)月后嗽仪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柒莉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年闻坚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兢孝。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡窿凤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跨蟹,到底是詐尸還是另有隱情雳殊,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布窗轩,位于F島的核電站夯秃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏品姓。R本人自食惡果不足惜寝并,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一箫措、第九天 我趴在偏房一處隱蔽的房頂上張望腹备。 院中可真熱鬧,春花似錦斤蔓、人聲如沸植酥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽友驮。三九已至漂羊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間卸留,已是汗流浹背走越。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耻瑟,地道東北人旨指。 一個(gè)月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像喳整,于是被迫代替她去往敵國和親谆构。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容