#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):
На втором рисунке показано больше деталей:
Спасибо всем за вашу поддержку.
С наилучшими пожеланиями