笔记---Hibernate的“N+1”问题和一级缓存

如图例子01:

import com.xing.hibernate_hql_xml.entity.Student;
import com.xing.util.HibernateUtil;
import org.hibernate.Session;
import org.junit.Test;

import java.util.Iterator;
import java.util.List;

/**
 * Created by Administrator on 2016/8/5.
 */
public class TestCache {

    @Test
    public void test01(){
        Session session = null ;
        try {
            session = HibernateUtil.openSession() ;
            session.beginTransaction();

            List<Student> stus = session.createQuery("from Student").list() ;
            Iterator<Student> student = stus.iterator();;
            while(student.hasNext()){
                System.out.println("学生:"+student.next().getName()+"---");
            }
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session!=null) session.getTransaction().rollback();
        } finally {
            HibernateUtil.closeSession(session);
        }
    }
}

控制台发出一条sql,且获取所有的学生信息:

Hibernate: select student0_.id as id1_2_, student0_.name as name2_2_, student0_.sex as sex3_2_, student0_.cid as cid4_2_ from t_student student0_

例子02:

    @Test
    public void test02(){
        Session session = null ;
        try {
            session = HibernateUtil.openSession() ;
            session.beginTransaction();

            Iterator<Student> stus = session.createQuery("from Student").iterate() ;
            while(stus.hasNext()){
                System.out.println("学生:"+stus.next().getName()+"---");
            }
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session!=null) session.getTransaction().rollback();
        } finally {
            HibernateUtil.closeSession(session);
        }
    }


①如例子,如果使用iterator获取列表信息执行后,控制台首先生成一条sql,然后获取每个学生的时候都会各自生成一条sql语句;
②对Hiberante而言,它只是生成了获取对应id列表的sql,这就是所谓的”N+1”问题。

③而出现iterator的原因是因为有可能会在一个session中查询两次数据,如果使用list每次都会把所有的对象查询一次,而iterator仅仅只会查询id,此时所有的对象已经存在于一级缓存session中了,只要session不关闭,可以直接获取

Hibernate: select student0_.id as col_0_0_ from t_student student0_
Hibernate: select student0_.id as id1_2_2_, student0_.name as name2_2_2_, student0_.sex as sex3_2_2_, student0_.cid as cid4_2_2_, classroom1_.id as id1_0_0_, classroom1_.name as name2_0_0_, classroom1_.grade as grade3_0_0_, classroom1_.sp_id as sp_id4_0_0_, special2_.id as id1_1_1_, special2_.name as name2_1_1_, special2_.type as type3_1_1_ from t_student student0_ left outer join t_class_room classroom1_ on student0_.cid=classroom1_.id left outer join t_special special2_ on classroom1_.sp_id=special2_.id where student0_.id=?
学生:谢林---
Hibernate: select student0_.id as id1_2_2_, student0_.name as name2_2_2_, student0_.sex as sex3_2_2_, student0_.cid as cid4_2_2_, classroom1_.id as id1_0_0_, classroom1_.name as name2_0_0_, classroom1_.grade as grade3_0_0_, classroom1_.sp_id as sp_id4_0_0_, special2_.id as id1_1_1_, special2_.name as name2_1_1_, special2_.type as type3_1_1_ from t_student student0_ left outer join t_class_room classroom1_ on student0_.cid=classroom1_.id left outer join t_special special2_ on classroom1_.sp_id=special2_.id where student0_.id=?
学生:郭唯---

例子03,观察下面的例子以及控制台生成的sql

    @Test
    public void test03(){
        Session session = null ;
        try {
            session = HibernateUtil.openSession() ;
            session.beginTransaction();

            List<Student> students = session.createQuery("from Student").list() ;
            Iterator<Student> stus = students.iterator();;
            while(stus.hasNext()){
                System.out.println("学生:"+stus.next().getName()+"---");
            }
            //***********************************************************
            stus =  session.createQuery("from Student").iterate() ;
            while(stus.hasNext()){
                System.out.println("学生:"+stus.next().getName()+"---");
            }
            //***********************************************************
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session!=null) session.getTransaction().rollback();
        } finally {
            HibernateUtil.closeSession(session);
        }
    }

做了两次查询, 生成两条sql语句,注意第二条,由于第一次查询已经将查询结果也就是所有的学生信息都已经保存在session对象中了,在第二次用iterator方法获取学生列表信息的时候仅仅只是查了对应的id,不会再出现n+1问题了

Hibernate: select student0_.id as id1_2_, student0_.name as name2_2_, student0_.sex as sex3_2_, student0_.cid as cid4_2_ from t_student student0_

Hibernate: select student0_.id as col_0_0_ from t_student student0_

例子04:

    @Test
    public void test04(){
        Session session = null ;
        try {
            session = HibernateUtil.openSession() ;
            session.beginTransaction();

            List<Student> students = session.createQuery("from Student").list() ;
            Iterator<Student> stus = students.iterator();;
            while(stus.hasNext()){
                System.out.println("学生:"+stus.next().getName()+"---");
            }
            //***********************************************************
            students =  session.createQuery("from Student").list() ;
            stus = students.iterator();;
            while(stus.hasNext()){
                System.out.println("学生:"+stus.next().getName()+"---");
            }
            //***********************************************************
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session!=null) session.getTransaction().rollback();
        } finally {
            HibernateUtil.closeSession(session);
        }
    }

它也会生成两条sql,如下,

Hibernate: select student0_.id as id1_2_, student0_.name as name2_2_, student0_.sex as sex3_2_, student0_.cid as cid4_2_ from t_student student0_
Hibernate: select student0_.id as id1_2_, student0_.name as name2_2_, student0_.sex as sex3_2_, student0_.cid as cid4_2_ from t_student student0_

例子04中同样查询两次,但是第二次查询的时候生成的是一条查询学生对象的sql,相比例子03的获取信息方法占用内存较多

观察如下例子05

    @Test
    public void test05(){
        Session session = null ;
        try {
            session = HibernateUtil.openSession() ;
            session.beginTransaction();

            List<Student> stus = session.createQuery("from Student").list() ;
            Iterator<Student> student = stus.iterator();;
            while(student.hasNext()){
                System.out.println("学生:"+student.next().getName()+"---");
            }
            Student stu = (Student) session.load(Student.class,1289);
            System.out.println("学生02:"+stu.getName());
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session!=null) session.getTransaction().rollback();
        } finally {
            HibernateUtil.closeSession(session);
        }
    }


上面例子中第一次做了query查询,第二次做了load获取指定id的学生信息,但是后台hibernate只生成了一条sql,因为第一次做query查询的时候,已经把所有的学生的信息都已经存在session缓存中了,只要session缓存不关闭,当再使用load加载的时候,只需要去session一级缓存中去找获取就可以了,不需要再发sql了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值