學習Spring有兩周時間了 , 個人覺得服務端主要實現的是數據關系的維護和數據結構的制定 , 以及由業務需求產生的CRUD , 只要保證對前端提供的接口穩定高效響應 , 具體的前端實現完全不關心.
這個是接觸后端的一個感受 , Spring boot使用了特定的方式來進行配置 ,大大簡化了后端開發人員的開發工作量 ,比如JPA:用來操作實體對象,執行CRUD操作,框架在后台替我們完成所有的事情,開發者從繁瑣的JDBC和SQL代碼中解脫出來 ,此時數據之間的關系維護和數據結構的制定就顯得尤為關鍵 , 下面記錄數據表對應關系中的常見關系:一對多關系.
數據表關系
下面用實際場景簡單理解數據表之間的關系
一對一:我有一個身份證號 ;
一對多:我有多張銀行卡;
多對多:我是招行、交行、建行的客戶,但是這些銀行用戶均過億。
一對多雙向關聯
下面以訂單order和訂單項orderitem來展示
創建訂單實體類,代碼如下:
package learn.jpa.entity; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; /** * 訂單 */ @Entity // 定義類為實體類 public class Order { private String orderid; private float amount = 0f; private Set<OrderItem> item = new HashSet<OrderItem>(); @Id // 實體標識符,因為是字符串類型,所有不能用 @GeneratedValue,只能人為的賦值 @Column(length=20) 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}) public Set<OrderItem> getItem() { return item; } public void setItem(Set<OrderItem> item) { this.item = item; } } /** * 1 - N * 多的一端為關系維護端,關系維護端負責外鍵記錄的更新 * */
創建訂單項實體類,代碼如下
package learn.jpa.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; /** * 訂單項 */ @Entity // 定義類為實體類 public class OrderItem { private int id; private String productName; private float sellPrice = 0f; private Order order; @Id // 實體標識符 @GeneratedValue // 主鍵自動增長 public int getId() { return id; } public void setId(int 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; } public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } }
注解:
1、@OneToMany(fetch=FetchType,cascade=CascadeType)
@OneToMany描述一個一對多的關聯,該屬性應該為集體類型,在數據庫中並沒有實際字段.
fetch:表示該屬性的讀取策略,有EAGER和LAZY兩種,分別表示主支抓取和延遲加載,默認為EAGER.
cascade:表示級聯操作策略,對於OneToMany類型的關聯非常重要,通常該實體更新或刪除時,其關聯的實體也應當被更新或刪除
(1)、CascadeType.MERGE級聯更新:若items屬性修改了那么order對象保存時同時修改items里的對象。對應EntityManager的merge方法
(2)、CascadeType.PERSIST級聯刷新:獲取order對象里也同時也重新獲取最新的items時的對象。對應EntityManager的refresh(object)方法有效。即會重新查詢數據庫里的最新數據
(3)、CascadeType.REFRESH級聯保存:對order對象保存時也對items里的對象也會保存。對應EntityManager的presist方法
(4)、CascadeType.REMOVE級聯刪除:對order對象刪除也對items里的對象也會刪除。對應EntityManager的remove方法
CascadeType.PERSIST只有A類新增時,會級聯B對象新增。若B對象在數據庫存(跟新)在則拋異常(讓B變為持久態)
CascadeType.MERGE指A類新增或者變化,會級聯B對象(新增或者變化)
CascadeType.REMOVE只有A類刪除時,會級聯刪除B類;
CascadeType.ALL包含所有;
綜上:大多數情況用CascadeType.MERGE就能達到級聯跟新又不報錯,用CascadeType.ALL時要斟酌下CascadeType.REMOVE
optional:是否允許該字段為null,該屬性應該根據數據庫表的外鍵約束來確定,默認為true
一對多延遲加載與關系維護
訂單實體類,代碼:
package learn.jpa.entity; 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.Id; import javax.persistence.OneToMany; import javax.persistence.Table; /** * 訂單 */ @Entity // 定義類為實體類 @Table(name="orders") public class Order { private String orderid; private float amount = 0f; private Set<OrderItem> item = new HashSet<OrderItem>(); @Id // 實體標識符,因為是字符串類型,所有不能用 @GeneratedValue,只能人為的賦值 @Column(length=20) 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; } /** * 如果是一對多或多對多 fetch 默認是延遲加載,反之是立即加載 * mappedBy="order" 表示由實體 OrderItem 中的 order 屬性維護 * @return */ @OneToMany(cascade={CascadeType.REFRESH,CascadeType.PERSIST,CascadeType.MERGE,CascadeType.REMOVE}, fetch=FetchType.LAZY,mappedBy="order") public Set<OrderItem> getItem() { return item; } public void setItem(Set<OrderItem> item) { this.item = item; } public void addOrderItem(OrderItem orderItem){ orderItem.setOrder(this); this.item.add(orderItem); } } /** * 1 - N * 多的一端為關系維護端,關系維護端負責外鍵記錄的更新 * */
mappedBy只有在雙向關聯時,才會使用這個屬性
mappedBy=”另一方的關系引用屬性”
訂單項實體類,代碼:
package learn.jpa.entity; 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.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; /** * 訂單項 */ @Entity // 定義類為實體類 public class OrderItem { private int id; private String productName; private float sellPrice = 0f; private Order order; @Id // 實體標識符 @GeneratedValue // 主鍵自動增長 public int getId() { return id; } public void setId(int 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},fetch=FetchType.EAGER,optional=false) @JoinColumn(name="order_id") public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } }
joinColumns屬性表示,在保存關系中的表中,所保存關聯關系的外鍵的字段。並配合@JoinColumn標記使用。
小結
理解關聯表中存儲的關系對含義, 記下使用的套路基本上就沒什么問題了 ,下篇文章將記錄多對多雙向關聯與級聯操作 .