首頁 > 軟體

使用SpringDataJpa建立中間表

2022-06-13 14:04:26

SpringDataJpa建立中間表

//fetch=FetchType.EAGER 關閉懶載入 相當於hibernate中的lazy=false
	//joinColumns 設定中間表的主列
	//inverseJoinColumns=@JoinColumn(name="t_roleId") 建立中間表的副列
	@ManyToMany(fetch=FetchType.EAGER)
	@JoinTable(name="t1_user_permission",joinColumns=@JoinColumn(name="t_userId"),
	inverseJoinColumns=@JoinColumn(name="t_perId"))
	private List<Permission> perList;
 
//mappedBy="roleList" 變成雙向
	//mappedBy="roleList" 把主權交給user 然後 role裡面就不建立中間表了
	//表示宣告自己不是多對多的關係維護端,由對方來維護
	@ManyToMany(mappedBy="perList",fetch=FetchType.EAGER)
	private List<User> userList;

JPA中間表(關係表)聯合主鍵設定說明

問題場景

平時在開發中經常會出現多對多的關係,這個時候會建立一個關係表。但該關係表中並沒有設定唯一主鍵欄位而是聯合主鍵,那麼JPA下建立該關係表實體後執行專案會提示No identifier specified或does not define an IdClass的錯誤。

下面以使用者部門關係進行舉例說明,使用者和部門是多對多的關係。

資料表結構

CREATE TABLE `mb_member_dept` (
    `member_id`  bigint(20) NOT NULL ,
    `dept_id`  bigint(20) NOT NULL ,
    PRIMARY KEY (`member_id`, `dept_id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8mb4 COLLATE=utf8mb4_general_ci
ROW_FORMAT=DYNAMIC;

實體程式碼

/**
* 使用者部門實體
* @author lizebin
* @version 1.0
* @date 2021/2/18 12:48 上午
*
**/
@Getter
@Setter
@Entity
@Table(name = "mb_member_dept")
public class MemberDeptPO implements Serializable{
  private static final long serialVersionUID = 1271571231859316736L;
  /**
  * 聯合主鍵使用者ID
  */
  @Column(name = "member_id", length = 20)
  private long memberId;
  /**
  * 聯合主鍵部門ID
  */
  @Column(name = "dept_id", length = 20)
  private long deptId;
}

觀察以上程式碼似乎並沒有什麼問題,但在啟動專案時會提示以下錯誤:

Caused by: org.hibernate.AnnotationException: No identifier specified for entity: com.test.po.MemberDeptPO

此時需要在聯合主鍵欄位memberId和deptId上增加@Id註解即可解決以上錯誤。這裡需要注意的是,一般出現以上錯誤提示時只要在主鍵欄位上增加@Id註解即可解決問題。

但再次啟動專案時還是會提示一個does not define an IdClass的錯誤:

Caused by: java.lang.IllegalArgumentException: This class [class com.test.po.MemberDeptPO] does not define an IdClass

這是因為聯合主鍵時需要額外定義一個idClass類作為實體的ID,idClass類程式碼如下:

idClass類程式碼

/**
* 使用者部門關係聯合主鍵定義
* @author lizebin
* @version 1.0
* @date 2021/2/18 10:36 上午
*
**/
@Getter
@Setter
public class MemberDeptKey implements Serializable {
    private static final long serialVersionUID = -5482200454871393530L;
	/**聯合主鍵,欄位名稱與MemberDeptPO 類中一致*/
    private long memberId;
	/**聯合主鍵,欄位名稱與MemberDeptPO 類中一致*/
    private long deptId;
    public MemberDeptKey() { }
    public MemberDeptKey(long memberId, long deptId) {
        this.memberId = memberId;
        this.deptId = deptId;
    }
}

說明:MemberDeptKey類中的欄位必須為MemberDeptPO類中的聯合主鍵且欄位名稱需保持一致。

實體類最終正確程式碼

增加@IdClass(value = MemberDeptKey.class)和@Id註解

/**
* 使用者部門實體
* @author lizebin
* @version 1.0
* @date 2021/2/18 12:48 上午
*
**/
@Getter
@Setter
@Entity
@Table(name = "mb_member_dept")
@IdClass(value = MemberDeptKey.class) // 定義聯合主鍵類
public class MemberDeptPO implements Serializable{
  private static final long serialVersionUID = 1271571231859316736L;
  /**
  * 聯合主鍵使用者ID
  */
  @Id // 定義該欄位為主鍵
  @Column(name = "member_id", length = 20)
  private long memberId;
  /**
  * 聯合主鍵部門ID
  */
  @Id // 定義該欄位為主鍵
  @Column(name = "dept_id", length = 20)
  private long deptId;
}

持久層設定

@Repository
public interface IMemberDeptRepository extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
}

由於這裡使用類聯合主鍵,上面程式碼中的ID不能在使用Long而是需要使用MemberDeptKey進行定義,最終程式碼如下:

@Repository
public interface IMemberDeptRepository extends JpaRepository<MemberDeptPO, MemberDeptKey>, JpaSpecificationExecutor<MemberDeptPO> {
    /**通過ID獲取資料資訊*/
    MemberDeptPO findById(MemberDeptKey id);
}

當需要通過ID獲取資料時則如下呼叫即可:

@Autowired
private IMemberDeptRepository memberDeptRepository;
MemberDeptPO memberDeptPO = memberDeptRepository.findById(new MemberDeptKey(1l, 0l));

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com