懒加载又称延迟加载,是当在真正需要数据的时候,才真正执行数据加载操作。也就是说,当使用到对象的具体属性时,才进行加载。
举个例子来说,一本书(Books)有很多章节(Chapters),一般加载数据的时候,加载Books的同时会同时把Chapters加载上来,而懒加载就是加载Books的属性时不会将Books中的Chapters立刻加载上来,只有在使用到Chapters中的属性时,才将Chapters加载出来。
@Entity@Tablepublic 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@Tablepublic 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; }}理解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懒加载所采用的设计模式便是代理模式,因此在很多情况下能节约很大的系统开销,上面已经说明,这里不再阐述。
明白了Hibernate懒加载与代理模式后,对于org.hibernate.LazyInitializationException也就明白了。由于懒加载的特性,在Hibernate session范围之外访问未初始化的集合或者代理都会抛出org.hibernate.LazyInitializationException。因此,在Session的范围之内访问的集合或代理即可解决该问题。
这里博主给出三种解决方案,仅供参考。
在配置文件或注解中,将懒加载设置为fetch=FetchType.EAGER或false。
这种做法,可能会把不需要的数据也加载上来,影响应用性能。
利用过滤器(Filter),在用户请求结束、页面生成结束时关闭Session,保证在视图显示层一直打开Session。
这种做法也比较简单,似乎还不错,但是副作用却不容小视。在DAO层数据已经加载完毕后,数据库Connection在在用户请求结束、页面生成结束时才关闭。因而各种原因,如传输数据大、数据传输速率低等问题会导致数据库Connection长时间被占用不关闭,造成连接池不足、网页访问慢、网页假死等现象。
在关闭Session之前,初始化代理属性或者集合属性。如:
for (Chapters chapter : book.getChapters());当然这样代码可读性较差,Hibernate也提供了一种方法
Hibernate.initialize(book.getChapters());新闻热点
疑难解答