Изменение всех id сущностей в spring
Всем привет Я новичок в spring. Пробую писать приложение. Столкнулся с проблемой. Есть сущность Statement. Она содержит список List. Проблема в том, что если введено 2 статуса с одинаковым idStatus и редактирую один из них, то происходит автоматическое изменение idStatus другой записи из List. У сущностей переопределены equals и hashCode, но проблема не решилась. Подскажите в какую сторону копать. Вот собственно куски кода
package by.ic.model;
import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.lang.Nullable;
@MappedSuperclass
public abstract class AbstractBaseEntity implements Serializable {
private static final long serialVersionUID = 5269025572367966131L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
protected Integer id;
@Column(name = "note")
@Nullable
private String note;
@Column(name = "d_form")
@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(pattern = "dd.MM.yyyy HH:mm:ss")
private Date dForm;
@Column(name = "d_change")
@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(pattern = "dd.MM.yyyy HH:mm:ss")
private Date dChange;
@Column(name = "pa")
private Integer pa;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public Date getdForm() {
return dForm;
}
public void setdForm(Date dForm) {
this.dForm = dForm;
}
public Date getdChange() {
return dChange;
}
public void setdChange(Date dChange) {
this.dChange = dChange;
}
public Integer getPa() {
return pa;
}
public void setPa(Integer pa) {
this.pa = pa;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AbstractBaseEntity other = (AbstractBaseEntity) obj;
return Objects.equals(id, other.id);
}
}
Сущность Statement
package by.ic.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.springframework.format.annotation.DateTimeFormat;
import by.ic.model.user.User;
@Entity(name = "Statement")
@Table(name = "statement")
public class Statement extends AbstractBaseEntity implements Serializable {
private static final long serialVersionUID = 1612993944763267493L;
@ManyToOne(targetEntity = Organ.class, fetch = FetchType.EAGER)
@JoinColumn(name = "code_organ", referencedColumnName = "id")
private Organ organ;
@ManyToOne(targetEntity = StatementType.class, fetch = FetchType.EAGER)
@JoinColumn(name = "code_type", referencedColumnName = "id")
private StatementType type;
@Column(name = "year_reg")
private Integer yearReg;
@Column(name = "reg_number")
private Integer regNumber;
/*@Valid
@NotNull(message = "'Дата' должна быть заполнена")*/
@Column(name = "date_receipt")
@Temporal(TemporalType.DATE)
@DateTimeFormat(pattern = "dd.MM.yyyy")
private Date dateReceipt;
@Column(name = "date_reg")
@Temporal(TemporalType.DATE)
@DateTimeFormat(pattern = "dd.MM.yyyy")
private Date dateReg;
@Valid
@OneToOne(targetEntity = Person.class, cascade = CascadeType.ALL)
@JoinColumn(name = "id_person", nullable = false)
private Person person;
@Valid
@ManyToOne(targetEntity = PaymentType.class)
@JoinColumn(name = "сode_payment", referencedColumnName = "id")
private PaymentType paymentType;
@NotNull(message = "'№ бланка' должен быть заполнен")
@Column(name = "blank_number")
private Long blankNumber;
@ManyToOne(targetEntity = User.class, fetch = FetchType.EAGER)
@JoinColumn(name = "id_user", referencedColumnName = "id", updatable = false, insertable = false)
private User user;
@OneToMany(mappedBy = "statement", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Status> statusList = new ArrayList<>();
@OneToOne(mappedBy = "statement", cascade = CascadeType.ALL)
private MarkLocation markLocation;
@OneToOne(mappedBy = "statement", cascade = CascadeType.ALL)
private MarkDestroy markDestroy;
public Organ getOrgan() {
return organ;
}
public void setOrgan(Organ organ) {
this.organ = organ;
}
public StatementType getType() {
return type;
}
public void setType(StatementType type) {
this.type = type;
}
public Integer getYearReg() {
return yearReg;
}
public void setYearReg(Integer yearReg) {
this.yearReg = yearReg;
}
public Integer getRegNumber() {
return regNumber;
}
public void setRegNumber(Integer regNumber) {
this.regNumber = regNumber;
}
public Date getDateReceipt() {
return dateReceipt;
}
public void setDateReceipt(Date dateReceipt) {
this.dateReceipt = dateReceipt;
}
public Date getDateReg() {
return dateReg;
}
public void setDateReg(Date dateReg) {
this.dateReg = dateReg;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public PaymentType getPaymentType() {
return paymentType;
}
public void setPaymentType(PaymentType paymentType) {
this.paymentType = paymentType;
}
public Long getBlankNumber() {
return blankNumber;
}
public void setBlankNumber(Long blankNumber) {
this.blankNumber = blankNumber;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<Status> getStatusList() {
return statusList;
}
public void setStatusList(List<Status> statusList) {
this.statusList = statusList;
}
public MarkLocation getMarkLocation() {
return markLocation;
}
public void setMarkLocation(MarkLocation markLocation) {
this.markLocation = markLocation;
}
public MarkDestroy getMarkDestroy() {
return markDestroy;
}
public void setMarkDestroy(MarkDestroy markDestroy) {
this.markDestroy = markDestroy;
}
}
Сущность Status
package by.ic.model;
import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.springframework.format.annotation.DateTimeFormat;
import by.ic.model.user.User;
@Entity
@Table(name = "status")
public class Status extends AbstractBaseEntity implements Serializable, Comparable<Status> {
private static final long serialVersionUID = 3687958201400103943L;
@ManyToOne(targetEntity = StatusType.class, fetch = FetchType.EAGER)
@JoinColumn(name = "code_status", referencedColumnName = "id")
private StatusType statusType;
@Column(name = "prefix_out_number")
private String prefixOutNumber;
@Column(name = "out_number")
private String outNumber;
@ManyToOne(targetEntity = Organ.class, fetch = FetchType.EAGER)
@JoinColumn(name = "code_organ", referencedColumnName = "id", nullable = true)
private Organ organ;
@Column(name = "date_status")
@Temporal(TemporalType.DATE)
@DateTimeFormat(pattern = "dd.MM.yyyy")
private Date dateStatus;
@ManyToOne(targetEntity = User.class, fetch = FetchType.EAGER)
@JoinColumn(name = "id_user", referencedColumnName = "id")
private User user;
@ManyToOne(targetEntity = Statement.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL )
@JoinColumn(name = "id_statement", referencedColumnName = "id" )
private Statement statement;
public StatusType getStatusType() {
return statusType;
}
public void setStatusType(StatusType statusType) {
this.statusType = statusType;
}
public String getPrefixOutNumber() {
return prefixOutNumber;
}
public void setPrefixOutNumber(String prefixOutNumber) {
this.prefixOutNumber = prefixOutNumber;
}
public String getOutNumber() {
return outNumber;
}
public void setOutNumber(String outNumber) {
this.outNumber = outNumber;
}
public Organ getOrgan() {
return organ;
}
public void setOrgan(Organ organ) {
this.organ = organ;
}
public Date getDateStatus() {
return dateStatus;
}
public void setDateStatus(Date dateStatus) {
this.dateStatus = dateStatus;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Statement getStatement() {
return statement;
}
public void setStatement(Statement statement) {
this.statement = statement;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + Objects.hash(dateStatus, organ, outNumber,
prefixOutNumber, statement, statusType, user);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
Status other = (Status) obj;
return Objects.equals(dateStatus, other.dateStatus)
&& Objects.equals(organ, other.organ)
&& Objects.equals(outNumber, other.outNumber)
&& Objects.equals(prefixOutNumber, other.prefixOutNumber)
&& Objects.equals(statement, other.statement)
&& Objects.equals(statusType, other.statusType)
&& Objects.equals(user, other.user);
}
@Override
public int compareTo(Status status) {
if (this.getDateStatus().after(status.getDateStatus())) {
return 1;
}
if (this.getDateStatus().before(status.getDateStatus())) {
return -1;
}
return this.getId() - status.getId();
}
}
StatusType
package by.ic.model;
import java.io.Serializable;
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "status_type")
public class StatusType implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Integer id;
@Column(name = "name")
private String name;
@Column(name = "short_name")
private String shortName;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
@Override
public int hashCode() {
return Objects.hash(id, name, shortName);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StatusType other = (StatusType) obj;
return Objects.equals(id, other.id) && Objects.equals(name, other.name)
&& Objects.equals(shortName, other.shortName);
}
}
Кусок контроллера для редактирования Статуса
@RequestMapping(value = "/statement/actions", params = "editStatus")
public String editStatus(HttpSession session,
Model model,
@ModelAttribute("statement") Statement statement,
@RequestParam(name ="indexStatus") Integer indexStatus) {
model.addAttribute("statement", statement);
model.addAttribute("indexStatus", indexStatus);
return "redirect:" + "/statement/status";
}
html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/head :: headFragment (pageTitle='Заявления')">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div class="sticky-top" th:insert="fragments/header :: fragment_header"></div>
<form id="statusForm" th:action="@{/statement/status/save}" th:object="${statement}" method="post">
<input type="text" th:value="${indexStatus}">
<input type="text" th:field="*{statusList[__${indexStatus}__].id}">
<input type="text" th:field="*{statusList[__${indexStatus}__].statement.id}">
<input hidden="true" type="text" th:field="*{statusList[__${indexStatus}__].user.id}">
<input hidden="true" type="text" th:field="*{statusList[__${indexStatus}__].dForm}" th:value="${#dates.format(statement.statusList[__${indexStatus}__].dForm, 'dd.MM.yyyy HH:mm:ss')}">
<input hidden="true" type="text" th:field="*{statusList[__${indexStatus}__].dChange}" th:value="${#dates.format(statement.statusList[__${indexStatus}__].dChange, 'dd.MM.yyyy HH:mm:ss')}">
<input hidden="true" type="text" th:field="*{statusList[__${indexStatus}__].pa}">
<div class="container">
<div class="d-flex justify-content-around">
<button type="submit" class="btn btn-primary cancel_button col-5" id="cancelStatus" name="cancelStatus">
<img th:src="@{/img/icon/icon_left_arrow.png}" height="24"> Назад
</button>
<button type="submit" class="btn btn-primary save_button col-5" id="saveStatus" name="saveStatus">
<img th:src="@{/img/icon/label_save.png}" height="24"> Сохранить
</button>
</div>
<div class="row mt-1 justify-content-center">
<label class="col-2 col-form-label bold">Статус</label>
<select class="col-8 custom-select selectpicker" id="status"
th:onchange="selectText(status, nameStatus); selectValue(status, codeStatus);">
<option th:if="${statement.statusList[__${indexStatus}__].statusType.id == null}" value="0"></option>
<option th:each="t : ${statusTypeList}"
th:value="${t.id}" th:text="${t.name}"
th:selected="${t.id == statement.statusList[__${indexStatus}__].statusType.id}"></option>
</select>
<input type="text" class="col-1" id="codeStatus" th:field="*{statusList[__${indexStatus}__].statusType.id}">
<input type="text" class="col-1" id="nameStatus" th:field="*{statusList[__${indexStatus}__].statusType.name}">
</div>
<div class="row mt-2 justify-content-center">
<label class="col-2 col-form-label bold">Дата</label>
<input type="text" class="col-8 form-control date-element"
th:field="*{statusList[__${indexStatus}__].dateStatus}" onfocus="clearStyle(this);"
th:value="${#dates.format(statement.statusList[__${indexStatus}__].dateStatus, 'dd.MM.yyyy')}"
onkeypress="return keypress(event);" onkeydown="keydown(this, event);" onkeyup="keyup(this, event);" maxlength="10"
id="dateStatus">
</div>
<div class="row mt-1 justify-content-center">
<label class="col-2 col-form-label bold">Префикс</label>
<input type="text" class="col-8 form-control" th:field="*{statusList[__${indexStatus}__].prefixOutNumber}" maxlength="10"
id="prefixOutNumber" onfocus="clearStyle(this);">
</div>
<div class="row mt-1 justify-content-center">
<label class="col-2 col-form-label bold">Исх.№</label>
<input type="text" class="col-8 form-control" th:field="*{statusList[__${indexStatus}__].outNumber}" maxlength="50"
id="outNumber" onfocus="clearStyle(this);">
</div>
<div class="row mt-1 justify-content-center">
<label class="col-2 col-form-label bold">Орган</label>
<select class="col-8 custom-select selectpicker" id="statusOrgan"
onchange="selectText(statusOrgan, statusNameOrgan); selectValue(statusOrgan, statusCodeOrgan); ">
<option value="0"></option>
<option th:each="o : ${organList}"
th:value="${o.id}" th:text="${o.name}"
th:selected="${statement.statusList[__${indexStatus}__].organ != null} ? ${o.id == statement.statusList[__${indexStatus}__].organ.id} : 'false'"></option>
</select>
<input hidden="true" type="text" class="col-3" id="statusCodeOrgan" th:field="*{statusList[__${indexStatus}__].organ.id}">
<input hidden="true" type="text" class="col-2" id="statusNameOrgan" th:field="*{statusList[__${indexStatus}__].organ.name}">
</div>
<div class="row mt-1 justify-content-center">
<label class="col-2 col-form-label bold">Заметка</label>
<input type="text" class="col-8 form-control" th:field="*{statusList[__${indexStatus}__].note}" maxlength="255">
</div>
</div>
</form>
<div class="mb-5" th:insert="fragments/footer :: fragment_footer"></div>
<script type="text/javascript">
function selectValue(source, target) {
target.value = source.options[source.selectedIndex].value;
}
</script>
<script type="text/javascript">
function selectText(source, target) {
target.value = source.options[source.selectedIndex].text;
}
</script>
<script type="text/javascript">
function selectChange(select) {
if (select.options[select.selectedIndex].value == 5) {
document.getElementById('prefixOutNumber').value = 'исх.'
}
}
</script>
<!--
<script type="text/javascript">
window.addEventListener("load", function() {
}, false);
</script>
-->
</body>
</html>
Кусок контроллера сохранения статуса
@RequestMapping(value = "/statement/status/save", params = "saveStatus")
public String saveStatus(HttpSession session,
Model model,
@ModelAttribute("statement") Statement statement,
@ModelAttribute("indexStatus") Integer indexStatus) {
Sysdate sysdate = sysdateService.getSysdate();
SecurityUserDetails userDetails = (SecurityUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
model.addAttribute("user", userDetails.getUser());
Status status = statement.getStatusList().get(indexStatus);
status.setdChange(sysdate.getSysdate());
status.setUser(userDetails.getUser());
if (null != status.getOrgan()) {
if ((null == status.getOrgan().getId()) || (0 == status.getOrgan().getId())) {
status.setOrgan(null);
}
}
if (status.getId() == null) {
status.setdForm(sysdate.getSysdate());
status.setPa(1);
}
System.out.println("******После сохранения******");
for (Status s : statement.getStatusList()) {
System.out.println("id=" + s.getId() + ", statusId: " + s.getStatusType().getId());
}
model.addAttribute("statement", statement);
return "redirect:" + "/statement/edit";
}
Помогите пожалуйста. Заранее спасибо за помощь. Причем это только касается idStatus. Их даты не меняются