hibernate FetchType理解

JPA定義實體之間的關係有如下幾種: 
@OneToOne 
@ManyToOne 
@OneToMany 
@ManyToMany 
在定義它們的時候可以通過fetch屬性指定加載方式,有兩個值: 
FetchType.LAZY:延遲加載 
FetchType.EAGER:急加載 
急加載就好理解了,在加載一個實體的時候,其中定義是急加載的的屬性(property)和字段(field)會立即從數據庫中加載 
開發過程中遇到問題最多的就是延遲加載,並且問題都是一個: 
“爲什麼我定義爲延遲加載了,但沒起作用,相關的屬性或者字段還是會立即加載出來?” 
對於這個問題,我的理解是這樣的,我們首先假設有如下的影射關係: 
@Entity 
@Table(name = "orders") 
class Order{ 

@OneToMany(cascade = {CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH},fetch = FetchType.LAZY,mappedBy = "order") 
private Collection 
lineItems = new HashSet 
(); 

@OneToOne(cascade={CascadeType.REMOVE},fetch=FetchType.LAZY,mappedBy="order") 
@JoinColumn(name="order_id") 
private OrderPrice salePrice; 

@OneToOne(cascade={CascadeType.REMOVE},fetch=FetchType.LAZY) 
@JoinColumn(name="customer_id") 
private Customer customer; 


@Entity 
@Table(name = "order_items") 
class LineItem{ 

@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, 
CascadeType.REFRESH},fetch = FetchType.LAZY) 
@JoinColumn(name = "order_id",referencedColumnName = "order_id") 
private Order order; 



