spring?jpa設(shè)置多個(gè)主鍵遇到的小坑及解決
jpa設(shè)置多個(gè)主鍵遇到的坑
由于項(xiàng)目需要,對(duì)多個(gè)業(yè)務(wù)表單獨(dú)另外建立對(duì)應(yīng)的歷史版本記錄表,為了原業(yè)務(wù)表數(shù)據(jù)能原封不動(dòng)記錄到歷史版本表,需要建立組合主鍵,id+歷史版本號(hào)作為主鍵唯一約束(rid+historyVersion)。
在實(shí)體上需要設(shè)置為主鍵的字段加上注解,@Id,例如:
/* * 主鍵-RID */ @Id @Column(name = "RID", length = 36) private String rid;
這樣會(huì)導(dǎo)致,若是該實(shí)體存在父類,那就會(huì)啟動(dòng)報(bào)錯(cuò),初始化不了
錯(cuò)誤信息:does not define an IdClass。
解決辦法
必須要在類聲名注入@IdClass(HistoryPK.class)。
實(shí)體代碼例子如下:
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; /* * 主鍵-歷史版本號(hào),保存格式年份_版本號(hào),例如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; /* * 主鍵-歷史版本號(hào),保存格式年份_版本號(hào),例如2018_1 */ @Id @Column(name = "HISTORY_VERSION",length=36) private String historyVersion; }
jpa遇到多主鍵表如何進(jìn)行查詢
數(shù)據(jù)表是原始就存在的,里面存在兩個(gè)主鍵:
當(dāng)建好實(shí)體類,然后用jpa去關(guān)聯(lián)操作查詢,(根據(jù)StudyId)去進(jìn)行查詢的時(shí)候,發(fā)現(xiàn)原本可以有八條不一樣的記錄,只是StudyId相同,其他的不同,這個(gè)時(shí)候,出來確實(shí)是八條,但是居然每一條都一樣,是根據(jù)StudyId一樣的數(shù)據(jù)記錄里的都一條。
即當(dāng)根據(jù)StudyId='194205'去查的時(shí)候,JPA都會(huì)返回八條一樣的記錄
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);
然后循環(huán)打印icFilmInfoList
FileName查出來都是1
后來才發(fā)現(xiàn)是聯(lián)合主鍵惹得鍋,數(shù)據(jù)庫(kù)表中有兩個(gè)主鍵,一個(gè)是StudyId,還有一個(gè)是FileName。
我的實(shí)體類是這么定義的(因?yàn)槲抑恍枰猄tudyId和FileName的信息就行了):
當(dāng)JPA在根據(jù)StudyId去查詢的時(shí)候,只會(huì)將StudyId當(dāng)做主鍵,當(dāng)StudyId一樣的時(shí)候,JPA會(huì)當(dāng)做所有的都是同一條記錄,不會(huì)管FileName是否相同了,一股腦的返回八條一樣的數(shù)據(jù)。
對(duì)此其實(shí)有很多種解決辦法,說下我使用的幾種:
1、使用 List<Map<String, Object>>的方式去接收
由于這邊數(shù)據(jù)庫(kù)的記錄值都字符串類型,我就直接使用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);
循環(huán)答應(yīng)查詢結(jié)果:
這種方式有點(diǎn)就是代碼簡(jiǎn)單,但是如果要對(duì)查詢的結(jié)果再進(jìn)行一步處理的話,就會(huì)變的更復(fù)雜,不好處理。
2、自定義接收類
新建一個(gè)IcFilmInfoVO類:
@AllArgsConstructor @Data public class IcFilmInfoVO { private String studyId; /** * 文件名 */ private String fileName; }
注意添加的全參構(gòu)造方法注解@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);
注意,這個(gè)時(shí)候這里的@Query注解里面是沒有加nativeQuery = true的
然后循環(huán)打印查詢結(jié)果:
這樣即使以后要操作查詢的結(jié)果也很方便,同時(shí)這種自定義接收類的用法還可以用于統(tǒng)計(jì)等業(yè)務(wù)場(chǎng)景,可以接收sum,count等SQL內(nèi)置函數(shù)查詢出來的結(jié)果。
3、配置聯(lián)合主鍵
由于表中是兩個(gè)主鍵的存在,接下來改造一下我們的實(shí)體類:
@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; }
在兩個(gè)主鍵映射的字段上都標(biāo)注@Id注解
同時(shí)新建一個(gè)IcFilmInfoPk類:
@Data public class IcFilmInfoPk implements Serializable { private static final long serialVersionUID = -1570834456846579284L; private String studyId; private String fileName; }
在實(shí)體類上加上@IdClass(IcFilmInfoPk.class)注解,這個(gè)時(shí)候就可以用一下JPA代碼直接去查詢了
@Query(value = "select * from tbl_ic_film_info where StudyId = ?1",nativeQuery = true) List<IcFilmInfo> findByStudyIdSQL(String studyId);
總結(jié):針對(duì)上面三種方法,貌似都可以解決我們的問題,但是個(gè)人只推薦第三種,應(yīng)為第三種是最貼合數(shù)據(jù)庫(kù)的,使用了聯(lián)合主鍵的注解,數(shù)據(jù)庫(kù)中也是多主鍵,但是第二種方式可以很好的解決我們?cè)谑褂肑PA去查詢的時(shí)候接口其他非數(shù)據(jù)庫(kù)字段的信息,例如統(tǒng)計(jì)等方面。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java組件FileUpload上傳文件實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了Java組件FileUpload上傳文件實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-06-06SpringBoot基于Shiro處理ajax請(qǐng)求代碼實(shí)例
這篇文章主要介紹了SpringBoot基于Shiro處理ajax請(qǐng)求代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06idea中安裝VisualVM監(jiān)控jvm的圖文教程
這篇文章主要介紹了idea中安裝VisualVM監(jiān)控jvm的教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09字節(jié)碼調(diào)教入口JVM?寄生插件javaagent
這篇文章主要介紹了字節(jié)碼調(diào)教入口JVM?寄生插件javaagent方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08springmvc 分頁(yè)查詢的簡(jiǎn)單實(shí)現(xiàn)示例代碼
我們?cè)陂_發(fā)項(xiàng)目中很多項(xiàng)目都用到列表分頁(yè)功能,本篇介紹了springmvc 分頁(yè)查詢的簡(jiǎn)單實(shí)現(xiàn)示例代碼,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2017-01-01使用CI/CD工具Github Action發(fā)布jar到Maven中央倉(cāng)庫(kù)的詳細(xì)介紹
今天通過對(duì)Github Action的簡(jiǎn)單使用來介紹了CI/CD的作用,這個(gè)技術(shù)體系是項(xiàng)目集成交付的趨勢(shì),也是面試中的一個(gè)亮點(diǎn)技能。 而且這種方式可以實(shí)現(xiàn)“一次配置,隨時(shí)隨地集成部署”,感興趣的朋友一起看看吧2021-07-07