6.hibernate的flush函數(shù)

hibernate的flush和操作方式

操作hibernate不是直接操作數(shù)據(jù)庫(kù),hibernate在其中又加入一層緩存,而正是因?yàn)檫@層緩存的原因,很多時(shí)候我們需要認(rèn)真去分析我們的sql執(zhí)行順序,不要引起意外情況,下面這張圖是描述了uuid和native兩種主鍵策略下,各種操作的流程:

hibernate的flush原理.png
  1. 當(dāng)執(zhí)行save/update/delete等操作后,hibernate并不是直接生成sql語(yǔ)句執(zhí)行,而是先把操作存入actionQueue的相應(yīng)隊(duì)列中,然后再把當(dāng)前操作對(duì)象緩存到persistenceContext中
  2. 只有主鍵需要數(shù)據(jù)庫(kù)生成時(shí),在做save等操作的時(shí)候,才會(huì)直接發(fā)出sql語(yǔ)句去數(shù)據(jù)庫(kù)中執(zhí)行
  3. 在commit或者flush執(zhí)行時(shí),要檢查User對(duì)象,persistenceContext的User對(duì)象緩存以及actionQueue中的對(duì)象引用,數(shù)據(jù)上是否一致,沒(méi)有出現(xiàn)單獨(dú)被修改的情況,否則會(huì)拋出異常

actionQueue和persistenceContext的具體位置請(qǐng)參看下圖,網(wǎng)絡(luò)上說(shuō)的有些路徑和我的不一致,有可能是版本的問(wèn)題,請(qǐng)自行檢查。

1.png
2.png
3.png

接下來(lái)就是對(duì)各種主鍵策略的代碼測(cè)試,具體測(cè)試內(nèi)容,請(qǐng)看代碼中的注釋:

實(shí)體類(lèi):

    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>

