父表的主键是子表的主键,如何使用jpa进行映射?

Posted

技术标签:

【中文标题】父表的主键是子表的主键,如何使用jpa进行映射?【英文标题】:The primary key of the parent table is the primary key of the child table, How can I map it using jpa? 【发布时间】:2021-12-10 00:19:52 【问题描述】:

这是图表:

参考:https://docs.spring.io/spring-batch/docs/current/reference/html/index-single.html

我从 batch_job_execution_params 表中获取列表,但数据与第一行中的信息重复,计数正确。

我的实体类如下:

BATCH_JOB_EXECUTION 表

package com.maxcom.interfact_services.entity;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;

import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

@Entity
@Table(name = "BATCH_JOB_EXECUTION")
public class BatchJobExecutionEntity implements Serializable 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "JOB_EXECUTION_ID", nullable = false)
    private Long jobExecutionId;

    @Column(name = "VERSION")
    private Long version;

    @ManyToOne
    @JoinColumn(name = "JOB_INSTANCE_ID", nullable = false, updatable = false)
    private BatchJobInstanceEntity jobInstanceId;

    @Column(name = "CREATE_TIME")
    @Temporal(TemporalType.TIMESTAMP)
    private Date createTime;

    @Column(name = "START_TIME")
    @Temporal(TemporalType.TIMESTAMP)
    private Date startTime;

    @Column(name = "END_TIME")
    @Temporal(TemporalType.TIMESTAMP)
    private Date endTime;

    @Column(name = "STATUS")
    private String status;

    @Column(name = "EXIT_CODE")
    private String exitCode;

    @Column(name = "EXIT_MESSAGE")
    private String exitMessage;

    @Column(name = "LAST_UPDATED")
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastUpdated;

    @Column(name = "JOB_CONFIGURATION_LOCATION")
    private String jobConfigurationLocation;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "batchJobExecutionEntity", orphanRemoval = true)
    private Set<BatchStepExecutionEntity> batchSteps;

    @JsonManagedReference
    @MapsId("jobExecutionId")
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "jobExecutionId", orphanRemoval = true)
    // @JsonIgnoreProperties("batchJobExecutionEntity")
    private List<BatchJobExecutionParamsEntity> batchJobParams = new ArrayList<>();

    public BatchJobExecutionEntity() 
    

    public Long getJobExecutionId() 
        return jobExecutionId;
    

    public void setJobExecutionId(Long jobExecutionId) 
        this.jobExecutionId = jobExecutionId;
    

    public Long getVersion() 
        return version;
    

    public void setVersion(Long version) 
        this.version = version;
    

    @JsonBackReference
    public BatchJobInstanceEntity getJobInstanceId() 
        return jobInstanceId;
    

    public void setJobInstanceId(BatchJobInstanceEntity jobInstanceId) 
        this.jobInstanceId = jobInstanceId;
    

    public Date getCreateTime() 
        return createTime;
    

    public void setCreateTime(Date createTime) 
        this.createTime = createTime;
    

    public Date getStartTime() 
        return startTime;
    

    public void setStartTime(Date startTime) 
        this.startTime = startTime;
    

    public Date getEndTime() 
        return endTime;
    

    public void setEndTime(Date endTime) 
        this.endTime = endTime;
    

    public String getStatus() 
        return status;
    

    public void setStatus(String status) 
        this.status = status;
    

    public String getExitCode() 
        return exitCode;
    

    public void setExitCode(String exitCode) 
        this.exitCode = exitCode;
    

    public String getExitMessage() 
        return exitMessage;
    

    public void setExitMessage(String exitMessage) 
        this.exitMessage = exitMessage;
    

    public Date getLastUpdated() 
        return lastUpdated;
    

    public void setLastUpdated(Date lastUpdated) 
        this.lastUpdated = lastUpdated;
    

    public String getJobConfigurationLocation() 
        return jobConfigurationLocation;
    

    public void setJobConfigurationLocation(String jobConfigurationLocation) 
        this.jobConfigurationLocation = jobConfigurationLocation;
    

    @JsonManagedReference
    public Set<BatchStepExecutionEntity> getBatchSteps() 
        return batchSteps;
    

    public void setBatchSteps(Set<BatchStepExecutionEntity> batchSteps) 
        this.batchSteps = batchSteps;
    

    public List<BatchJobExecutionParamsEntity> getBatchJobParams() 
        return batchJobParams;
    

    public void setBatchJobParams(List<BatchJobExecutionParamsEntity> batchJobParams) 
        this.batchJobParams = batchJobParams;
    



