何为懒加载
懒加载又称延迟加载,是当在真正需要数据的时候,才真正执行数据加载操作。也就是说,当使用到对象的具体属性时,才进行加载。
举个例子来说,一本书(Books)有很多章节(Chapters),一般加载数据的时候,加载Books的同时会同时把Chapters加载上来,而懒加载就是加载Books的属性时不会将Books中的Chapters立刻加载上来,只有在使用到Chapters中的属性时,才将Chapters加载出来。
@Entity
@Table
public class Books {
private int id;
private String name;
private Set<Chapters> chapters = new HashSet<Chapters>();
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Column
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@OneToMany(targetEntity=Chapters.class,mappedBy="book")
public Set<Chapters> getChapters() {
return chapters;
}
public void setChapters(Set<Chapters> chapters) {
this.chapters = chapters;
}
public Books() {
super();
}
public Books(String name) {
super();
this.name = name;
}
}
@Entity
@Table
public class Chapters {
private int id;
private int number;
private Books book;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Column
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
@ManyToOne(targetEntity=Books.class)
public Books getBook() {
return book;
}
public void setBook(Books book) {
this.book = book;
}
public Chapters() {
super();
}
public Chapters(int number, Books book) {
super();
this.number = number;
this.book = book;
}
}
代理模式(Proxy Pattern)
理解Hibernate懒加载之前,需要简单了解什么是代理模式。
代理模式就是为其他对象提供一种代理以控制对这个对象的访问。以一段加载图片的代码为例。
public interface Image {
public void load();
}
创建一个背景图(Background)需要耗时2.333秒
public class Background implements Image {
public Background() {
super();
try {
Thread.sleep(2333);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void load() {
System.out.println("加载图片中...");
}
}
代理类如下
public class ImageProxy implements Image {
private Image image;
public ImageProxy(Image image) {
super();
this.image = image;
}
@Override
public void load() {
if (null == image) {
image = new Background();
}
image.load();
}
}
public class Test {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
Image imageProxy = new ImageProxy(null);
long endTime = System.currentTimeMillis();
System.out.println("生成代理耗时:" + (endTime - startTime));
imageProxy.load();
long endTime2 = System.currentTimeMillis();
System.out.println("访问耗时:" + (endTime2 - startTime));
}
}
/*
生成代理耗时:0
加载图片中...
访问耗时:2335
*/
Background的创建被推迟到使用时才创建,优点显而易见:
- 提高了获取Background对象的系统性能。
- 减少了对象在内存中存在的时间,节约了系统内存的开销。
- 除此之外,倘若在Test程序中不调用imageProxy的load()方法,在这种情况下,使用代理模式就可以不创建Background对象,打打提高了系统的运行性能。
这里仅谈与懒加载有关的部分代理模式内容,其他内容不再阐述。
Hibernate懒加载与代理模式(Proxy Pattern)
Hibernate懒加载所采用的设计模式便是代理模式,因此在很多情况下能节约很大的系统开销,上面已经说明,这里不再阐述。
懒加载特性与org.hibernate.LazyInitializationException
明白了Hibernate懒加载与代理模式后,对于org.hibernate.LazyInitializationException也就明白了。由于懒加载的特性,在Hibernate Session范围之外访问未初始化的集合或者代理都会抛出org.hibernate.LazyInitializationException。因此,在Session的范围之内访问的集合或代理即可解决该问题。
这里博主给出三种解决方案,仅供参考。
解决方案一: 不使用懒加载
在配置文件或注解中,将懒加载设置为fetch=FetchType.EAGER或false。
这种做法,可能会把不需要的数据也加载上来,影响应用性能。
解决方案二: Open Session In View模式
利用过滤器(Filter),在用户请求结束、页面生成结束时关闭Session,保证在视图显示层一直打开Session。
这种做法也比较简单,似乎还不错,但是副作用却不容小视。在DAO层数据已经加载完毕后,数据库Connection在在用户请求结束、页面生成结束时才关闭。因而各种原因,如传输数据大、数据传输速率低等问题会导致数据库Connection长时间被占用不关闭,造成连接池不足、网页访问慢、网页假死等现象。
解决方案三:手动初始化数据
在关闭Session之前,初始化代理属性或者集合属性。如:
for (Chapters chapter : book.getChapters());
当然这样代码可读性较差,Hibernate也提供了一种方法
Hibernate.initialize(book.getChapters());