JPA中的一對多雙向關聯與級聯操作


學習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標記使用。

小結

理解關聯表中存儲的關系對含義, 記下使用的套路基本上就沒什么問題了 ,下篇文章將記錄多對多雙向關聯與級聯操作 .

 


免責聲明!

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



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