BATCH_JOB_EXECUTION_PARAMS 表

package com.maxcom.interfact_services.entity;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@Entity
@Table(name = "BATCH_JOB_EXECUTION_PARAMS")
public class BatchJobExecutionParamsEntity implements Serializable 

    @Id
    // @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "JOB_EXECUTION_ID", length = 20)
    private Long jobExecutionId;

    // @JsonBackReference
    // @ManyToOne(fetch = FetchType.LAZY)
    // @JoinColumn(name= "JOB_EXECUTION_ID", insertable = false, updatable = false)
    // @JsonIgnoreProperties("batchJobParams")
    // private BatchJobExecutionEntity batchJobExecutionEntity;

    @Column(name = "TYPE_CD", length = 6)
    private String typeCd;

    @Column(name = "KEY_NAME", length = 100)
    private String keyName;

    @Column(name = "STRING_VAL", length = 250)
    private String stringVal;

    @Column(name = "DATE_VAL")
    @Temporal(TemporalType.TIMESTAMP)
    private Date dateVal;

    @Column(name = "LONG_VAL")
    private Long longVal;

    @Column(name = "DOUBLE_VAL")
    private Double doubleVal;

    @Column(name = "IDENTIFYING", columnDefinition = "char(1)")
    private String identifying;

    public BatchJobExecutionParamsEntity() 
    

    public Long getJobExecutionId() 
        return jobExecutionId;
    

    public void setJobExecutionId(Long jobExecutionId) 
        this.jobExecutionId = jobExecutionId;
    

    public String getTypeCd() 
        return typeCd;
    

    public void setTypeCd(String typeCd) 
        this.typeCd = typeCd;
    

    public String getKeyName() 
        return keyName;
    

    public void setKeyName(String keyName) 
        this.keyName = keyName;
    

    public String getStringVal() 
        return stringVal;
    

    public void setStringVal(String stringVal) 
        this.stringVal = stringVal;
    

    public Date getDateVal() 
        return dateVal;
    

    public void setDateVal(Date dateVal) 
        this.dateVal = dateVal;
    

    public Long getLongVal() 
        return longVal;
    

    public void setLongVal(Long longVal) 
        this.longVal = longVal;
    

    public Double getDoubleVal() 
        return doubleVal;
    

    public void setDoubleVal(Double doubleVal) 
        this.doubleVal = doubleVal;
    

    public String getIdentifying() 
        return identifying;
    

    public void setIdentifying(String identifying) 
        this.identifying = identifying;
    

    /*
    public BatchJobExecutionEntity getBatchJobExecutionEntity() 
        return batchJobExecutionEntity;
    

    public void setBatchJobExecutionEntity(BatchJobExecutionEntity batchJobExecutionEntity) 
        this.batchJobExecutionEntity = batchJobExecutionEntity;
    
     */




如何映射此场景的1:N

【问题讨论】:

对于 1:N,BatchJobExecutionParamsEntity 需要的不仅仅是 jobExecutionId 作为@Id。唯一标识它的其他字段是什么? 感谢@BrianVosburgh,解决方案是 EmbeddedId。下面我添加我的解决方案,再次感谢您的回复。 【参考方案1】:

拥有非唯一主键是灾难的根源 (/duplicates)。 你错过了Appendix B.3:

请注意,此表没有主键。这是因为框架对一个没有用处,因此不需要它。如果需要,您可以添加一个主键,该主键可以与数据库生成的键一起添加,而不会对框架本身造成任何问题。

因此,您可以将生成的主键添加到表中并将其用作 Id,或者在 BatchJobExecutionParamsEntity 上创建一个 EmbeddedId / IdClass 以及所有字段。

【讨论】:

【参考方案2】:

