博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
hibernate笔记(六)关于懒加载和load()方法之三——误区
阅读量:7112 次
发布时间:2019-06-28

本文共 4014 字,大约阅读时间需要 13 分钟。

hot3.png

首先是第一个误区:延迟加载只能作用于关联实体

看到这个是不是在想:非关联实体延迟加载有什么用?
为了解答上面这个问题,我们可以先考虑另一个问题:Hibernate Session的get和load方法有什么区别?
如果你的回答是:当方法参数为不存在的id时,get会返回null,load会抛出异常,那么恭喜你,进入了第二个误区
如果此时你还想补充一下:load会从缓存中取出数据而get不会,再次恭喜,进入第三个误区

如果你在上面三个误区中有一个踏入了,那么我敢打赌,你一定是被网上那些半吊子的工程师们写的博客给戕害了。。。。

此时是不是很愤怒?这些长久以来你牢记在心的Hibernate的特性原来都是浮云。。。。

呵呵,接下来我们一个个来走出这些误区。

Mop上无图无真相,我们这里无码无真相——不要误会,我是说代码

首先看看第二个误区:当方法参数为数据库不存在的id时,get会返回null,load会抛出异常

如果你现在想说:没错啊,我自己就过,get确实返回了null,load确实抛出了异常。
那么请回答:load是在执行load语句时抛出异常的吗?为什么?如果你答不上来,那么接着看下面的代码吧: 

@Test(expected = IllegalArgumentException.class)  public void 延迟加载() throws Exception {      // 启动      Session session = sessionFactory.openSession();      Transaction tx = session.beginTransaction();        User user = (User)session.load(User.class, 100L);  // 不存在的ID        try {          user.getName();      } catch (ObjectNotFoundException ex) {          // 命中数据库发现没有对象即抛出ObjectNotFoundException异常          throw new IllegalArgumentException("随便抛出一个不可能的异常");      }        tx.commit();      session.close();  }

由这个test case我们可以知道load并不是在执行时就马上抛出不存在数据的异常的(ObjectNotFoundException),这是为什么呢?再看代码:

 

@Test(expected = IllegalArgumentException.class)  public void 延迟加载() throws Exception {      // 启动      Session session = sessionFactory.openSession();      Transaction tx = session.beginTransaction();        User user = (User)session.load(User.class, 100L);  // 不存在的ID        Assert.assertTrue(user instanceof HibernateProxy);        user.getId();  // 由于ID是不被延迟加载的属性,因此不会抛出异常        try {          Hibernate.initialize(user);  // 此时才会触发命中数据库          //user.getName();      } catch (ObjectNotFoundException ex) {          // 命中数据库发现没有对象即抛出ObjectNotFoundException异常          throw new IllegalArgumentException("随便抛出一个不可能的异常");      }        tx.commit();      session.close();  }

看高亮的几行,代码已经把问题说得很清楚了,get和load最大的区别是(假设缓存皆空的情况):get是立即命中数据库去查询这条记录,而load则是直接返回一个代理对象(HibernateProxy)而不命中数据库,换句话来说load是为单个对象进行了延迟加载,如果你不去访问这个对象的除ID外的属性,即使目标记录不存在它也永远都不会抛出异常。由于load不立即命中数据库,它确实有一定几率提高效率

OK,我想上面一段话应该可以解释第一和第二个误区了,那么第三个误区呢?

再看代码

 

@Test  public void get和load一级缓存测试() throws Exception {      // 启动      Session session = sessionFactory.openSession();      Transaction tx = session.beginTransaction();        // 验证load在缓存为空的情况下是否会使得加载的对象过一级缓存      User user1 = (User)session.load(User.class, 1L);  // 存在的ID,此时虽然没有解开Proxy但已经进入缓存      Assert.assertTrue(user1 instanceof HibernateProxy);      Hibernate.initialize(user1);  // 解开Proxy,会触发命中数据库操作      User user3 = (User)session.get(User.class, 1L);      Assert.assertTrue(user3 instanceof HibernateProxy);  // 即使使用get,但由于缓存中存储的是一个Proxy,所以这里得到的也是Proxy      Hibernate.initialize(user3);  // 解开Proxy,但不会命中数据库        // 验证在load一个不存在的ID后,不解开然后get      User user4 = (User)session.load(User.class, 100L);  // 不存在的ID,仍然将Proxy进入缓存      Assert.assertTrue(user4 instanceof HibernateProxy);      //Hibernate.initialize(user3);  // 不解开Proxy      try {          session.get(User.class, 100L);  // 得到Proxy,命中数据库尝试解开Proxy,由于ID不存在因此抛出异常          Assert.fail("ID不存在所以会出错,不会执行本条");      } catch (ObjectNotFoundException ex) {        }        // 清空缓存      session.clear();        // 验证缓存为空的情况下get是否为Proxy      User user6 = (User)session.get(User.class, 1L);  // 命中数据库,直接将组装完成的User实体进入缓存      Assert.assertTrue(!(user6 instanceof HibernateProxy));        // 验证get从缓存中取出对象      User user7 = (User)session.get(User.class, 1L);      Assert.assertTrue(!(user7 instanceof HibernateProxy)); // 缓存中是真实的User对象,get取出的就是真实的User对象        // 验证load是否从一级缓存取数据      User user8 = (User)session.load(User.class, 1L);      Assert.assertTrue(!(user8 instanceof HibernateProxy));  // 缓存中是真实的User对象,load取出的也是真实的User对象        tx.commit();      session.close();  }

相信注释已经足够详细了,打开hibernate.show_sql,总共命中三次数据库(执行SQL),分别在高亮的三行处,其余的全是从缓存中取数据。

而且值得注意的一点是,如果对象是从load加载到缓存中的,那么不论get还是load获取出来的都是一个Proxy,如果没有被解开过,那么get会尝试解开它;如果对象是从get加载到缓存中的,那么load和get取出来都会是真实的实体对象。也就是说,get和load都会从缓存中取出对象,且取出的对象总是保持其第一次加载时的状态(load为Proxy,get为真实对象)

以上代码是一级缓存的验证,想验证二级缓存只需要从Hibernate中开启二级缓存再次运行代码即可

转载于:https://my.oschina.net/java1314/blog/855577

你可能感兴趣的文章
spring-boot使用spring-security进行身份认证(2)
查看>>
Tensorflow catdog-checkpoint
查看>>
学习springBoot(9)RabbitMQ
查看>>
Python爬虫 Selenium初探
查看>>
第二节:Web前端-ASP.NET之C#基础
查看>>
银行卡 验证
查看>>
jQuery源码解析之replaceWith()/unwrap()
查看>>
交叉熵损失随记
查看>>
flask学习笔记之flask-migrate
查看>>
Swift练习题—基础控制流
查看>>
vue-router
查看>>
js基础归纳
查看>>
[译] 2019 年你应该要知道的 11 个 React UI 组件库
查看>>
技术专栏丨从原理到应用,Elasticsearch详解(上)
查看>>
JS学习笔记(一):数据类型判断
查看>>
Netty 源码解析系列-客户端连接接入及读I/O解析
查看>>
基于Spark的机器学习实践 (一) - 初识机器学习
查看>>
什么是散列表(Hash Table)
查看>>
iOS逆向(五)应用场景、基本思路、流程、
查看>>
GUI图形界面
查看>>