hibernate多對多,單個修改很傷神

導讀

項目詳細中添加項目標籤

客戶單擊項目詳細時,會跳轉到項目詳細頁面。用戶單擊紅框中的加號,頁面彈出選擇標籤頁面。用戶單擊待選標籤中的標籤,當前標籤會被保存到數據庫中。前端

固然,項目標籤與項目之間是多對多的關係,也就是說,一個項目能夠有多個標籤,一個標籤對應多個項目,整體來講,它們之間是多對多的關係。數據庫

多對多的關係

多也是相對的多,就像導讀中的項目和項目標籤同樣。這是從宏觀上來講的多對多,但從微觀上來看,其內部仍是一對多的關係。一個項目能夠有多個項目標籤,一個項目標籤能夠對應多個項目。既然對各自來講,都是一對多的關係,爲何不這樣寫呢:json

// 項目表
@Entity
@Table(name = "zq_project")
public class Project extends BaseObj {
 /**
 * 項目標籤
 */
    @ManyToOne
    @JoinColumn(name = "attach_id")
    private List<DataDict> tagss;
}

//項目標籤表
@Entity
@Table(name = "core_data_dict", uniqueConstraints = {@UniqueConstraint(columnNames = {"tenant_id", "code"}, name = "uk_dd_tid_code")})
public class DataDict extends ToString{

  /**
   * 項目
  */
    @ManyToOne
    @JoinColumn(name = "attach_id")
    private List<Project> projectList;
    
}

而是這樣書寫方式呢?session

/**
     * 添加項目標籤,若是是標籤來源於數據字典,則能夠直接讀取數據字典
     */
    @ManyToMany(fetch = FetchType.EAGER)
    @Fetch(FetchMode.SELECT)
    @JoinTable(
            name = "zq_project_tags",
            joinColumns = {@JoinColumn(name = "project_id")},
            inverseJoinColumns = @JoinColumn(name = "tag_code")
    )
    @JSONField(serialize = false)
    private List<DataDict> tagList;

由於這樣書寫,hibernate會根據項目和數據字典(項目標籤放在數據字典表中)生成一箇中間表,如圖所示:框架

項目標籤表

爲何不採用第一種方式呢?
一、第一種方式擴展性很差,破壞框架結構,爲何這麼說?maven

項目標籤放置在數據字典中,換句話,數據字典所存儲的數據集不會常常改變。就像咱們的新華字典,不會時常作版本更新。於是,相似於項目標籤,數值型的單位呀、系統標量等,這些數據都不會時常改變。ide

並且,如今的項目都採用maven構建,什麼是maven項目呢?不少博客都有詳細介紹,我只作簡單地介紹。maven是項目對象模型(POM),舉一個簡單的例子,咱們在不使用maven建立項目時,每每須要拷貝jar包到項目中。然而,咱們使用maven建立項目時,就會建立一個pom文件。咱們在pomp文件中作配置,若是須要新的jar包,咱們直接使用<dependence>...</dependence>模塊化

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>${fastjson.version}</version>
  </dependency>

固然,引用jar包是一回事,還有模塊化構建項目。咱們常常分模塊構建項目,好比信息發佈一個模塊,核心層一個模塊,自定義業務層一個模塊。各個模塊之間就像是Java同樣,具備繼承和關聯關係。子pom文件可使用父pom的全部jar包。如圖所示:fetch

模塊間的相互關係

層級很是嚴格,即使在同一個項目不一樣的模塊中,咱們若是想要調用另一個模塊的類時,也須要用dependency來引用其模塊。好比,咱們想要在platform-custom層調用platform-core層的類,咱們須要在platform-custom的pom文件中寫入依賴:spa

<dependency>
    <groupId>com.zfounder.platform</groupId>
    <artifactId>platform-core</artifactId>
   </dependency>

可是,項目表是自定義業務範疇的,是在platform-custom模塊中的,而數據字典是屬於platform-core模塊的。由於platform-core的pom沒有依賴platform-custom模塊,於是,數據字典沒法調用platform-custom模塊中的項目類,如圖所示:

數據字典調用項目類

於是,若是咱們採用這種方式建立多對多的關係,會破壞maven結構或者框架結構。