@Entity 
@Table(name = "order_finance") 
@AttributeOverride(name="id",column=@Column(name="order_id")) 
class OrderPrice extends Price{ 

private Order order; 

@OneToOne(cascade={},fetch=FetchType.LAZY) 
@JoinColumn(name="order_id",referencedColumnName="order_id") 
public Order getOrder() { 
return order; 


public void setOrder(Order order) { 
this.order = order; 




@MappedSupperclass 
@Table(name = "order_finance") 
class Price{ 
@Id 
public Integer getId(){ 
... 


表的關係是:orders表是一個單獨的表,order_items表有一個外鍵(order_id)引用到orders 表,order_finance有一個外鍵(order_id)引用到orders表. 
order_items——->orders<————order_finance 
| 
customer 
現在的問題就是: 
Order.lineItems 這個@OneToMany的LAZY延遲加載是起作用的,find order的時候沒有find出lineItems 
Order.customer 這個@OneToMany的LAZY延遲加載是起作用的,find order的時候沒有find出Customer 
Order.salePrice 這個@OneToOne的LAZY延遲加載沒起作用,find order後會把相關的OrderPrice也fetch 出來 
LineItem.order 這個@ManyToOne的LAZY延遲加載是起作用的,find lineitem沒有把相關的order find出來 
OrderPrice.order 這個@OneToOne的LAZY延遲加載沒起作用,find orderprice的時候把相關的order find出來了 
延遲加載,顧名思義,就是在訪問具體的屬性時才從數據庫中加載,比如例子中,只有調用OrderPrice.getOrder()的時候才應該會 加載Order這個實體,加載OrderPrice的時候是不應該加載Order的。 
那麼首先想想,對於延遲加載,hibernate怎麼知道什麼時候會調用到相關的實體的get方法呢? 
答案是它不知道,hibernate不知道什麼時候會調用到相關的get方法,那麼hibernate如何實現只有訪問到才加載這一點? 
hibernate使用了代理(Proxy),對實體的調用會被代理接受和處理,hibernate可以設置這個代理被調用到的時候去加載數據, 從而實現延遲加載。那麼對於一個映射對象,要麼它有值,要麼它是null,對於null值建立代理是沒多大作用的,而且也不能對null建立動態代理。那 就是說hibernate在對延遲加載建立代理的時候要考慮這個映射的對象是否是null。如果是null不需要建立代理,直接把映射的值設置成 null,如果映射的對象不爲null,那麼hibernate就建立代理對象 
延遲加載失敗都是由於確定映射的內容是否是null引起的 
先來看@OneToMany,比如例子中的Order.lineitems,這是一個Collection,hibernate在加載Order 的時候不加載lineitems,而是創建一個代理(Proxy)一個針對Collection的代理(通常是 org.hibernate.collection.persistentBag)。除非你調用了像Order.getLineItems.size() 或者Order.getLineItems.get()方法的時候hibernate纔會去加載這個order的lineitems數據,要不然只是調用 Order.getLineItems是不會加載到數據的,因爲這個時候並沒有具體的訪問LineItem. 
由於代理是針對Collection建立的,而不是針對實體建立的,hibernate不用太多考慮是否爲null,如果lineitem沒有, 也只是代表這個集合是長度是0,這個集合是不爲Null的。所以這很容易實現延遲加載 
現在在來看例子@OneToOne Order.salePrice。它爲什麼會失敗呢? 
hibernate也會建立代理,但這個代理是針對OrderPrice建立的(如果延遲加載成功,這個代理類形如 Customer_javasisst_$1),默認optioanl=true,也就是說OrderPrice可以爲null,那麼hibernate 就要考慮,這裏是放一個null呢?還是放一個代理。但在Order這個實體裏是不能確定它有沒有價格的(但在價格裏知道他的Order,有個外鍵指向 order),所以hibernate要確認這個OrderPrice是否存在,這個確認就導致的延遲加載失敗,因爲OrderPrice要被查詢一次, 如果不存在映射值爲null,如果存在這個時候值都取出來了,當然就不用什麼代理了 
Order.customer延遲加載是成功的,order表有一個外鍵關聯到customer表,hibernate應該從這裏知道這個 customer是確實存在的,不用把映射值設置成null了,可以設置成代理類Customer_javasisst_$2 
那如果把Order.salePrice的映射定義修改成: 
@OneToOne(cascade={CascadeType.REMOVE},fetch=FetchType.LAZY,optional=false,mappedBy=”order”) 
@JoinColumn(name=”order_id”) 
private OrderPrice salePrice; 
延遲加載就成功了,因爲optional=false指定salePrice不是可選的,必須有值,所以hibernate也不用考慮是該放 null還是放代理,既然必須有值,又是延遲加載,那就設置成代理類了 
根據上面所說的,OrderPrice定義有一個外鍵關聯到Order,那OrderPrice.order這個延遲加載應該是成功的,但爲什麼 會失敗呢? 
難道是Order與OrderPrice兩邊都定義了OneToOne關係? 
我這個例子中,這裏失敗我想是因爲OrderPrice這個實體的定 義:@AttributeOverride(name=”id”,column=@Column(name=”order_id”)) 
再來看看ManyToOne的LineItem.order,這個延遲加載也是成功的。因爲lineitem定義了外健關係到order 
對於延遲加載的對象,如果已經脫離了容器,調用會得到org.hibernate.LazyInitializationException: could not initialize proxy – no Session方法異常 
還有一種情況下延遲加載“看起來是沒起作用的”:其實是起作用的,但可能在什麼地方的代碼調用到了相關的get方法,把延遲加載的對象加載出來 的,所以看起來是沒有成功的 
總結: 
對於延遲加載,hibernate無法知道什麼時候會調用到延遲加載的屬性/字段的get方法,所以對於延遲加載的屬性/字 段,hibernate會通過建立代理Proxy來包裝(Wrapper)一下 
代理可能會根據實體本身建立,也可以是根據一個集合建立,如果是根據一個集合建立,延遲加載一般都能成功,如果是根據實體建立,null是不能建 立代理的,如果能夠確定代理類一定存在,那延遲加載就能成功,相關的映射放置的是代理類,如果不能確定映射的屬性是否存在,那就會去數據庫中進行查詢,這 就導致的延遲失敗。 
外鍵定義可以讓hibernate知道映射的屬性是否存在 
也可以通過optional=false來告訴hibernate,映射的屬性一定存在
發佈了4 篇原創文章 · 獲贊 2 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章