美文网首页
6.hibernate的flush函数

6.hibernate的flush函数

作者: arkulo | 来源:发表于2017-07-23 21:42 被阅读23次

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);
            }
        }
    
    }

相关文章

网友评论

      本文标题:6.hibernate的flush函数

      本文链接:https://www.haomeiwen.com/subject/otpvzttx.html