單元測(cè)試:

    package entity;
    
    import java.util.Date;
    
    import org.hibernate.classic.Session;
    
    import junit.framework.TestCase;
    import util.hibernateUtil;
    
    /**
     * 測(cè)試目的:
     * 
     * 不同的id策略(uuid,native,assigned)下,save之后commit之前,系統(tǒng)會(huì)不會(huì)自動(dòng)清理緩存,包括各種異常情況測(cè)試
     * 
     * @author arkulo
     *
     */
    
    public class TestOneToOne extends TestCase {
        // uuid情況下,普通方式
        public void test1() {
            Session session = null;
            try {
                session = hibernateUtil.getSession();
                session.beginTransaction();
    
                User user = new User();
                user.setUserName("王蕊");
                user.setAddtime(new Date());
                // 在save后,hibernate會(huì)在session的insertion中添加一條操作記錄,同時(shí)會(huì)在persistenceContext中添加一個(gè)user緩存對(duì)象
                // 但這時(shí)候,并沒(méi)有發(fā)出sql語(yǔ)句,系統(tǒng)不會(huì)自動(dòng)flush
                session.save(user);
                // 只有commit的時(shí)候,系統(tǒng)會(huì)自動(dòng)flush,并且發(fā)出sql語(yǔ)句,真正實(shí)現(xiàn)持久化
                session.getTransaction().commit();
            } catch (Exception e) {
                e.printStackTrace();
                session.getTransaction().rollback();
            } finally {
                hibernateUtil.closeSession(session);
            }
        }
    
        // 主鍵策略是uuid的情況下,各種緩存引起的問(wèn)題
        public void test2() {
            Session session = null;
            try {
                session = hibernateUtil.getSession();
                session.beginTransaction();
    
                User user = new User();
                user.setUserName("王蕊");
                user.setAddtime(new Date());
                // 在save后,hibernate會(huì)在session的insertion中添加一條操作記錄,同時(shí)會(huì)在persistenceContext中添加一個(gè)user緩存對(duì)象
                // 但這時(shí)候,并沒(méi)有發(fā)出sql語(yǔ)句,系統(tǒng)不會(huì)自動(dòng)flush
                session.save(user);
    
                // 這個(gè)時(shí)候如果設(shè)置id為null,會(huì)提示錯(cuò)誤:
                // identifier of an instance of entity.User was altered from
                // 402881e65b956dbc015b956e88d00001 to null
                // 這表示在persistenceContext中登記的user對(duì)象,和實(shí)際的user對(duì)象在執(zhí)行checkId操作的時(shí)候?qū)Σ簧狭耍虼似爻霎惓?                // user.setId(null);
    
                // 如果這里選擇刪除當(dāng)前對(duì)象緩存,其實(shí)刪除的就是persistenceContext的對(duì)象,等到commit的時(shí)候,insertion操作檢查persistenceContext
                // 中的對(duì)象緩存時(shí),發(fā)現(xiàn)沒(méi)有了,就會(huì)曝出異常:
                // org.hibernate.AssertionFailure: possible nonthreadsafe access to
                // session
                // session.evict(user);
    
                // 如果我們更新一下user對(duì)象,然后在新建一個(gè)新的user1對(duì)象,看看實(shí)際的sql執(zhí)行過(guò)程,是不是和我們代碼的順序不一樣
                // Hibernate: insert into User (userName, addtime, id) values (?, ?,
                // ?)
                // Hibernate: insert into User (userName, addtime, id) values (?, ?,
                // ?)
                // Hibernate: update User set userName=?, addtime=? where id=?
                // user.setUserName("謹(jǐn)言");
                // session.update(user);
                // User user1 = new User();
                // user1.setUserName("李四");
                // user1.setAddtime(new Date());
                // session.save(user1);
    
                session.getTransaction().commit();
            } catch (Exception e) {
                e.printStackTrace();
                session.getTransaction().rollback();
            } finally {
                hibernateUtil.closeSession(session);
            }
        }
    
        // native情況下,普通方式
        public void test3() {
            Session session = null;
            try {
                session = hibernateUtil.getSession();
                session.beginTransaction();
    
                User user = new User();
                user.setUserName("王蕊");
                user.setAddtime(new Date());
                // 當(dāng)id策略是native的時(shí)候,save函數(shù)必須從數(shù)據(jù)庫(kù)中獲取主鍵,因此當(dāng)save后就會(huì)直接發(fā)出sql語(yǔ)句,而不是等待flush
                // 這時(shí)候查看對(duì)象的狀態(tài)existsInDatabase是true,并且insertion隊(duì)列中已經(jīng)沒(méi)有待執(zhí)行的操作了
                session.save(user);
    
                // 這里執(zhí)行commit的時(shí)候,就不會(huì)引發(fā)sql語(yǔ)句的執(zhí)行了
                session.getTransaction().commit();
            } catch (Exception e) {
                e.printStackTrace();
                session.getTransaction().rollback();
            } finally {
                hibernateUtil.closeSession(session);
            }
        }
    
        // 主鍵策略是native的情況下,各種緩存引起的問(wèn)題
        public void test4() {
            Session session = null;
            try {
                session = hibernateUtil.getSession();
                session.beginTransaction();
    
                User user = new User();
                user.setUserName("王蕊");
                user.setAddtime(new Date());
                session.save(user);
    
                // 如果我們?cè)俅螄L試插入-更新-插入操作,看看sql的執(zhí)行順序是什么樣的?結(jié)果發(fā)現(xiàn)和uuid的時(shí)候一樣,還是先執(zhí)行了insert,然后再執(zhí)行update
                // 這是怎么回事呢?
                // 原因是:只有save是立刻發(fā)出sql語(yǔ)句的,因?yàn)樗枰獢?shù)據(jù)庫(kù)分配主鍵,但是update不需要立刻去數(shù)據(jù)庫(kù)執(zhí)行,因此他還是按照hibernate的方式執(zhí)行
                // 因此,當(dāng)執(zhí)行到update函數(shù)的時(shí)候,它還是會(huì)被暫時(shí)記錄到更新操作隊(duì)列中的,當(dāng)commit的時(shí)候,按照插入,更新,刪除等順序,一個(gè)個(gè)的執(zhí)行。
                // Hibernate: insert into _user (userName, addtime) values (?, ?)
                // Hibernate: insert into _user (userName, addtime) values (?, ?)
                // Hibernate: update _user set userName=?, addtime=? where id=?
                // 如果我們更新一下user對(duì)象,然后在新建一個(gè)新的user1對(duì)象,看看實(shí)際的sql執(zhí)行過(guò)程,是不是和我們代碼的順序不一樣
                // user.setUserName("謹(jǐn)言");
                // session.update(user);
                // User user1 = new User();
                // user1.setUserName("李四");
                // user1.setAddtime(new Date());
                // session.save(user1);
    
                session.getTransaction().commit();
            } catch (Exception e) {
                e.printStackTrace();
                session.getTransaction().rollback();
            } finally {
                hibernateUtil.closeSession(session);
            }
        }
    
        // assigned情況下,普通方式
        public void test5() {
            Session session = null;
            try {
                session = hibernateUtil.getSession();
                session.beginTransaction();
    
                User user = new User();
                user.setUserName("王蕊");
                user.setAddtime(new Date());
                user.setId(111);
                session.save(user);
                // 這里采用assigned的主鍵策略,因?yàn)橹麈I是自己在代碼中指定的,因此不需要去數(shù)據(jù)庫(kù)中生成主鍵,因此當(dāng)save之后,并沒(méi)有
                // 直接發(fā)出sql,而是按照管理在insertion中添加了操作記錄,并且在persistenceContext生成了緩存對(duì)象
    
                // 這里執(zhí)行commit的時(shí)候,就不會(huì)引發(fā)sql語(yǔ)句的執(zhí)行了
                session.getTransaction().commit();
            } catch (Exception e) {
                e.printStackTrace();
                session.getTransaction().rollback();
            } finally {
                hibernateUtil.closeSession(session);
            }
        }
    
        // 主鍵策略是assigned的情況下,各種緩存引起的問(wèn)題
        public void test6() {
            Session session = null;
            try {
                session = hibernateUtil.getSession();
                session.beginTransaction();
    
                User user = new User();
                user.setUserName("王蕊");
                user.setAddtime(new Date());
                user.setId(111);
                session.save(user);
    
                // 這個(gè)時(shí)候如果設(shè)置id為null,commit的時(shí)候系統(tǒng)會(huì)拋異常??梢钥吹?,insetion和緩存中的id沒(méi)有發(fā)生變化,但是實(shí)際user對(duì)象的id變?yōu)榱?
                // 這在執(zhí)行checkid操作的時(shí)候會(huì)報(bào)錯(cuò),系統(tǒng)就會(huì)拋出異常。
                // user.setId(0);
    
                // 如果這里選擇刪除當(dāng)前對(duì)象緩存,其實(shí)刪除的就是persistenceContext的對(duì)象,等到commit的時(shí)候,insertion操作檢查persistenceContext
                // 中的對(duì)象緩存時(shí),發(fā)現(xiàn)沒(méi)有了,就會(huì)曝出異常:
                // org.hibernate.AssertionFailure: possible nonthreadsafe access to
                // session
                // session.evict(user);
    
                // 這里和uuid的方式一樣,在save和update的時(shí)候都是不發(fā)sql語(yǔ)句的,只有到了commit的時(shí)候,一次性的按照插入,更新,刪除的順序執(zhí)行
                // Hibernate: insert into User (userName, addtime, id) values (?, ?,
                // ?)
                // Hibernate: insert into User (userName, addtime, id) values (?, ?,
                // ?)
                // Hibernate: update User set userName=?, addtime=? where id=?
                // user.setUserName("謹(jǐn)言");
                // session.update(user);
                // User user1 = new User();
                // user1.setUserName("李四");
                // user1.setAddtime(new Date());
                // session.save(user1);
    
                session.getTransaction().commit();
            } catch (Exception e) {
                e.printStackTrace();
                session.getTransaction().rollback();
            } finally {
                hibernateUtil.closeSession(session);
            }
        }
    
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Hibernate: 一個(gè)持久化框架 一個(gè)ORM框架 加載:根據(jù)特定的OID,把一個(gè)對(duì)象從數(shù)據(jù)庫(kù)加載到內(nèi)存中OID...
    JHMichael閱讀 2,095評(píng)論 0 27
  • 這部分主要是開(kāi)源Java EE框架方面的內(nèi)容,包括Hibernate、MyBatis、Spring、Spring ...
    雜貨鋪老板閱讀 1,570評(píng)論 0 2
  • Hibernate是一個(gè)開(kāi)放源代碼的對(duì)象關(guān)系映射框架,它對(duì)JDBC進(jìn)行了非常輕量級(jí)的對(duì)象封裝,它將POJO與數(shù)據(jù)庫(kù)...
    蘭緣小妖閱讀 1,286評(píng)論 1 18
  • 本文包括: 1、CRM 項(xiàng)目的整體介紹 2、Hibernate 框架概述 3、Hibernate 快速入門(mén) 4、H...
    廖少少閱讀 3,531評(píng)論 9 66
  • 一位心理學(xué)教育工作者發(fā)現(xiàn),越來(lái)越多的年輕人出現(xiàn)心理問(wèn)題,他們無(wú)法融入正常的生活。他們或抑郁或想自殺。甚至越來(lái)越呈現(xiàn)...
    Clouds_liu閱讀 580評(píng)論 0 0

友情鏈接更多精彩內(nèi)容