关于JAP FetchType.LAZY(hibernate实现)的理解
來源:http://www.diwuzhang.com/people-1/article-124/
?
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 CollectionlineItems = 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{@Idpublic 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,映射的屬性一定存在
總結
以上是生活随笔為你收集整理的关于JAP FetchType.LAZY(hibernate实现)的理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 农业银行通用k宝蓝牙版怎么用
- 下一篇: 健康保险包括