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,映射的屬性一定存在


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM