Hibernate Session方法中get()和load()具有相同的懒加载效果?

去年刚接触Hibernate时,看到一个题目,做了分析并写了文档。今天整理电脑所幸看到了,感觉很有趣,于是写篇博客记录下来,顺便做个简单的分享。

问题的提出

/**
Hibernate的Session.get()是不支持懒加载的,但是load方法支持,但是当进行如下操作的时候出现如下现象,请进行相关验证和研究并解释其原因。
**/
    //1.定义一个Customer类
    public class Customer implements java.io.Serializable {
    //其中的orders集合需要进行懒加载
    private Set<Orders> orders=new HashSet<Orders>();
    //2.在Customer.hbm.xml中进行如下配置
    <set name="orders" lazy="true">
          <key column="cid"/>
          <one-to-many class="com.Orders"/>
        </set>
    //3.此时若执行一下代码:
    Session session=HibernateSessionFactory.getSession();
    Customer cs=(Customer)session.1·get(Customer.class,"c001"); 
    System.out.println("------------------");
    System.out.println(cs.getOrders());
/**
这段程序会触发两条SQL,一条用于加载Customer的,一条加载orders的,get本身不支持懒加载,应该是两条SQL一起执行,但是实际输出却是这样:
Hibernate: select customer0_.id as id1_0_, customer0_.name as name1_0_, customer0_.age as age1_0_, customer0_.sex as sex1_0_, customer0_.city as city1_0_ from simple.customer customer0_ where customer0_.id=?
------------------
Hibernate: select orders0_.cid...
输出的 SQL语句显示,只有当运行到cs.getOrders时才到数据库中查询,这与懒加载有何不一样?并且get返回的实体不是代理类,那么get方法是采用和何种机制触发第二条SQL语句呢?
**/

在开发的过程中,解决问题最关键的就是问题重现。搭建了一个简单Spring-SpringMVC-Hibernate框架后,立马做了配置,模拟出了问题的场景。运行之后,竟然真的和问题描述的一样,哈哈,问题的发布者没骗我。问题重现,接下来就是解决问题。在debug了无数遍的Hibernate源码后,终于得到了答案。以下就是解决问题的大致过程。


问题分析定位

开启懒加载的情况下,无论是get还是load方法,访问students集合属性时,都会触发查询数据库的SQL语句。假设问题就出现在集合属性中,或者Hibernate对集合属性有特殊处理。证明假设,在访问数据库获取到实体对象后,对实体中的非集合属性进行访问:

Session session =HibernateUtil.getSession();

Grade grade =(Grade)session.get(Grade.class, 3);

System.out.println("-----分割线-----");

System.out.println(grade.getGname());

此时查看控制台输出信息:

Hibernate: select grade0_.gid asgid1_0_0_, grade0_.gname as gname2_0_0_, grade0_.gdesc as gdesc3_0_0_ fromgrade grade0_ where grade0_.gid=?

-----分割线-----

Java三班

从控制台的输出可以看出,在访问持久化类对象的非集合属性时,不会触发查询语数据库的SQL语句。同时,采用load加载数据,访问非集合属性时也不触发第二条查询数据的SQL。将问题定位到实体类的集合属性中。


详细过程

1)测试非集合属性

设置lazy=true的情况下,使用get方法查询id为1的班级信息,此时控制台打印出第一条查询的SQL语句。当访问返回结果中的班级名称属性(gname)时,没有进行对数据库进行查询,而是直接输出了我们需要的信息。如图1所示:

图1

从运行结果可看出,当我们访问持久化类的非集合属性时,是能够获得需要的数值,但是并不触发查询数据库的SQL语句。这也是由于get方法不支持懒加载而决定的,首次查询就到数据库中进行查询并返回结果信息。

2)集合属性测试

在懒加载开启的情况下,同样使用get进行测试。获取Session,查询id为1的班级信息。数据库中有对应的数据,返回一个班级实体对象。获取集合属性的类别并输出,如图2所示:

