`

一些关于Hibernate延迟加载的误区

 
阅读更多

首先是第一个误区:延迟加载只能作用于关联实体
看到这个是不是在想:非关联实体延迟加载有什么用?
为了解答上面这个问题,我们可以先考虑另一个问题: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中开启二级缓存再次运行代码即可

分享到:
评论

相关推荐

    Hibernate延迟加载以及利用Spring

    ### Hibernate延迟加载以及利用Spring #### 一、Hibernate延迟加载概念与原理 在理解Hibernate的延迟加载机制之前,我们首先需要了解什么是延迟加载。延迟加载(Lazy Loading)是一种设计模式,其核心思想是在真正...

    hibernate 延迟加载深入剖析

    ### Hibernate延迟加载深入剖析 #### 一、概述 在现代软件开发中,特别是与数据库交互的应用场景下,Hibernate作为一款流行的Java持久层框架,提供了多种高效处理数据的技术。其中,延迟加载(Lazy Loading)是一...

    什么是hibernate延迟加载

    详细介绍hibernate延迟加载,对hibernate初学者有一定的帮助

    hibernate延迟加载解决

    ### Hibernate延迟加载详解 #### 一、什么是延迟加载? 延迟加载是一种优化技术,在软件开发中广泛应用于各种场景,尤其在数据库交互中尤为重要。其核心思想是仅在确实需要某个资源时才加载它,而非一开始就加载...

    Hibernate 延迟加载剖析与代理模式应用

    《Hibernate延迟加载与代理模式解析》 在Java的持久化框架Hibernate中,延迟加载(Lazy Load)是一项重要的优化策略,其核心目标是提高系统性能,减少内存占用,避免不必要的数据库交互。延迟加载允许我们在需要...

    Hibernate延迟加载机制.zip

    Hibernate延迟加载机制.zip

    hibernate 延迟加载.docx

    【hibernate 延迟加载】 在Java的持久化框架Hibernate中,延迟加载(Lazy Loading)是一种优化数据库访问性能的技术。它允许我们在需要时才加载关联的对象,而不是在初始查询时就一次性加载所有数据。这有助于减少...

    Hibernate延迟加载介绍.doc

    《Hibernate延迟加载详解》 Hibernate作为Java领域中的一个强大的对象关系映射框架,提供了许多优化数据库操作的策略,其中之一便是延迟加载(Lazy Loading)。延迟加载机制旨在减少不必要的性能消耗,只在真正需要...

    Hibernate 延迟加载

    ### Hibernate延迟加载详解 #### 什么是Hibernate延迟加载? 在探讨Hibernate延迟加载之前,我们先简要回顾一下Hibernate框架。Hibernate是一种流行的Java持久层框架,它提供了对象关系映射(ORM)的功能,允许...

    Hibernate的延迟加载

    ### Hibernate的延迟加载详解 #### 实体对象的延迟加载 延迟加载是Hibernate中一项关键的优化技术,旨在减少不必要的数据库交互,从而提升应用程序的性能。其实现原理在于,只有当确实需要访问某个对象或集合的...

    hibernate延迟加载技术详细解

    ### Hibernate 延迟加载技术详解 #### 一、概述 在 Hibernate 框架中,延迟加载(Lazy Loading)是一种非常重要的优化技术,它能够有效地减少数据库查询次数,提高应用性能。通常,在多对多或者一对多的关系中,...

    hibernate延迟加载

    ### Hibernate延迟加载详解 #### 一、概述 Hibernate 是一种常用的 Java 持久层框架,它能够将 Java 应用程序与底层数据库之间建立桥梁,简化数据访问逻辑。为了提高应用程序性能,Hibernate 提供了多种优化手段,...

    Hibernate延迟加载

    Hibernate延迟加载是ORM框架Hibernate中的一个重要特性,它旨在提高应用程序的性能和效率。延迟加载(Lazy Loading)策略允许我们仅在需要访问一个对象或其属性时才从数据库加载它们,而不是在初始加载实体时就一次...

    Hibernate集合属性的延迟加载.doc

    在 Hibernate 框架中,延迟加载(Lazy Loading)是一种优化数据访问性能的重要技术。它允许我们只在真正需要数据时才从数据库加载,避免一次性加载大量数据导致的内存消耗和性能瓶颈。当我们处理与实体相关的集合...

    Flex 与 Hibernate 的延迟加载问题

    在开发Flex与Hibernate集成的应用时,延迟加载(Lazy Loading)是一个常见的挑战,因为Flex客户端无法直接理解和处理Hibernate的延迟加载机制。延迟加载是一种优化策略,它允许关联的对象在真正需要时才被加载,而...

    Hibernate延迟加载案例 (多: 一: 一,附代码)

    本案例主要探讨的是Hibernate的延迟加载(Lazy Loading)机制,这是一种优化数据库访问性能的重要策略。延迟加载允许我们在需要数据时才去加载,而不是在初始化对象时一次性加载所有关联数据,从而减少了内存消耗和...

Global site tag (gtag.js) - Google Analytics