In Spring JPA, I did not find anything about saving entity only if there are modification to the fields
After I get the record from JPA, how can I verify, before I save the entity, if there are modification/changes to the fields. This is my approach
In the hasModifications
function I can compare if obj.field!=cloneObject.field
then only save
Class obj = repository.findById(id);
Class cloneObj = cloneObject(obj);
process(obj);
if (hasModifications(obj,cloneObj))
repository.save(obj);
else
//don't save
I tried adding @DynamicUpdate
annotation at the class level. But it didnt work. Is there any other clean way to do this?
EDIT : I am asking this as I have a field called last_updated_at in MySql DB. And Whenever I call save this field is getting updated with the current timestamp but no other fields values are updated. So thats why I asked this question Here is the table definition
'CREATE TABLE `username` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`info` mediumtext,
`status` varchar(255) NOT NULL,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`version` int NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_updated_at` (`updated_at`),
KEY `idx_created_at` (`created_at`)
)
EDIT 2:
I tried putting @Trasactional
at the method and removed clone and save call. But still updated_at is getting updated in DB
@Transactional
public someMethod(){
Class obj = repository.findById(id);
process(obj);
}
Here is my obj entity
@Entity
@Table(name = "gratification_entity_task")
@JsonIgnoreProperties(
value = {"createdAt", "updatedAt"},
allowGetters = true)
@EntityListeners(AuditingEntityListener.class)
public class GratificationEntityTask {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
private TaskStatus status;
@Convert(converter = InfoConverter.class)
private Info info;
@CreatedDate
private Date createdAt;
@LastModifiedDate
private Date updatedAt;
@Version
private Integer version;
private int retryCount;
@Enumerated(EnumType.STRING)
private BeneficiaryType beneficiaryType;
private String beneficiaryId;
@Convert(converter = GratificationConvertor.class)
private GratificationData primaryGratification;
@Convert(converter = GratificationScheduleConverter.class)
private GratificationSchedule scheduleGratification;
private String referenceId;
private String redemptionType;
private String clientId;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public TaskStatus getStatus() {
return status;
}
public void setStatus(TaskStatus status) {
this.status = status;
}
public Info getInfo() {
return info;
}
public long getExpiryTimestamp() {
if (scheduleGratification != null && scheduleGratification.getExpiryTimestamp() > 0) {
return scheduleGratification.getExpiryTimestamp();
}
return 0;
}
public long getScheduleInSecondsInMillis() {
if (scheduleGratification != null && scheduleGratification.getScheduleInSecs() > 0) {
return scheduleGratification.getScheduleInSecs() * 1000;
}
return 0;
}
public void setInfo(Info info) {
this.info = info;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public int getRetryCount() {
return retryCount;
}
public void setRetryCount(int retryCount) {
this.retryCount = retryCount;
}
public BeneficiaryType getBeneficiaryType() {
return beneficiaryType;
}
public void setBeneficiaryType(BeneficiaryType beneficiaryType) {
this.beneficiaryType = beneficiaryType;
}
public String getBeneficiaryId() {
return beneficiaryId;
}
public void setBeneficiaryId(String beneficiaryId) {
this.beneficiaryId = beneficiaryId;
}
public GratificationData getPrimaryGratification() {
return primaryGratification;
}
public void setPrimaryGratification(GratificationData primaryGratification) {
this.primaryGratification = primaryGratification;
}
public GratificationSchedule getScheduleGratification() {
return scheduleGratification;
}
public void setScheduleGratification(GratificationSchedule scheduleGratification) {
this.scheduleGratification = scheduleGratification;
}
public String getReferenceId() {
return referenceId;
}
public void setReferenceId(String referenceId) {
this.referenceId = referenceId;
}
public String getRedemptionType() {
return redemptionType;
}
public void setRedemptionType(String redemptionType) {
this.redemptionType = redemptionType;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
@Override
public String toString() {
return "GratificationEntityTask{" +
"id=" + id +
", status=" + status +
", info=" + info +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
", version=" + version +
", retryCount=" + retryCount +
", beneficiaryType=" + beneficiaryType +
", beneficiaryId='" + beneficiaryId + '\'' +
", primaryGratification=" + primaryGratification +
", scheduleGratification=" + scheduleGratification +
", referenceId='" + referenceId + '\'' +
", redemptionType='" + redemptionType + '\'' +
", clientId=" + clientId +
'}';
}
}
Ok, I found the issue which was causing the unnecessary update. While debugging I found the update query generated with Info column as one of the one columns set.
update
com.paytm.gratification.commons.entity.GratificationEntityTask */ update
gratification_entity_task
set
info=?,
updated_at=?,
version=?
where
id=?
and version=?
There was @Convert
annotation at the entity level which was creating a new Object and causing a dirty read.
Here is the link which mentions this issue https://stackoverflow.com/a/58181716/6780814