6.flush刷新(hibernate笔记)

一、复习数据库隔离级别

隔离级别 是否存在脏读 是否存在不可重复读 是否存在幻读
Read UnCommitted Y Y Y
Read Committed N Y Y
Repeated Read N N Y
Serializable N N N

说明:

  • Read UnCommitted表示未提交读,即在未提交之前我们就可以读到数据,也就是脏读。Read Committed表示提交之后才能读到数据。Repeated Read表示可重复读。Serializable表示序列化读。

  • 不可重复读的意思是说我们第一次读到数据,但是别人也能读到,而当别人将数据改变之后我刷新再读,发现两次读到的数据不一样了,这就是不可重复读。不能避免别人修改或添加。

  • 幻读的意思是第一次根据相应的条件查出来了10条数据,但是此时别人又录入了数据,我再次查询发现同样条件可以查出来20条数据,两次结果不一样了。就是说避免不了别人添加,但是可以避免别人修改。

  • 隔离级别由低到高,一般使用Read Committed这个级别,这个级别存在不可重复读取,但是我们可以使用相应的锁就可以避免了,而最后一种使用的极少,因为是序列化了,变成串行的了,并发性太差了。MySQL的默认隔离级别是Repeated Read,Oracle的默认级别是Read Committed

二、Session flush测试(工程hibernate_session_flush

SessionFlushTest.java

package junit.test;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import cn.itcast.model.User1;
import cn.itcast.model.User2;
import cn.itcast.model.User3;
import cn.itcast.util.HibernateUtils;

public class SessionFlushTest {
    
    //测试uuid主键生成策略
    @Test
    public void testSave1(){
        Session session = HibernateUtils.getSession();
        Transaction tx = null;
        try{
            tx = session.beginTransaction();
            User1 user1 = new User1();
            user1.setName("张三1");
            user1.setPassword("123");
            user1.setCreateTime(new Date());
            user1.setExpireTime(new Date());
            //这里因为user1的主键生成策略采用的是id,所以调用save方法后,只是将user1纳入到session的管理
            //不会发出insert语句,但是id已经生成,session中existsInDatabase状态为false,表示在数据库中还不存在
            session.save(user1);
            
            //调用flush时hibernate会清理缓存,发出sql语句
            //那么我们可以看到flush过的数据并且session中existsInDatabase状态为true,但是在数据库中看不到
            //因为MySQL的默认隔离级别是Read Committed
            //session.flush();
            
            //默认情况下,commit操作会先执行flush清理缓存,所以不用显示的调用flush
            //commit后数据是无法回滚的。
            tx.commit();
            
            
        }catch(Exception e){
            e.printStackTrace();
            tx.rollback();
        }finally{
            HibernateUtils.closeSession(session);
        }
    }
    
    //测试native主键生成策略
    @Test
    public void testSave2(){
        Session session = null;
        Transaction tx = null;
        try {
            session = HibernateUtils.getSession();
            tx = session.beginTransaction();
            User2 user2 = new User2();
            user2.setName("张三");
            user2.setPassword("123");
            user2.setCreateTime(new Date());
            user2.setExpireTime(new Date());
            
            //因为主键生成策略是native,所以调用save方法后,将立刻发出sql语句,返回由数据库生成的id
            //纳入session的管理,修改了session中existsInDatabase的状态为true
            //如果数据库的隔离级别设置为Read Committed,那么那么我们是看不到save添加过的数据
            session.save(user2);
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            tx.rollback();
        }finally{
            HibernateUtils.closeSession(session);
        }
    }
    
    //测试uuid主键生成策略
    @Test
    public void testSave3(){
        Session session = HibernateUtils.getSession();
        Transaction tx = null;
        try{
            tx = session.beginTransaction();
            User1 user1 = new User1();
            user1.setName("张三2");
            user1.setPassword("123");
            user1.setCreateTime(new Date());
            user1.setExpireTime(new Date());

            session.save(user1);
            
            //这里如果我们事先进行手工刷新,会将user1对象保存到数据库中,将sess中的insertions中的user1对象
            //清除,并且设置existsInDatabase为true,那么此时是可以成功提交的。
            //session.flush();
            
            //将user1对象从session中逐出,即session的EntityEntries对象中逐出
            session.evict(user1);
            
            //此时是无法成功提交的,因为hibernate在清理缓存时,在session的insertions集合中取出user1对象进行
            //insert操作后,需要更新EntityEntries对象中的existsInDatabase为true,而我们已经将user1对象逐出
            //所以找不到相关数据,无法更新,抛出异常。
            tx.commit();
            
            
        }catch(Exception e){
            e.printStackTrace();
            tx.rollback();
        }finally{
            HibernateUtils.closeSession(session);
        }
    }
    
    //测试native主键生成策略
    @Test
    public void testSave4(){
        Session session = null;
        Transaction tx = null;
        try {
            session = HibernateUtils.getSession();
            tx = session.beginTransaction();
            User2 user2 = new User2();
            user2.setName("张三");
            user2.setPassword("123");
            user2.setCreateTime(new Date());
            user2.setExpireTime(new Date());

            session.save(user2);
            
            //这里就算将对象逐出,但是这种主键生成策略中在使用save方法后默认是执行flush的,即
            //已经将数据保存到数据库中了,还是可以成功提交的
            session.evict(user2);
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            tx.rollback();
        }finally{
            HibernateUtils.closeSession(session);
        }
    }   
    
    //测试assigned主键生成策略
    @Test
    public void testSave5(){
        Session session = null;
        Transaction tx = null;
        try {
            session = HibernateUtils.getSession();
            tx = session.beginTransaction();
            User3 user3 = new User3();
            user3.setId("001");
            user3.setName("张三");

            session.save(user3);
            
            user3.setName("王五");
            session.update(user3);
            
            User3 user4 = new User3();
            user4.setId("002");
            user3.setName("李四");
            session.save(user4);
            
            /*
             *  Hibernate: insert into _user3 (name, password, createTime, expireTime, id) values (?, ?, ?, ?, ?)
                Hibernate: insert into _user3 (name, password, createTime, expireTime, id) values (?, ?, ?, ?, ?)
                Hibernate: update _user3 set name=?, password=?, createTime=?, expireTime=? where id=?
                hibernate会按照save(insert)、update、delete的顺序提交相关的操作
             * */
            
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            tx.rollback();
        }finally{
            HibernateUtils.closeSession(session);
        }
    }
    
    //测试assigned主键生成策略
    @Test
    public void testSave6(){
        Session session = null;
        Transaction tx = null;
        try {
            session = HibernateUtils.getSession();
            tx = session.beginTransaction();
            User3 user3 = new User3();
            user3.setId("003");
            user3.setName("张三");

            session.save(user3);
            
            user3.setName("王五");
            session.update(user3);
            //如果手动进行刷新,那么会hibernate会按照我们的操作顺序执行相关的操作
            session.flush();
            
            User3 user4 = new User3();
            user4.setId("004");
            user3.setName("李四");
            session.save(user4);

            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            tx.rollback();
        }finally{
            HibernateUtils.closeSession(session);
        }
    }
}

说明:

  • 1.从测试方法1中我们可以看到使用uuid主键生成策略在使用save方法的时候不会发出相关的sql语句,只有在提交的时候清理缓存时才会发出相应的sql语句。当然我们可以在使用save方法之后手动进行flush清理缓存,那样是会发出sql语句的。但是这样即使我们保存在数据库中了,在提交之前还是会因为数据库的隔离级别的问题而看不到数据。

  • 2.从测试方法2中我们可以看到使用native这种主键生成策略在使用save方法时是会立即清理缓存,发出相关sql语句的。同时save方法执行之后提交之前同样是看不到数据的。还要注意此时实体类中id的类型是int型而不是String类型。

  • 3.测试方法3和4再次说明了这个问题,即uuid主键生成方式中不会自动刷新,而native主键生成方式则会自动刷新。

  • 4.测试方法5和6中采用的自己分配主键,此时hibernate会按照save(insert)、update、delete的顺序提交相关的操作,而不会自动刷新,当然如果我们使用手动刷新,那么hibernate会按照我们自己的顺序执行相关的操作。

最后:

  • flush方法主要做清理缓存和执行sql这两个工作;
  • session在下列情况下执行flush:1.默认在事务提交的时候;2.显式调用flush方法时;3.在执行查询前,如:iterate
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容