简介
Hibernate通过延迟加载在真正需要使用数据时从数据库表中加载,这样可以加快程序运行速度,减少内存开销。Hibernate在以下三种情况会默认使用延迟加载:
- load方法延迟加载;
- 实体属性延迟加载;
- 集合属性延迟加载。
数据库示例
示例数据库包含3张表:
- 文章表(post),存储文章标题、内容,主键为自增整数;
- 分类表(category),存储分类名称,主键为自增整数;
- 标签表(score),存储标签名称,主键为自增整数;
- 文章标签表(post_tag),存储文章包含的标签,使用文章id和标签id作为联合主键。
每篇文章属于一个分类,并包含多个标签。
延迟加载示例
创建Post类与post表映射。
Post.java: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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58package com.magicwt.bean;
import java.util.Set;
public class Post {
//与主键id映射
private int id;
//与字段title映射
private String title;
//与字段content映射
private String content;
//关联Category实体
private Category category;
//关联Tag实体
private Set<Tag> tags;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public Set<Tag> getTags() {
return tags;
}
public void setTags(Set<Tag> tags) {
this.tags = tags;
}
}
Post.hbm.xml:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<hibernate-mapping>
<class name="com.magicwt.bean.Post" table="post">
<id name="id">
<column name="id" sql-type="int" not-null="true"/>
</id>
<property name="title">
<column name="title" sql-type="varchar" length="16"/>
</property>
<property name="content">
<column name="content" sql-type="text" length="65535"/>
</property>
<!-- 实体属性,对应于一个分类实体 -->
<one-to-one name="category" class="com.magicwt.bean.Category" constrained="true"/>
<!-- 集合属性,对应于多个标签实体 -->
<set name="tags" table="post_tag" inverse="true">
<key column="post_id"/>
<many-to-many column="tag_id" class="com.magicwt.bean.Tag"/>
</set>
</class>
</hibernate-mapping>
创建Category类与category表映射。
Category.java: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
26package com.magicwt.bean;
public class Category {
//与主键id映射
private int id;
//与字段name映射
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Category.hbm.xml:1
2
3
4
5
6
7
8
9
10
11
12
13
14
<hibernate-mapping>
<class name="com.magicwt.bean.Category" table="category">
<id name="id">
<column name="id" sql-type="int" not-null="true"/>
</id>
<property name="name">
<column name="name" sql-type="varchar" length="16"/>
</property>
</class>
</hibernate-mapping>
创建Tag类与tag表映射。
Tag.java: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
26package com.magicwt.bean;
public class Tag {
//与主键id映射
private int id;
//与字段name映射
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Tag.hbm.xml:1
2
3
4
5
6
7
8
9
10
11
12
13
14
<hibernate-mapping>
<class name="com.magicwt.bean.Tag" table="tag">
<id name="id">
<column name="id" sql-type="int" not-null="true"/>
</id>
<property name="name">
<column name="name" sql-type="varchar" length="16"/>
</property>
</class>
</hibernate-mapping>
Hibernate配置文件hibernate.cfg.xml:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://xxx.xxx.xxx.xxx:3306/test</property>
<property name="connection.username">xxx</property>
<property name="connection.password">xxx</property>
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<mapping resource="mapper/Category.hbm.xml"/>
<mapping resource="mapper/Post.hbm.xml"/>
<mapping resource="mapper/Tag.hbm.xml"/>
</session-factory>
</hibernate-configuration>
load方法延迟加载
创建测试类Test:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21package com.magicwt;
import com.magicwt.bean.Post;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
public class Test {
public static void main(String[] args) {
SessionFactory sessionFactory = (new Configuration()).configure().buildSessionFactory();
Session session = sessionFactory.openSession();
//使用load方法加载id为1的文章
Post post = (Post)session.load(Post.class, Integer.valueOf(1));
//输出post实例的类名
System.out.println("class of post: " + post.getClass().getCanonicalName());
//取出文章标题并输出
System.out.println(post.getTitle());
}
}
执行main方法,输出如下:
class of post: com.magicwt.bean.Post$$EnhancerByCGLIB$$70a21d16
Hibernate:
select
post0_.id as id1_0_,
post0_.title as title1_0_,
post0_.content as content1_0_
from
post post0_
where
post0_.id=?
title of post: 测试标题1
从中可以看出,load方法返回的是通过动态代理实现的代理类实例。通过断点可以观察到,执行“post.getTitle()”前,post实例各属性都是默认值(null或0),执行“post.getTitle()”后,才真正从数据库表中取出数据并输出。
将load方法修改为get方法并重新执行,输出如下:
Hibernate:
select
post0_.id as id1_0_,
post0_.title as title1_0_,
post0_.content as content1_0_
from
post post0_
where
post0_.id=?
class of post: com.magicwt.bean.Post
title of post: 测试标题1
从中可以看出,get方法返回的是Post类实例。通过断点可以观察到,get方法返回的post实例各属性已从数据库表中取出对应的值,get方法并没有延迟加载。
实体属性延迟加载
修改测试类Test:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23package com.magicwt;
import com.magicwt.bean.Post;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
public class Test {
public static void main(String[] args) {
SessionFactory sessionFactory = (new Configuration()).configure().buildSessionFactory();
Session session = sessionFactory.openSession();
//使用load方法加载id为1的文章
Post post = (Post)session.get(Post.class, Integer.valueOf(1));
//输出post实例的类名
System.out.println("class of post: " + post.getClass().getCanonicalName());
//输出post实例category属性的类名
System.out.println("class of category: " + post.getCategory().getClass().getCanonicalName());
//输出分类名称
System.out.println("name of category:" + post.getCategory().getName());
}
}
执行main方法,输出如下:
Hibernate:
select
post0_.id as id1_0_,
post0_.title as title1_0_,
post0_.content as content1_0_
from
post post0_
where
post0_.id=?
class of post: com.magicwt.bean.Post
class of category: com.magicwt.bean.Category$$EnhancerByCGLIB$$483428f6
Hibernate:
select
category0_.id as id0_0_,
category0_.name as name0_0_
from
category category0_
where
category0_.id=?
name of category: 测试分类1
从中可以看出,get方法返回的post实例,其属性category是代理类实例,并没有真正读取category表中的数据,只有在输出分类名称时,才读取category表中的数据。
集合属性延迟加载
修改测试类Test: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
26package com.magicwt;
import com.magicwt.bean.Post;
import com.magicwt.bean.Tag;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
public class Test {
public static void main(String[] args) {
SessionFactory sessionFactory = (new Configuration()).configure().buildSessionFactory();
Session session = sessionFactory.openSession();
//使用load方法加载id为1的文章
Post post = (Post)session.get(Post.class, Integer.valueOf(1));
//输出post实例的类名
System.out.println("class of post: " + post.getClass().getCanonicalName());
//输出post实例tags属性的类名
System.out.println("class of tags: " + post.getTags().getClass().getCanonicalName());
//输出标签名称
for (Tag tag : post.getTags()) {
System.out.println("name of tag: " + tag.getName());
}
}
}
执行main方法,输出如下:
Hibernate:
select
post0_.id as id1_0_,
post0_.title as title1_0_,
post0_.content as content1_0_
from
post post0_
where
post0_.id=?
class of post: com.magicwt.bean.Post
class of tags: org.hibernate.collection.PersistentSet
Hibernate:
select
tags0_.post_id as post1_1_,
tags0_.tag_id as tag2_1_,
tag1_.id as id3_0_,
tag1_.name as name3_0_
from
post_tag tags0_
left outer join
tag tag1_
on tags0_.tag_id=tag1_.id
where
tags0_.post_id=?
name of tag: 测试标签1
name of tag: 测试标签2
从中可以看出,get方法返回的post实例,其属性tags是PersistentSet实例,并没有真正读取tag表中的数据,只有在输出标签名称时,才读取tag表中的数据。