6.hibernate的flush函数

hibernate的flush和操作方式

操作hibernate不是直接操作数据库,hibernate在其中又加入一层缓存,而正是因为这层缓存的原因,很多时候我们需要认真去分析我们的sql执行顺序,不要引起意外情况,下面这张图是描述了uuid和native两种主键策略下,各种操作的流程:

hibernate的flush原理.png
  1. 当执行save/update/delete等操作后,hibernate并不是直接生成sql语句执行,而是先把操作存入actionQueue的相应队列中,然后再把当前操作对象缓存到persistenceContext中
  2. 只有主键需要数据库生成时,在做save等操作的时候,才会直接发出sql语句去数据库中执行
  3. 在commit或者flush执行时,要检查User对象,persistenceContext的User对象缓存以及actionQueue中的对象引用,数据上是否一致,没有出现单独被修改的情况,否则会抛出异常

actionQueue和persistenceContext的具体位置请参看下图,网络上说的有些路径和我的不一致,有可能是版本的问题,请自行检查。

1.png
2.png
3.png

接下来就是对各种主键策略的代码测试,具体测试内容,请看代码中的注释:

实体类:

    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 org.hibernate.classic.Session;
    
    import junit.framework.TestCase;
    import util.hibernateUtil;
    
    /**
     * 测试目的:
     * 
     * 不同的id策略(uuid,native,assigned)下,save之后commit之前,系统会不会自动清理缓存,包括各种异常情况测试
     * 
     * @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会在session的insertion中添加一条操作记录,同时会在persistenceContext中添加一个user缓存对象
                // 但这时候,并没有发出sql语句,系统不会自动flush
                session.save(user);
                // 只有commit的时候,系统会自动flush,并且发出sql语句,真正实现持久化
                session.getTransaction().commit();
            } catch (Exception e) {
                e.printStackTrace();
                session.getTransaction().rollback();
            } finally {
                hibernateUtil.closeSession(session);
            }
        }
    
        // 主键策略是uuid的情况下,各种缓存引起的问题
        public void test2() {
            Session session = null;
            try {
                session = hibernateUtil.getSession();
                session.beginTransaction();
    
                User user = new User();
                user.setUserName("王蕊");
                user.setAddtime(new Date());
                // 在save后,hibernate会在session的insertion中添加一条操作记录,同时会在persistenceContext中添加一个user缓存对象
                // 但这时候,并没有发出sql语句,系统不会自动flush
                session.save(user);
    
                // 这个时候如果设置id为null,会提示错误:
                // identifier of an instance of entity.User was altered from
                // 402881e65b956dbc015b956e88d00001 to null
                // 这表示在persistenceContext中登记的user对象,和实际的user对象在执行checkId操作的时候对不上了,因此曝出异常
                // user.setId(null);
    
                // 如果这里选择删除当前对象缓存,其实删除的就是persistenceContext的对象,等到commit的时候,insertion操作检查persistenceContext
                // 中的对象缓存时,发现没有了,就会曝出异常:
                // org.hibernate.AssertionFailure: possible nonthreadsafe access to
                // session
                // session.evict(user);
    
                // 如果我们更新一下user对象,然后在新建一个新的user1对象,看看实际的sql执行过程,是不是和我们代码的顺序不一样
                // 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("谨言");
                // 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());
                // 当id策略是native的时候,save函数必须从数据库中获取主键,因此当save后就会直接发出sql语句,而不是等待flush
                // 这时候查看对象的状态existsInDatabase是true,并且insertion队列中已经没有待执行的操作了
                session.save(user);
    
                // 这里执行commit的时候,就不会引发sql语句的执行了
                session.getTransaction().commit();
            } catch (Exception e) {
                e.printStackTrace();
                session.getTransaction().rollback();
            } finally {
                hibernateUtil.closeSession(session);
            }
        }
    
        // 主键策略是native的情况下,各种缓存引起的问题
        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);
    
                // 如果我们再次尝试插入-更新-插入操作,看看sql的执行顺序是什么样的?结果发现和uuid的时候一样,还是先执行了insert,然后再执行update
                // 这是怎么回事呢?
                // 原因是:只有save是立刻发出sql语句的,因为它需要数据库分配主键,但是update不需要立刻去数据库执行,因此他还是按照hibernate的方式执行
                // 因此,当执行到update函数的时候,它还是会被暂时记录到更新操作队列中的,当commit的时候,按照插入,更新,删除等顺序,一个个的执行。
                // Hibernate: insert into _user (userName, addtime) values (?, ?)
                // Hibernate: insert into _user (userName, addtime) values (?, ?)
                // Hibernate: update _user set userName=?, addtime=? where id=?
                // 如果我们更新一下user对象,然后在新建一个新的user1对象,看看实际的sql执行过程,是不是和我们代码的顺序不一样
                // user.setUserName("谨言");
                // 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的主键策略,因为主键是自己在代码中指定的,因此不需要去数据库中生成主键,因此当save之后,并没有
                // 直接发出sql,而是按照管理在insertion中添加了操作记录,并且在persistenceContext生成了缓存对象
    
                // 这里执行commit的时候,就不会引发sql语句的执行了
                session.getTransaction().commit();
            } catch (Exception e) {
                e.printStackTrace();
                session.getTransaction().rollback();
            } finally {
                hibernateUtil.closeSession(session);
            }
        }
    
        // 主键策略是assigned的情况下,各种缓存引起的问题
        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);
    
                // 这个时候如果设置id为null,commit的时候系统会抛异常。可以看到,insetion和缓存中的id没有发生变化,但是实际user对象的id变为了0
                // 这在执行checkid操作的时候会报错,系统就会抛出异常。
                // user.setId(0);
    
                // 如果这里选择删除当前对象缓存,其实删除的就是persistenceContext的对象,等到commit的时候,insertion操作检查persistenceContext
                // 中的对象缓存时,发现没有了,就会曝出异常:
                // org.hibernate.AssertionFailure: possible nonthreadsafe access to
                // session
                // session.evict(user);
    
                // 这里和uuid的方式一样,在save和update的时候都是不发sql语句的,只有到了commit的时候,一次性的按照插入,更新,删除的顺序执行
                // 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("谨言");
                // 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);
            }
        }
    
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容

  • Hibernate: 一个持久化框架 一个ORM框架 加载:根据特定的OID,把一个对象从数据库加载到内存中OID...
    JHMichael阅读 1,958评论 0 27
  • 这部分主要是开源Java EE框架方面的内容,包括Hibernate、MyBatis、Spring、Spring ...
    杂货铺老板阅读 1,344评论 0 2
  • Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库...
    兰缘小妖阅读 1,195评论 1 18
  • 本文包括: 1、CRM 项目的整体介绍 2、Hibernate 框架概述 3、Hibernate 快速入门 4、H...
    廖少少阅读 3,464评论 9 66
  • 一位心理学教育工作者发现,越来越多的年轻人出现心理问题,他们无法融入正常的生活。他们或抑郁或想自杀。甚至越来越呈现...
    Clouds_liu阅读 498评论 0 0