我的解决方案基于@Lookslikeitsnot 的回答以及以下文章:https://fullstackdeveloper.guru/2021/08/25/what-is-mapsid-used-for-in-jpa-hibernate-part-ii/

这个场景中的关键字是:

@Embeddable @EmbeddedId @MapsId

我分享我的最终代码:

- BatchJobExecutionParamsEntity:(子表)

package com.maxcom.interfact_services.entity;

import com.fasterxml.jackson.annotation.JsonBackReference;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name = "BATCH_JOB_EXECUTION_PARAMS")
public class BatchJobExecutionParamsEntity implements Serializable 

    @EmbeddedId
    private BatchJobParamIdEmbedded id;

    @JsonBackReference
    @ManyToOne
    @MapsId("jobExecutionId")
    @JoinColumn(name= "JOB_EXECUTION_ID", insertable = false, updatable = false)
    private BatchJobExecutionEntity batchJobExecutionEntity;


    public BatchJobExecutionParamsEntity() 
    

    public BatchJobParamIdEmbedded getId() 
        return id;
    

    public void setId(BatchJobParamIdEmbedded id) 
        this.id = id;
    

    public BatchJobExecutionEntity getBatchJobExecutionEntity() 
        return batchJobExecutionEntity;
    

    public void setBatchJobExecutionEntity(BatchJobExecutionEntity batchJobExecutionEntity) 
        this.batchJobExecutionEntity = batchJobExecutionEntity;
    


- BatchJobParamIdEmbedded:(嵌入式实体)

package com.maxcom.interfact_services.entity;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.io.Serializable;
import java.util.Date;

@Embeddable
public class BatchJobParamIdEmbedded implements Serializable 

    private Long jobExecutionId; // composite key class

    @Column(name = "TYPE_CD", length = 6)
    private String typeCd;

    @Column(name = "KEY_NAME", length = 100)
    private String keyName;

    @Column(name = "STRING_VAL", length = 250)
    private String stringVal;

    @Column(name = "DATE_VAL")
    @Temporal(TemporalType.TIMESTAMP)
    private Date dateVal;

    @Column(name = "LONG_VAL")
    private Long longVal;

    @Column(name = "DOUBLE_VAL")
    private Double doubleVal;

    @Column(name = "IDENTIFYING", columnDefinition = "char(1)")
    private String identifying;

    public Long getJobExecutionId() 
        return jobExecutionId;
    

    public void setJobExecutionId(Long jobExecutionId) 
        this.jobExecutionId = jobExecutionId;
    

    public String getTypeCd() 
        return typeCd;
    

    public void setTypeCd(String typeCd) 
        this.typeCd = typeCd;
    

    public String getKeyName() 
        return keyName;
    

    public void setKeyName(String keyName) 
        this.keyName = keyName;
    

    public String getStringVal() 
        return stringVal;
    

    public void setStringVal(String stringVal) 
        this.stringVal = stringVal;
    

    public Date getDateVal() 
        return dateVal;
    

    public void setDateVal(Date dateVal) 
        this.dateVal = dateVal;
    

    public Long getLongVal() 
        return longVal;
    

    public void setLongVal(Long longVal) 
        this.longVal = longVal;
    

    public Double getDoubleVal() 
        return doubleVal;
    

    public void setDoubleVal(Double doubleVal) 
        this.doubleVal = doubleVal;
    

    public String getIdentifying() 
        return identifying;
    

    public void setIdentifying(String identifying) 
        this.identifying = identifying;
    

- BatchJobExecutionEntity:(父表)

package com.maxcom.interfact_services.entity;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;

