zoukankan      html  css  js  c++  java
  • Hibernate-一级缓存session

    hibernate提供的一级缓存

      hibernate是一个线程对应一个session,一个线程可以看成一个用户。也就是说session级缓存(一级缓存)只能给一个线程用,别的线程用不了,一级缓存就是和线程绑定了。

      hibernate一级缓存生命周期很短,和session生命周期一样,一级缓存也称session级的缓存或事务级缓存。

    如果tb事务提交或回滚了,我们称session就关闭了,生命周期结束了。

    实例:

    (hibernate使用load查询)

    //同一个session中,发出两次load方法查询
    Student student = (Student)session.load(Student.class, 1);
    System.out.println("student.name=" + student.getName());
    //不会发出查询语句,load使用缓存 student = (Student)session.load(Student.class, 1); System.out.println("student.name=" + student.getName());

    第二次查询第一次相同的数据,第二次load方法就是从缓存里取数据,不会发出sql语句到数据库里查询。

    (hibernate使用get查询)

    //同一个session,发出两次get方法查询
    Student student = (Student)session.get(Student.class, 1);
    System.out.println("student.name=" + student.getName());
    
    //不会发出查询语句,get使用缓存
    student = (Student)session.get(Student.class, 1);
    System.out.println("student.name=" + student.getName());

    第二次查询第一次相同的数据,第二次不会发出sql语句查询数据库,而是到缓存里取数据。


    (hibernate使用iterate查询)

    //同一个session,发出两次iterate查询实体对象
    Iterator iter = session.createQuery("from Student s where s.id<5").iterate();
    while (iter.hasNext()) {
      Student student = (Student)iter.next();
      System.out.println(student.getName());
    }
    System.out.println("--------------------------------------");
    
    //它会发出查询id的语句,但不会发出根据id查询学生的语句,因为iterate使用缓存
    iter = session.createQuery("from Student s where s.id<5").iterate();
    while (iter.hasNext()) {
      Student student = (Student)iter.next();
      System.out.println(student.getName());
    }

    第二次查询第一次相同的数据,第二次不会发出sql语句查询数据库,而是到缓存里取数据。

    说到iterater查询就要立刻想起:iterater查询在没有缓存的情况下会有N+1的问题。

      执行上面代码查看控制台的sql语句,第一次iterate查询会发出N+1条sql语句,第一条sql语句查询所有的id,然后根据id查询实体对象,有N个id就发出N条语句查询实体。

      第一次发出N+1条sql语句,第二次还是发出了N+1条sql语句。因为一级缓存只缓存实体对象,tb不会缓存普通属性,所以第二次还是发出sql查询语句。

    -----------

    (测试两个session之间不能共享缓存数据)

    //两个session,每个session发出一个load方法查询实体对象
    try {
      session = HibernateUtils.getSession();
      session.beginTransaction();
      Student student = (Student)session.load(Student.class, 1);
      System.out.println("student.name=" + student.getName());
      session.getTransaction().commit();
    }catch(Exception e) {
      e.printStackTrace();
      session.getTransaction().rollback();
    }finally {
      HibernateUtils.closeSession(session);
    }

    第二个session调用load方法

    try {
      session = HibernateUtils.getSession();
      session.beginTransaction();
      Student student = (Student)session.load(Student.class, 1);
      //会发出查询语句,session间不能共享一级缓存数据
      //因为他会伴随着session的消亡而消亡
      System.out.println("student.name=" + student.getName());
      session.getTransaction().commit();  
    }catch(Exception e) {
      e.printStackTrace();
      session.getTransaction().rollback();  
    }finally {  
      HibernateUtils.closeSession(session);  
    }

    第一个session的load方法会发出sql语句查询实体对象,第二个session的load方法也会发出sql语句查询实体对象。因为session间不能共享一级缓存的数据,所以第二个session的load方法查询相同的数据还是要到数据库中查询,因为它找不到第一个session里缓存的数据。

    (session的save支持缓存)

    //同一个session,先调用save方法再调用load方法查询刚刚save的数据
    Student student = new Student();
    student.setName("张三");
    //save方法返回实体对象的id
    Serializable id = session.save(student);
    student = (Student)session.load(Student.class, id);
    //不会发出查询语句,因为save支持缓存
    System.out.println("student.name=" + student.getName());

    先save保存实体对象,再用load方法查询刚刚save的实体对象,则load方法不会发出sql语句到数据库查询的,而是到缓存里取数据,因为save方法也支持缓存。当然前提是同一个session。

    大批量的数据添加(内存的溢出)

    for (int i=0; i<100; i++) {
      Student student = new Student();
      student.setName("张三" + i);
      session.save(student);
      //每20条更新一次
      if (i % 20 == 0) {
        session.flush();
        //清除缓存的内容
        session.clear();
      }
    }

    大批量数据添加时,会造成内存溢出的,因为save方法支持缓存,每save一个对象就往缓存里放,如果对象足够多内存肯定要溢出。一般的做法是先判断一下save了多少个对象,如果save了20个对象就对缓存手动的清理缓存,这样就不会造成内存溢出。

    注意:清理缓存前,要手动调用flush方法同步到数据库,否则save的对象就没有保存到数据库里。

    注意:大批量数据的添加还是不要使用hibernate,这是hibernate弱项。可以使用jdbc(速度也不会太快,只是比hibernate好一点),或者使用工具产品来实现,比如oracle的Oracle SQL Loader,导入数据特别快。 

  • 相关阅读:
    最小公倍数
    记录输入的数字并输出
    带时间的进度条
    进度条二:普通版
    文件的练习
    《k8s权威指南》读书笔记
    面试后如何判断岗位是否符合自己要求
    索引的三星系统
    使用Mysql分区表对数据库进行优化
    MySQL索引的基本操作
  • 原文地址:https://www.cnblogs.com/hwaggLee/p/4502207.html
Copyright © 2011-2022 走看看