图2

从控制台的输出可看到,返回的结果实体中,集合属性(students)的类型已经不是持久化类(Grade)中声明的Set类型,而是改写过的类型。当访问students内容时,控制台输出了查询学生信息的SQL语句。因为Java中使用输出行数直接输出对象时,Java虚拟机会自动调用该对象的toString()方法,查看PersistentSet类中toString()方法的实现可发现在调用父类的toString()方法之前,显示调用read()方法,如图3:

图3

而在read()方法中,先是对改类是否初始化(initialized)做出了判断。如果没有初始化,则分别在一级缓存、二级缓存、数据库中对相关的信息进行查找。对于首次查询出来的持久化类对象,集合属性都是没有初始化过initialized属性值为false,并且一级缓存、二级缓存中也是没有相应的信息,所以进行了数据库查询,打印了查询语句。

在Hibernate提供的PersistentSet类中,一些需要取值的方法例如equal(),toArray(),iterator()等方法,都是优先调用了read()。这就保证了get在初次在数据库中查询时,集合属性不会完全初始化而在使用时才初始化。

3)List测试

通过查阅资料,发现Hibernate对集合属性有延迟加载机制,其中包括的集合类型有:Set、List、Map、SortedSet、SortedMap等接口,而不能用对应子类。查看4.3.0版本org/hibernate/collection/ internal目录下,五个类有着对应改写的持久类。于是对List集合进行测试。

新建实体类teacher,有姓名(tname)和性别(sex)属性,主键为tid其余配置与学生实体(student)一致。在班级实体类中添加List属性teachers,插入教师信息,(‘李泉’,‘男’),班级为3。

编写测试代码,查询班级号为3的班级信息,输出教师信息如图4:

图4

从控制台输出的结果看,对于List集合,Hibernate所采用的处理方式和Set集合和是一样的。


问题解答

1)延迟加载

在懒加载开启的情况下,get也成了类似于懒加载的操作,其原因并不是get特殊的机制,而是由于持久化类中集合属性。Hibernate在加载持久化类信息时运用了一项最常用的技术延迟载。实体的集合属性默认会被延迟加载,实体所关联的默认也会被默认加载。考虑到系统的开销和内存消耗,Hibernate在初始化某个持久化类的时候不会对其集合属性进行抓取,因为有的时候是不使用持久化类的集合属性的。如果持久化类的集合属性所包含的内容较小,延迟加载的效果就不会很明显。如果集合属性数据量达到百万级别甚至更大,延迟加载的效果就会体现得特别明显。对于像hibernate这样的框架来说,无论是从对提升系统的性能来说、还是从本身的发展来看,这都是需要考虑并且完善的。

2)直接原因

由于采用get方法加载数据库返回的持久化类对象中,集合属性为PersistentSet类的对象,所以在调用输出语句输出该集合的信息时,自动调用了PersistentSet类的toString()方法输出,该方法在实现时先是调用read()方法,同样的打开read()方法的实现就发现:该方法首先对PersistentSet对象的初始化属性initialized进行判断。由于是第一次查询持久化类的相关信息,所以initialized为false,并到Session中查找,查询顺序为一级缓存、二级缓存、数据库。因为是第一次查询的缘故,一级缓存和二级缓存中都不会查询到相关的信息。于是到数据库中进行查询,打印出了查询的SQL语句,也正是问题中的第二条SQL语句。对于Set、List、Map、SortedSet、SortedMap五种集合属性,都是有相同特性。至于是什么触发了第二条SQL语句,那就是输出语在输出对象时,调用了toString()方法,而此时对象的toString()方法已经被改写,因此触发了一系列的操作。


文章中没有debug部分的截图,如果有兴趣的话需要动手实践一番。文章内多数说明用了截图,比较粗糙。以上所描述的内容如果有不恰当的地方,欢迎指出,这对我是最大的帮助!





猜你喜欢

转载自blog.csdn.net/qq_36247781/article/details/80788366
今日推荐