Первичный ключ родительской таблицы является первичным ключом дочерней таблицы, как я могу сопоставить его с помощью jpa?

#spring #spring-boot #hibernate #jpa #spring-batch

Вопрос:

Это диаграмма:

введите описание изображения здесь

Ссылки: 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. Для 1: N BatchJobExecutionParamsEntity требуется нечто большее, чем просто jobExecutionId как @Id . Какие другие поля однозначно идентифицируют его?

2. Спасибо @BrianVosburgh, решение было встроено id . Ниже я добавляю свое решение, еще раз спасибо за ваш ответ.

Ответ №1:

Наличие неуникального первичного ключа — это путь к катастрофе (/duplicates ).
Вы пропустили приложение B.3:

Обратите внимание, что для этой таблицы нет первичного ключа. Это связано с тем, что фреймворк не использует его и, следовательно, не требует его. При необходимости вы можете добавить первичный ключ, который может быть добавлен с помощью ключа, сгенерированного базой данных, не вызывая никаких проблем с самой платформой.

Таким образом, вы можете либо добавить сгенерированный первичный ключ в таблицу и использовать его в качестве идентификатора, либо создать EmbeddedId / IdClass в BatchJobExecutionParamsEntity со всеми полями.

Ответ №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);
    }
}
 

Как показано на следующем рисунке, теперь я получаю разные объекты, конечно, инкапсулированные в идентификатор (BatchJobParamIdEmbedded):

введите описание изображения здесь

На втором рисунке показано больше деталей:

введите описание изображения здесь

Спасибо всем за вашу поддержку.

С наилучшими пожеланиями