知识点
相关文章
更多最近更新
更多hibernate一级缓存及N+1问题
2019-03-03 14:40|来源: 领悟书生
Hibernate的一级缓存是由Session提供的,因此它只存在于Session的生命周期中,也就是当Session关闭的时候该Session所管理的一级缓存也会立即被清除。
@Test public void test01() { Session session = null; try { /** * 此时会发出一条sql取出所有的学生信息 */ session = HibernateUtil.openSession(); List<Student> ls = session .createQuery("from Student") .setFirstResult(0).setMaxResults(50).list(); Iterator<Student> stus = ls.iterator(); for(;stus.hasNext();) { Student stu = stus.next(); System.out.println(stu.getName()); } /** * id=1的Student对象已经在session的缓存(一级缓存)中, * 此时就不会发sql去取Student */ Student stu = (Student)session.load(Student.class, 1); System.out.println(stu.getName()+",---"); } catch (Exception e) { e.printStackTrace(); } finally { HibernateUtil.close(session); } try { session = HibernateUtil.openSession(); /** * 上一个Session已经关闭,此时又得重新取Student */ Student stu = (Student)session.load(Student.class, 1); System.out.println(stu.getName()+",---"); } catch (Exception e) { e.printStackTrace(); } finally { HibernateUtil.close(session); } }
典型的N+1问题
如果使用iterator方法返回列表,对于hibernate而言,它仅仅只是发出取id列表的sql
在查询相应的具体的某个学生信息时,会发出相应的SQL去取学生信息,这就是典型的N+1问题
@Test public void test02() { Session session = null; try { session = HibernateUtil.openSession(); Iterator<Student> stus = session.createQuery("from Student") .setFirstResult(0).setMaxResults(50).iterate(); for(;stus.hasNext();) { Student stu = stus.next(); System.out.println(stu.getName()); } } catch (Exception e) { e.printStackTrace(); } finally { HibernateUtil.close(session); } }
存在iterator的原因是
有可能会在一个session中查询两次数据,如果使用list每一次都会把所有的对象查询上来,而是要iterator仅仅只会查询id,此时所有的对象已经存储在一级缓存(session的缓存)中,可以直接获取。如下例:
/** * 此时会发出一条sql取出所有的学生信息 */ session = HibernateUtil.openSession(); List<Student> ls = session.createQuery("from Student") .setFirstResult(0).setMaxResults(50).list(); Iterator<Student> stus = ls.iterator(); for(;stus.hasNext();) { Student stu = stus.next(); System.out.println(stu.getName()); } /** * 使用iterate仅仅只会去Student的id, * 此时Student的数据已经在缓存中,所以不会在出现N+1 */ stus = session.createQuery("from Student") .setFirstResult(0).setMaxResults(50).iterate(); for(;stus.hasNext();) { Student stu = stus.next(); System.out.println(stu.getName()); }
如果上面不使用iterate,而直接使用list,会查出很多数据
/** * 此时会发出一条sql取出所有的学生信息 */ session = HibernateUtil.openSession(); List<Student> ls = session.createQuery("from Student") .setFirstResult(0).setMaxResults(50).list(); Iterator<Student> stus = ls.iterator(); for(;stus.hasNext();) { Student stu = stus.next(); System.out.println(stu.getName()); } /** * 会发出SQL取完整的学生对象,占用内存相对较多 */ ls = session.createQuery("from Student") .setFirstResult(0).setMaxResults(50).list(); stus = ls.iterator(); for(;stus.hasNext();) { Student stu = stus.next(); System.out.println(stu.getName()); }
本文链接:hibernate一级缓存及N+1问题,领悟书生学习笔记,转载请注明出处http://www.656463.com/article/419
相关问答
更多-
如果hibernate和spring集成,hibernate里的一级缓存怎么管理呢?[2021-10-08]
spring只是把它的hibernate的sessionfactory封装了一下,而session还是由hibernate管理的 所以说有没spring hibernate对一级缓存还是一样做的 -
什么时候用到了hibernate的一级缓存[2020-11-01]
Hibernate的一级缓存其实就是Session内置的一个Map,用来缓存它操作过的实体对象,对象的主关键字ID是Map的key,实体对象就是对应的值。所以,一级缓存是以实体对象为单位进行存储的,访问时也是以实体为单位的(直接访问属性是不能使用缓存的),并且要求使用主关键字ID来进行访问。 一级缓存是由Session提供的,所以它只存在于Session的生命周期中,当程序调用save(),update(),saveorupdate()等方法以及调用查询接口list,filter,iterate时,如果se ... -
hibernate一级缓存和二级缓存的区别[2023-10-16]
缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高了应用的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。 缓存的介质一般是内存,所以读写速度很快。但如果缓存中存放的数据量非常大时,也会用硬盘作为缓存介质。缓存的实现不仅仅要考虑存储的介质,还要考虑到管理缓存的并发访问和缓存数据的生命周期。 Hibernate的缓存包括Session的缓存和SessionFactory的缓存,其中Se ... -
把它归结为1个查询是非常困难的(即我不知道一个可移植的解决方案),但将其归结为2个查询(不论n)是非常简单的: Criteria criteria = this.getSession().createCriteria(Mother.class); criteria.addOrder(Order.asc("title")) .setMaxResults(details.getMaxRows()) .setFirstResult(details.getStartResult()) .se ...
-
1.1)一级缓存 一级缓存总是与Session对象关联。 默认情况下,Hibernate使用此缓存。 在这里,它处理一个事务,一个又一个,意味着不会处理一个事务多次。 主要是减少了在给定事务中需要生成的SQL查询的数量。 这不是在事务中完成每次修改之后进行更新,而是仅在事务结束时更新事务。 1.2)二级缓存 第二级缓存始终与Session Factory对象关联。 在运行事务时,在它之间加载会话工厂级别的对象,以便这些对象将可用于整个应用程序,而不会绑定到单个用户。 由于对象已经加载到缓存中,所以每当查询返 ...
-
是。 第一级缓存仅用于会话。 第二个getList()调用将转到数据库。 编辑:第二个getList()调用将转到数据库,在第二级缓存的情况下,以及由mR_fr0g在他的答案中指出。 Yes. The 1st level cache is for the session only. The second getList() call will go to the database. EDIT: The second getList() call will go to the database, in the ...
-
Hibernate:一级缓存(Hibernate: 1st level cache)[2023-10-19]
您是否只是为了自己的利益和学习而使用这些信息? 就我所知,在任何一种标准的JPA批准方法中,您将无法访问这些信息。 但是,如果你正在使用Hibernate,你可以设置一个断点并深入研究Hibernate的PersistenceContext.java实现,它被称为StatefulPersistenceContext.java。 应该有一个实体和集合的地图加载。 您当然需要Hibernate源代码。 这些字段的名称应该是...... private Map entitiesByKey; private Map ... -
不,Hibernate没有做任何事情来同步会话缓存中的实体的状态与数据库,除非你明确地请求它。 通常这不是问题,因为活动工作通常发生在事务内部,并且事务内的操作不应该看到其他并发事务所做的更改(尽管详细信息取决于隔离级别)。 因此,在这种情况下,Hibernate的行为补充了事务隔离的典型语义。 当实体的状态需要显式同步以反映在同一事务内进行的更改时,也可能会出现这种情况。 这可能是由批量更新查询或数据库触发器的执行引起的。 在这种情况下,您需要通过调用refresh()明确请求这样的同步。 No, Hib ...
-
不 - 我相信你不能覆盖EAGER映射,既不使用查找,查询也不能使用条件查询。 例如,请参阅StackOverflow线程 。 覆盖LAZY虽然容易。 hbm.xml文件,fetch =“select”表示所有关系表的一对多 ...似乎是一个糟糕的默认选择 - 这就是为什么相反的是Hibernate自己的默认选择。 通常,在映射中保持低位和懒惰是一种很好的做法,并在您渴望时使用查询。 No - I believe you cannot override EAGER mappings, neither usi ...
-
您需要使用@Transactional注释测试方法或使用Spring TransactionTemplate 。 即使检索实体不需要事务 ,在一个实体中对多个调用进行分组也会明确指定持久性上下文边界(它应该从哪里开始以及它应该在哪里结束)。 要复制第一级缓存,您需要在整个测试方法中使用相同的UnitOfWork,并使用@Transactional注释测试将启动一个事务(一个绑定到当前执行的持久性上下文事务的Spring逻辑事务,该事务与实际的物理数据库事务相关) 。 当测试方法结束时,事务通常会回滚,因此不 ...