懶加載又稱延遲加載,是當在真正需要數(shù)據(jù)的時候,才真正執(zhí)行數(shù)據(jù)加載操作。也就是說,當使用到對象的具體屬性時,才進行加載。
舉個例子來說,一本書(Books)有很多章節(jié)(Chapters),一般加載數(shù)據(jù)的時候,加載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();}創(chuàng)建一個背景圖(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的創(chuàng)建被推遲到使用時才創(chuàng)建,優(yōu)點顯而易見: * 提高了獲取Background對象的系統(tǒng)性能。 * 減少了對象在內(nèi)存中存在的時間,節(jié)約了系統(tǒng)內(nèi)存的開銷。 * 除此之外,倘若在Test程序中不調(diào)用imageProxy的load()方法,在這種情況下,使用代理模式就可以不創(chuàng)建Background對象,打打提高了系統(tǒng)的運行性能。
這里僅談與懶加載有關(guān)的部分代理模式內(nèi)容,其他內(nèi)容不再闡述。
Hibernate懶加載所采用的設(shè)計模式便是代理模式,因此在很多情況下能節(jié)約很大的系統(tǒng)開銷,上面已經(jīng)說明,這里不再闡述。
明白了Hibernate懶加載與代理模式后,對于org.hibernate.LazyInitializationException也就明白了。由于懶加載的特性,在Hibernate session范圍之外訪問未初始化的集合或者代理都會拋出org.hibernate.LazyInitializationException。因此,在Session的范圍之內(nèi)訪問的集合或代理即可解決該問題。
這里博主給出三種解決方案,僅供參考。
在配置文件或注解中,將懶加載設(shè)置為fetch=FetchType.EAGER或false。
這種做法,可能會把不需要的數(shù)據(jù)也加載上來,影響應用性能。
利用過濾器(Filter),在用戶請求結(jié)束、頁面生成結(jié)束時關(guān)閉Session,保證在視圖顯示層一直打開Session。
這種做法也比較簡單,似乎還不錯,但是副作用卻不容小視。在DAO層數(shù)據(jù)已經(jīng)加載完畢后,數(shù)據(jù)庫Connection在在用戶請求結(jié)束、頁面生成結(jié)束時才關(guān)閉。因而各種原因,如傳輸數(shù)據(jù)大、數(shù)據(jù)傳輸速率低等問題會導致數(shù)據(jù)庫Connection長時間被占用不關(guān)閉,造成連接池不足、網(wǎng)頁訪問慢、網(wǎng)頁假死等現(xiàn)象。
在關(guān)閉Session之前,初始化代理屬性或者集合屬性。如:
for (Chapters chapter : book.getChapters());當然這樣代碼可讀性較差,Hibernate也提供了一種方法
Hibernate.initialize(book.getChapters());新聞熱點
疑難解答