首頁 > 軟體

spring jpa設定多個主鍵遇到的小坑及解決

2022-06-13 14:03:20

jpa設定多個主鍵遇到的坑

由於專案需要,對多個業務表單獨另外建立對應的歷史版本記錄表,為了原業務表資料能原封不動記錄到歷史版本表,需要建立組合主鍵,id+歷史版本號作為主鍵唯一約束(rid+historyVersion)。

在實體上需要設定為主鍵的欄位加上註解,@Id,例如:

/*
* 主鍵-RID
*/
@Id
@Column(name = "RID", length = 36)
private String rid;

這樣會導致,若是該實體存在父類別,那就會啟動報錯,初始化不了

錯誤資訊:does not define an IdClass。

解決辦法

必須要在類聲名注入@IdClass(HistoryPK.class)。

實體程式碼例子如下:

package com.southgis.officeHouse.entity;
import java.io.Serializable;
import javax.persistence.Id;
import lombok.Data;
import lombok.NoArgsConstructor;
 
/**
 * 
 * @author Administrator
 *	組合主鍵
 */
@Data
public class HistoryPK implements Serializable
{
	private static final long serialVersionUID = 1L;
	
	/*
	 * 主鍵-RID
	 */
	private String rid;
	
	/*
	 * 主鍵-歷史版本號,儲存格式年份_版本號,例如2018_1
	 */
	private String historyVersion;
}
package com.southgis.officeHouse.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Index;
import javax.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
 
/**
*
* @author Administrator
*	單位基本資訊歷史版本表
*/
 
@Data
@EqualsAndHashCode(callSuper=false)
@NoArgsConstructor
@Entity
@IdClass(HistoryPK.class)
 
@Table(name = "UNIT_HISTORY",indexes={
@Index(name="inx_unitHistory_orgid",columnList="ORGID")})
 
public class UnitHistory extends UnitBase implements Serializable
{
private static final long serialVersionUID = -4466904221026481006L;
/*
* 主鍵-RID
*/
 
@Id
@Column(name = "RID", length = 36)
private String rid;
/*
* 主鍵-歷史版本號,儲存格式年份_版本號,例如2018_1
*/
 
@Id
@Column(name = "HISTORY_VERSION",length=36)
private String historyVersion;
}

jpa遇到多主鍵表如何進行查詢

資料表是原始就存在的,裡面存在兩個主鍵:

當建好實體類,然後用jpa去關聯操作查詢,(根據StudyId)去進行查詢的時候,發現原本可以有八條不一樣的記錄,只是StudyId相同,其他的不同,這個時候,出來確實是八條,但是居然每一條都一樣,是根據StudyId一樣的資料記錄裡的都一條。

即當根據StudyId='194205'去查的時候,JPA都會返回八條一樣的記錄

JPA程式碼:

@Query(value = "select * from tbl_ic_film_info where StudyId = ?1",nativeQuery = true)
List<IcFilmInfo> findByStudyIdSQL(String studyId);

service程式碼:

List<IcFilmInfo> icFilmInfoList= icFilmInfoRepository.findByStudyIdSQL(studyId);

然後迴圈列印icFilmInfoList

FileName查出來都是1

後來才發現是聯合主鍵惹得鍋,資料庫表中有兩個主鍵,一個是StudyId,還有一個是FileName。

我的實體類是這麼定義的(因為我只需要StudyId和FileName的資訊就行了):

當JPA在根據StudyId去查詢的時候,只會將StudyId當做主鍵,當StudyId一樣的時候,JPA會當做所有的都是同一條記錄,不會管FileName是否相同了,一股腦的返回八條一樣的資料。

對此其實有很多種解決辦法,說下我使用的幾種:

1、使用 List<Map<String, Object>>的方式去接收

由於這邊資料庫的記錄值都字串型別,我就直接使用List<Map<String, String>>了~

JPA程式碼:

@Query(value = "select StudyId,FileName from tbl_ic_film_info where StudyId = ?1",nativeQuery = true)
List<Map<String,String>> findByStudyIdMap(String studyId);

迴圈答應查詢結果:

這種方式有點就是程式碼簡單,但是如果要對查詢的結果再進行一步處理的話,就會變的更復雜,不好處理。

2、自定義接收類

新建一個IcFilmInfoVO類:

@AllArgsConstructor
@Data
public class IcFilmInfoVO {
    private String studyId;
    /**
     * 檔名
     */
    private String fileName;
}

注意新增的全參構造方法註解@AllArgsConstructor

JAP程式碼

@Query(value = "select new cloud.image.vo.IcFilmInfoVO(t.studyId,t.fileName) from IcFilmInfo t where t.studyId = ?1")
List<IcFilmInfoVO> findByStudyId(String studyId);

注意,這個時候這裡的@Query註解裡面是沒有加nativeQuery = true的

然後迴圈列印查詢結果:

這樣即使以後要操作查詢的結果也很方便,同時這種自定義接收類的用法還可以用於統計等業務場景,可以接收sum,count等SQL內建函數查詢出來的結果。

3、設定聯合主鍵

由於表中是兩個主鍵的存在,接下來改造一下我們的實體類:

@Entity
@Data
@IdClass(IcFilmInfoPk.class)
@Table(name = "tbl_ic_film_info")
public class IcFilmInfo implements Serializable {
    private static final long serialVersionUID = 9121531612760132363L;
    @Id
    @Column(name = "StudyId")
    private String studyId;
    /**
     * 檔名
     */
    @Id
    @Column(name = "FileName")
    private String fileName;
}

在兩個主鍵對映的欄位上都標註@Id註解

同時新建一個IcFilmInfoPk類:

@Data
public class IcFilmInfoPk implements Serializable {
    private static final long serialVersionUID = -1570834456846579284L;
    private String studyId;
    private String fileName;
}

在實體類上加上@IdClass(IcFilmInfoPk.class)註解,這個時候就可以用一下JPA程式碼直接去查詢了

@Query(value = "select * from tbl_ic_film_info where StudyId = ?1",nativeQuery = true)
List<IcFilmInfo> findByStudyIdSQL(String studyId);

總結:針對上面三種方法,貌似都可以解決我們的問題,但是個人只推薦第三種,應為第三種是最貼合資料庫的,使用了聯合主鍵的註解,資料庫中也是多主鍵,但是第二種方式可以很好的解決我們在使用JPA去查詢的時候介面其他非資料庫欄位的資訊,例如統計等方面。

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


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