import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Entity
@Table(name = "BATCH_JOB_EXECUTION")
public class BatchJobExecutionEntity implements Serializable 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "JOB_EXECUTION_ID", nullable = false)
    private Long jobExecutionId;

    @Column(name = "VERSION")
    private Long version;

    @ManyToOne
    @JoinColumn(name = "JOB_INSTANCE_ID", nullable = false, updatable = false)
    private BatchJobInstanceEntity jobInstanceId;

    @Column(name = "CREATE_TIME")
    @Temporal(TemporalType.TIMESTAMP)
    private Date createTime;

    @Column(name = "START_TIME")
    @Temporal(TemporalType.TIMESTAMP)
    private Date startTime;

    @Column(name = "END_TIME")
    @Temporal(TemporalType.TIMESTAMP)
    private Date endTime;

    @Column(name = "STATUS")
    private String status;

    @Column(name = "EXIT_CODE")
    private String exitCode;

    @Column(name = "EXIT_MESSAGE")
    private String exitMessage;

    @Column(name = "LAST_UPDATED")
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastUpdated;

    @Column(name = "JOB_CONFIGURATION_LOCATION")
    private String jobConfigurationLocation;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "batchJobExecutionEntity", orphanRemoval = true)
    private List<BatchStepExecutionEntity> batchSteps;

    @JsonManagedReference
    @OneToMany(mappedBy = "batchJobExecutionEntity", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
    private List<BatchJobExecutionParamsEntity> batchJobParams = new ArrayList<>();

    public BatchJobExecutionEntity() 
    

    public Long getJobExecutionId() 
        return jobExecutionId;
    

    public void setJobExecutionId(Long jobExecutionId) 
        this.jobExecutionId = jobExecutionId;
    

    public Long getVersion() 
        return version;
    

    public void setVersion(Long version) 
        this.version = version;
    

    @JsonBackReference
    public BatchJobInstanceEntity getJobInstanceId() 
        return jobInstanceId;
    

    public void setJobInstanceId(BatchJobInstanceEntity jobInstanceId) 
        this.jobInstanceId = jobInstanceId;
    

    public Date getCreateTime() 
        return createTime;
    

    public void setCreateTime(Date createTime) 
        this.createTime = createTime;
    

    public Date getStartTime() 
        return startTime;
    

    public void setStartTime(Date startTime) 
        this.startTime = startTime;
    

    public Date getEndTime() 
        return endTime;
    

    public void setEndTime(Date endTime) 
        this.endTime = endTime;
    

    public String getStatus() 
        return status;
    

    public void setStatus(String status) 
        this.status = status;
    

    public String getExitCode() 
        return exitCode;
    

    public void setExitCode(String exitCode) 
        this.exitCode = exitCode;
    

    public String getExitMessage() 
        return exitMessage;
    

    public void setExitMessage(String exitMessage) 
        this.exitMessage = exitMessage;
    

    public Date getLastUpdated() 
        return lastUpdated;
    

    public void setLastUpdated(Date lastUpdated) 
        this.lastUpdated = lastUpdated;
    

    public String getJobConfigurationLocation() 
        return jobConfigurationLocation;
    

    public void setJobConfigurationLocation(String jobConfigurationLocation) 
        this.jobConfigurationLocation = jobConfigurationLocation;
    

    @JsonManagedReference
    public List<BatchStepExecutionEntity> getBatchSteps() 
        return batchSteps;
    

    public void setBatchSteps(List<BatchStepExecutionEntity> batchSteps) 
        this.batchSteps = batchSteps;
    

    public List<BatchJobExecutionParamsEntity> getBatchJobParams() 
        return batchJobParams;
    

    public void setBatchJobParams(List<BatchJobExecutionParamsEntity> batchJobParams) 
        this.batchJobParams = batchJobParams;
    

    /* Add an batchJobParam to jobExecution Entity to maintain the bi-directional OneToMapping */
    public void addBatchJobExecutionParam(BatchJobExecutionParamsEntity batchJobParam) 
        this.batchJobParams.add(batchJobParam);
        batchJobParam.setBatchJobExecutionEntity(this);
    

如下图所示,我现在得到不同的对象,当然是封装在id(BatchJobParamIdEmbedded)中:

第二张图片显示更多细节:

感谢大家的支持。

最好的问候

【讨论】:

以上是关于父表的主键是子表的主键,如何使用jpa进行映射?的主要内容,如果未能解决你的问题,请参考以下文章

SQL如果建立子表

子表,父表;一对多,多对一;主键,外键梳理。

Mysql-外键foreign key

mysql中的外键foreign key

JPA映射:一个表的主键列到另一个表的非Pk/Fk列

使用外键约束