spring jpa设置多个主键遇到的小坑及解决

 更新时间:2022年06月13日 10:10:23   作者:TEN上海哥  
这篇文章主要介绍了spring jpa设置多个主键遇到的小坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

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去查询的时候接口其他非数据库字段的信息,例如统计等方面。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringBoot中配置双数据源的实现示例

    SpringBoot中配置双数据源的实现示例

    在许多应用程序中,可能会遇到需要连接多个数据库的情况,本文主要介绍了SpringBoot中配置双数据源的实现示例,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-08-08
  • 详细分析Java并发集合ArrayBlockingQueue的用法

    详细分析Java并发集合ArrayBlockingQueue的用法

    这篇文章主要介绍了详细分析Java并发集合ArrayBlockingQueue的用法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • 解决Hmily与Feign冲突报错 NullPointerException的问题

    解决Hmily与Feign冲突报错 NullPointerException的问题

    这篇文章主要介绍了解决Hmily与Feign冲突报错 NullPointerException的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Map按单个或多个Value排序当Value相同时按Key排序

    Map按单个或多个Value排序当Value相同时按Key排序

    Map可以先按照value进行排序,然后按照key进行排序。 或者先按照key进行排序,然后按照value进行排序,这样操作都行,这篇文章主要介绍了Map按单个或多个Value排序,当Value相同时按Key排序,需要的朋友可以参考下
    2023-02-02
  • mybatis 字段名自动转小写的实现

    mybatis 字段名自动转小写的实现

    这篇文章主要介绍了mybatis 字段名自动转小写的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • scala 隐式转换与隐式参数的使用方法

    scala 隐式转换与隐式参数的使用方法

    这篇文章主要介绍了scala 隐式转换与隐式参数的使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • 一文搞懂Spring中的Bean作用域

    一文搞懂Spring中的Bean作用域

    scope用来声明容器中的对象所应该处的限定场景或者说该对象的存活时间,即容器在对象进入其 相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象,这篇文章主要介绍了Spring中的Bean作用域,需要的朋友可以参考下
    2022-06-06
  • 在Java项目中实现CI/CD持续集成与持续部署

    在Java项目中实现CI/CD持续集成与持续部署

    这篇文章主要为大家介绍了在Java项目中实现CI/CD持续集成与持续部署详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • java8根据某一属性过滤去重的实例

    java8根据某一属性过滤去重的实例

    这篇文章主要介绍了java8根据某一属性过滤去重的实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • Spring Cloud Eureka服务注册中心入门流程分析

    Spring Cloud Eureka服务注册中心入门流程分析

    这篇文章主要介绍了Spring Cloud Eureka服务注册中心入门流程分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06

最新评论