The primary key of the parent table is the primary key of the child table, How can I map it using jpa?

Viewed 422

This is the diagram:

enter image description here

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

I get the list from the batch_job_execution_params table but the data is repeated with the information in the first row, the count is correct.

enter image description here

My Entity classes are as follows:

BATCH_JOB_EXECUTION TABLE

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 TABLE

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;
    }
     */

}


How to map 1:N of this scenario?

2 Answers

Having a non-unique primary key is a recipe for disaster (/duplicates).
You missed Appendix B.3:

Note that there is no primary key for this table. This is because the framework has no use for one and, thus, does not require it. If need be, you can add a primary key may be added with a database generated key without causing any issues to the framework itself.

So you can either add a generated primary key to the table and use it as Id or create an EmbeddedId / IdClass on BatchJobExecutionParamsEntity with all the fields.

my solution was the following based on the answer from @Lookslikeitsnot and also to the following article: https://fullstackdeveloper.guru/2021/08/25/what-is-mapsid-used-for-in-jpa-hibernate-part-ii/

The keywords in this scenario were:

  • @Embeddable
  • @EmbeddedId
  • @MapsId

I Share my final code:

- BatchJobExecutionParamsEntity: (Child Table)

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: (Embedded Entity)

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: (Parent Table)

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);
    }
}

As shown in the following image, I now get different objects, of course encapsulated in the id (BatchJobParamIdEmbedded):

enter image description here

The second picture shows more details:

enter image description here

Thank you all for your support.

Best regards

Related