JPA一對多延遲加載(orders和orderdetails), 對於一次查詢大量數據和多線程併發非常有幫助。
1. 一對多一般默認就是延遲加載, 在用到具體屬性的時候纔會執行SQL去抓取數據。
entity.getItems(); //不執行sql
entity.getItems().getProperty(); // 具體使用的纔會執行sql
1.1 多對一默認是關閉懶加載的, 需要註解顯示開啓懶加載
2. 屬性級延遲加載註解(就算是blob等大字段JPA好像也不支持, 可以單獨設置爲一個實體, 然後做OneToOne懶加載)
@Basic(fetch = FetchType.Lazy)
package cn.itcast.bean;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name="orders") //把表名改成orders(默認表名是order),防止默認表名order與數據庫的關鍵字"order by"中的order衝突。不改的話測試不成功,出現異常,orderitem表建立成功,order表建不了。
public class Order {
private String orderId;
private Float amount = 0f;
private List<OrderDetail> orderDetails= new ArrayList<OrderDetail>();
@Id //要注意:目前JPA規範並沒有提供UUID這種生成策略,目前主鍵值只提供了整型的生成方式,所以@GeneratedValue這個註解就不能在這裏用上,不能對字符串進行id自增長。
@Column(length = 12)
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
@Column(nullable = false)
public Float getAmount() {
return amount;
}
public void setAmount(Float amount) {
this.amount = amount;
}
@OneToMany(cascade = { CascadeType.REFRESH, CascadeType.PERSIST,
CascadeType.MERGE, CascadeType.REMOVE },fetch=FetchType.LAZY,mappedBy="order")
//mappedBy="order",中的order是關係維護端的order屬性,這個order屬性的類型是這個bean。
public Set<OrderDetail> getOrderDetails() {
return orderDetails;
}
/*
@OneToMany(fetch=FetchType.)的選項有,如下圖:
FetchType.EAGER:代表立即加載;
FetchType.LAZY:代表延遲加載。
當我們把fetch設置爲FetchType.LAZY的時候,什麼時候初始化items裏面的數據呢?當我們第一次訪問這個屬性,並對這個屬性進行操作的時候,這個集合的數據纔會從數據庫裏面load出來。但要注意:當我們訪問這個延遲屬性的時候,我們的前提要EntityManager這個對象沒有被關閉,如果被關閉了我們再去訪問延遲屬性的話,就訪問不到,並拋出延遲加載意外。
如果沒有設置fetch這屬性的話,會怎麼樣呢?是立即加載?還是延遲加載呢?
記住@OneToMany這個標籤最後的英文單詞,如果是要得到Many的一方,我不管你前面是什麼,只要後面的單詞是Many,也就是說要得到多的一方,你們就給我記住,默認的加載策略就是延遲加載(Many記錄可能上幾萬條,立即加載的話可能對效率影響大,所以延遲加載)。
反過來,如果後面是One呢?因爲它是加載一的一方,這對性能影響不是很大,所以它的默認加載策略是立即加載。
mappedBy:我們怎麼知道關係的維護端和被維護端呢?當然JPA規範規定多的一端應該是爲維護端(關係維護段增加一個字段爲外鍵,裏面保存的是一的一端的主鍵),一的一端爲關係被維護端,那麼我們總要在程序裏給他們打上標誌吧?雖然規範是這麼規定,但總要申明一下吧?就是通過mappedBy屬性,只要哪個類出現了mappedBy,那麼這個類就是關係的被維護端。裏面的值指定的是關係維護端。
orderDetail這邊由哪一個屬性去維護關係呢?是OrderItem類的order屬性。
mappedBy屬性對應Hibernate裏面的inverse屬性:<SET name="orderDetails" inverse="true"></SET>
*/
public void setOrderDetails(List<OrderDetail> orderDetails) {
this.orderDetails<span style="font-family: Arial, Helvetica, sans-serif;"> </span>= orderDetails;
}
//用這個方法會方便很多
public void addOrderDetail(OrderDetail orderDetail){
orderDetail.setOrder(this); //關係維護方orderDetail加入關係被維護方(this)後,才能維護更新關係(orderDetail表中的外鍵字段order_id),維護關係其實就是更新外鍵。只有爲關係維護端設置了關係被維護端,關係才能建立起來。
this.orderDetails.add(orderDetail);
}
public void removeOrderDetail(OrderDetail orderDetail){
orderDetail.setOrderDetail(null);
this.orderDetails.remove(orderDetail);
}
package cn.itcast.bean;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
@Entity
public class OrderDetail {
private Integer id;
private String productName;
private Float sellPrice = 0f; //默認值爲0。
private Order order;
@Id
@GeneratedValue //id自增長方式生成主鍵。
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(length = 40, nullable = false)
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
@Column(nullable = false)
public Float getSellPrice() {
return sellPrice;
}
public void setSellPrice(Float sellPrice) {
this.sellPrice = sellPrice;
}
@ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)
@JoinColumn(name="order_id") //設置外鍵的名稱。
public Order getOrder() { //OrderItem是關係維護端,負責關係更新,它是根據它的order屬性值維護關係的。當它保存的時候(主動保存或是被級聯保存),他會根據order屬性的值更新關係,當order爲null時,就不會更新關係了。級聯操作也是根據雙方對象中的映射屬性值進行的,當映射屬性沒值的時候就不會對對方進行級聯操作了。
return order;
}
/*
@ManyToOne的級聯保存(CascadeType.PERSIST)是不需要的,不可能說你保存某個訂單項OrderItem的時候,也保存訂單Order的。通常都是保存訂單Order的時候,保存訂單項OrderItem的。
CascadeType.MERGE:如果我們更新了訂單項orderItem產品的價錢,那麼整個訂單Order的總金額是會發生改變的,所以可以定義這個級聯更新。
CascadeType.REFRESH:如果我們想得到目前數據庫裏orderItem最新的數據的話,我們也希望得到訂單order的最新數據,我們可以定義這個級聯刷新,就是說把數據庫裏最新的數據重新得到。
CascadeType.REMOVE:這個屬性這裏肯定不設。就好比現在有一個訂單,一個訂單裏面有3個購物項orderItem,買了A,B,C三個產品,我現在不要A這個產品了,我們只是把A這條記錄刪掉,那麼如果這裏定義了級聯刪除的話,那麼你刪除A記錄的同時,也會把整個訂單也刪除掉,所以這裏不需要設級聯刪除。
*/
//optional:說明order這個是否是可選的?是否可以沒有的?false表示必須的,true表示是可選的。
public void setOrder(Order order) {
this.order = order;
}
}