二、對於數據庫的維護性很差。
對於項目來講,相同項目標籤的可能有數條項目的記錄,可是,每一個項目都是惟一的,一個項目應該有多個標籤,但如今的問題是,同一個項目的編號不一樣了,由於不一樣的項目標籤,這就破壞了數據庫的惟一性。

針對以上兩種狀況,咱們應該使用中間表,來建立多對多的關係。如圖所示:

clipboard.png

這樣簡潔明快,也便於往後的維護工做。

哪些是多對多的關係

一、用戶和角色之間的關係,一個用戶能夠有多重角色,一個角色能夠對應多個用戶。
二、既然說到了角色,天然說到角色所能管理的權限,好比當用戶登陸,根據用戶的角色,須要展現哪些頁面,能夠操做該頁面的哪些按鈕,等等。這些都放在資源表中。如圖所示:

角色資源說明
三、項目和圖片的關係,一個項目有不少張圖片,可是一張圖片也能夠被多個項目使用。
四、。。。。

中間表通常都涉及兩個字段,字段分別引用兩張表的主鍵。

多對多的實例,以選擇項目標籤爲例

咱們在點擊保存項目標籤時,心想咱們點擊一次,保存一次,而項目類中項目標籤對象是一個集合對象,咱們應該建立這個集合,而後拿到前端傳


過來的項目標籤的code值(數據字典對應的是code值),經過數據字典的事務方法獲取這個數據字典對象,再判斷該對象的父類是否是項目標籤。由於數據字典存儲不少類字典,好比項目標籤字典、單位字典、系統變量字典等等。而後保存這個數據字典,但結果事與願違,它會覆蓋數據庫中原來的數據,這是更新數據,而不是保存數據,如圖所示:

 List&lt;DataDict&gt; tags = new ArrayList&lt;&gt;()方法

爲何會這樣,由於,其底層使用的是merge方法,如代碼所示:

/**
 * @see com.zfounder.platform.core.dao.GenericDao#save(T)
 */
@SuppressWarnings("unchecked")
@Override
public T save(T object) {
    Session session = getSession();
    return (T) session.merge(object);
}

merger若是發現數據庫存在該編號的對象,則執行update操做;若是不存在,則執行insert操做。於是,中間表,即項目標籤表,已經存在該項目編號的id,因此,執行更新操做。

咱們換一種思路,首先看項目類的部分源碼:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "zq_project")
public class Project extends BaseObj {

    /**
     * 項目名稱
     */
    @Column(name = "name")
    private String name;
    
    /**
     * 錄入日期
     */
    @Column(name = "sign_date")
    private Date signDate;

    /**
     * 備註
     */
    @Lob
    @Basic(fetch = FetchType.LAZY)
    @Column(name = "remark", columnDefinition = "mediumtext")
    private String remark;

    /**
     * 預算金額
     */
    @Column(name = "budgetary_amount", precision = 10, scale = 2)
    private BigDecimal budgetaryAmount;
 
    /**
     * 工程起始時間
     */
    @Column(name = "start_time")
    private Date startTime;

    /**
     * 工程結束時間
     */
    @Column(name = "end_time")
    private Date endTime;

    /**
     * 添加項目標籤,若是是標籤來源於數據字典,則能夠直接讀取數據字典
     */
    @ManyToMany(fetch = FetchType.EAGER)
    @Fetch(FetchMode.SELECT)
    @JoinTable(
            name = "zq_project_tags",
            joinColumns = {@JoinColumn(name = "project_id")},
            inverseJoinColumns = @JoinColumn(name = "tag_code")
    )
    @JSONField(serialize = false)
    private List<DataDict> tagList;
}

由於項目標籤時多對多的關係,當咱們接收到前端傳過來的項目編號,並調用項目的事務的get方法,建立當前項目瞬時態的對象時。hibernate根據項目標籤表中的code值,建立項目標籤(數據字典)的集合對象,並填充到tagList的集合中。咱們只要在對集合對象上,添加從前端傳過的code值,並經過code值建立項目標籤(數據字典)的對象便可。於是,代碼改爲下面的方式:

  List&lt;DataDict&gt; tags = dbProject.getTagList();

這樣保存就沒問題了。

結尾

若是咱們掌握了最基本知識是,就能夠很容易掌握其餘語言了。加油,致本身。

相關文章
相關標籤/搜索