Hibernate之get()和load()的区别

Hibernate中根据Id单条查询获取对象的方式有两种,分别是get()和load(),来看一下这两种方式的区别。

查询时机:

1.get()和load()的区别是在于查询主表的时候get是立即加载,load是延时加载

lazy延时加载:https://www.cnblogs.com/lovlife/articles/12421117.html

而lazy这个属性,是针对查询从表的时候延迟加载还是不延迟加载。

get()直接发出sql、load()用的时候发出sql,这个不涉及从表的事儿,也就是说如果如果你没设置lazy或lazy=“false”,无论是get()还是load()方法,当它发出sql语句的时候,你会发现都会去查询所关联的表。

1. 对于Hibernate get方法,只查询主表是立即加载,在有从表的情况下根据映射文件上类级别的lazy属性的配置(默认为true),分情况讨论

Hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查询数据库,数据库中没有就返回null。在表有从表查询情况下

(1) lazy="true" 的时候,你查询主表,调用get()方法,你会发现它的sql,仅仅查询的主表,而当我们调用了主表pojo中对应的从表pojo的全局变量的时候,它又会发出一条sql,这时在查询从表。

(2) lazy="false"的时候,你查询主表,调用get()方法,你会发现它直接发出两条sql,一个是查询的主表,一个是查询的从表。这里多提一嘴,lazy和fetch可以配合使用,如果设置了fetch=“join” ,它会将这两条sql,通过外链接合并成一条sql从而提高效率。

2. Hibernate load方法加载实体对象的时候,根据映射文件上类级别的lazy属性的配置(默认为true),分情况讨论:(有无从表情况一样)

(1)若为true,则首先在Session缓存中查找,看看该id对应的对象是否存在,不存在则使用延迟加载,返回实体的代理类对象(该代理类为实体类的子类,由CGLIB动态生成)。等到具体使用该对象(除获取OID以外)的时候,再查询二级缓存和数据库才会发出sql语句,从数据库中去查询需要的数据,若仍没发现符合条件的记录,则会抛出一个ObjectNotFoundException。

(2)若为false,就跟Hibernate get方法查找顺序一样立即执行sql语句,只是最终若没发现符合条件的记录,则会抛出一个ObjectNotFoundException。

 

返回值:

get()返回的是查询出来的实体对象,而load()查询出来的是一个目标实体的代理对象。

 

查询结果为空时:

get()抛出NullPointerException

load()抛出ObjectNotFoundException

 

1. get()

使用get()来根据ID进行单条查询:

1
User user=session.get(User.class"1");

当get()方法被调用的时候就会立即发出SQL语句:

1
2
3
4
5
6
7
8
9
10
11
Hibernate:
    select
        user0_.ID as ID1_1_0_,
        user0_.CREATETIME as CREATETI2_1_0_,
        user0_.UPDATETIME as UPDATETI3_1_0_,
        user0_.USERNAME as USERNAME4_1_0_,
        user0_.PASSWD as PASSWD5_1_0_
    from
        USER user0_
    where
        user0_.ID=?

并且返回的对象也是实际的对象:

image

使用get()和普通的单条查询并没有多大的区别。

 

2. load()

当使用load()进行查询的时候情况就变得很不一样了:

1
User user=session.load(User.class"1");

当调用load()方法的时候会返回一个目标对象的代理对象,在这个代理对象中只存储了目标对象的ID值,只有当调用除ID值以外的属性值的时候才会发出SQL查询的。

返回值:

image 

在handler中有一个属性叫做target,保存着被代理的对象:

image 

现在这个位置还是空的呢。

 

当我们尝试下面的代码时:

1
2
User user=session.load(User.class"1");
System.out.println(user.getId());

因为我们只访问了ID属性,这个在代理对象中是已经存在的了,所以并不需要再去数据库中查询,因此并不会发出SQL查询语句。

 

当使用到除ID以外的属性的时候,会发出SQL查询语句,比如尝试执行下面的代码:

1
2
User user=session.load(User.class"1");
System.out.println(user.getUsername());

会发现控制台打印了SQL查询语句:

1
2
3
4
5
6
7
8
9
10
11
Hibernate:
    select
        user0_.ID as ID1_1_0_,
        user0_.CREATETIME as CREATETI2_1_0_,
        user0_.UPDATETIME as UPDATETI3_1_0_,
        user0_.USERNAME as USERNAME4_1_0_,
        user0_.PASSWD as PASSWD5_1_0_
    from
        USER user0_
    where
        user0_.ID=?

这个时候再看代理对象的话,会发现target已经被填充上了:

image 

 

3. Exception

下面的代码会报一个空指针异常:

1
2
User user=session.get(User.class"foobar");
System.out.println(user.getUsername());

上面的这段代码抛出了空指针异常 NPE:

image 

这个是很容易理解的,因为没有查询到的就空指针了嘛。

 

而下面的这段代码则会报一个ObjectNotFoundException异常:

1
2
User user=session.load(User.class"foobar");
System.out.println(user.getUsername());

抛出了ObjectNotFoundException异常:

image

这个就有点奇怪了,这个是因为我们使用load()的时候返回的是一个代理对象,因为这个时候还没有进行查询,所以我们并没有办法确定要查询的对象到底存在不存在,所以使用load()查询的返回值是永远不会为空的,但是呢,当我们试图访问被代理的真实对象的时候,因为这个对象并不存在,所以就抛出了一个ObjectNotFoundException。

还有一种说法是get()是用于不确定对象是否真的存在的情况,所以在查询出来后可以先进行一个空指针判断,而load()方法用于对象一定存在的情况下,不然等会儿使用的时候就可能会抛出ObjectNotFoundException了。

再来看下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Test
public void test_001(){
     
    SessionFactory sessionFactory=new Configuration().configure().buildSessionFactory();
    Session session=sessionFactory.openSession();
     
    User user=null;
     
    try {
        session.beginTransaction();
         
        user=session.load(User.class"foobar");
         
        session.getTransaction().commit();
    catch (Exception e) {
        session.getTransaction().rollback();
        e.printStackTrace();
    }finally{
        try {
            session.close();
        catch (Exception e) {
            e.printStackTrace();
        }
    }
     
    System.out.println(user.getUsername());
     
}

抛出了一个LazyInitializationException:

image

这个是因为我们使用load()查询出来的对象只有在调用完非ID属性的时候才会去查询数据填充进来,但是查询数据的时候是需要依赖产生这个代理对象的那个Session去查询的,当我们将Session关闭后,再试图去访问非ID属性,它正打算拿着自己依赖的Session去数据库查询,一看Session竟然被关闭了,得,干脆抛出一个LazyInitializationException好了。

解决办法就是在查询的时候思考一下,这个对象时候需要在Session关闭之后还能使用得到呢?如果是的话,那么我们使用get()来查询,或者手动调用空访问一个非ID属性让它把数据回填上先。

 

4. 缓存

get()和load()都会使用缓存,都是首先从一级缓存Session中查找,当找不到的时候再去二级缓存中查找,当查询不到的时候get()返回的是null,而load()则返回代理对象。

来看下面这段代码:

1
2
User user1=session.get(User.class"1");
User user2=session.load(User.class"1");

来看一下这两条执行过后这两个变量的值:

image

会发现两个都是真实对象,连load()返回的也是真实对象,并且它们引用的还是同一块对象。

这个是因为get()查询出来ID为1的对象后会将其放入到缓存中,而load()再去查询的时候它会先去缓存中查找,如果缓存中没有的话才会返回代理对象,但是当缓存中已经存在的话就直接将真实对象返回来了。

 
原文章链接:https://www.jianshu.com/p/f1144cd94729
posted @ 2020-03-05 11:20  你猜lovlife  阅读(116)  评论(0)    收藏  举报