Reputation: 95
i wanna store spring JPA repository object before it should be saved.
in function update, i wanna store old record data to dbDomain
but suddenly after repository.save(updated)
the dbDomain changed to new updated data.
any one know how to solve this ? thanks
package com.test.admintool.userauth.services;
import java.text.SimpleDateFormat;
import java.util.*;
import com.test.admintool.engine.common.AdminToolFunction;
import com.test.admintool.engine.common.AuditTrailMessage;
import com.test.admintool.engine.common.CopyUtil;
import com.test.admintool.engine.service.AdminService;
import com.test.admintool.engine.service.AuditTrailEventService;
import com.test.admintool.engine.utils.SecurityUtil;
import com.test.admintool.userauth.entity.User;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.GenericTypeResolver;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;
import com.test.admintool.engine.AdminEntity;
import com.test.admintool.engine.exception.EntityNotFoundException;
import com.test.admintool.engine.repository.AdminRepository;
import com.querydsl.core.types.Predicate;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Table;
@Slf4j
public abstract class AdminServiceImpl<T extends AdminEntity<T>> implements AdminService<T> {
private static final String AUDIT_ACTION_CREATE = "CREATE";
private static final String AUDIT_ACTION_UPDATE = "UPDATE";
private static final String AUDIT_ACTION_DELETE = "DELETE";
private final AdminRepository<T> repository;
private final AuditTrailEventService auditTrailEventService;
protected AdminServiceImpl(AdminRepository<T> repository, AuditTrailEventService auditTrailEventService) {
this.repository = repository;
this.auditTrailEventService = auditTrailEventService;
}
public T get(Long id) throws EntityNotFoundException {
return repository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(String.format("Product %s not found", id)));
}
@Transactional
public T update(T updated) throws EntityNotFoundException {
T dbDomain = get(updated.getId());
T updatedData = repository.save(updated);
produceAuditMessageUpdate(dbDomain, updatedData, AUDIT_ACTION_UPDATE);
return updatedData;
}
}
Upvotes: 1
Views: 1414
Reputation: 1365
I assume that you are passing a detached entity instance to the update
method, and a new transaction is started when entering the method (by the spring transactional aop proxy).
(Otherwise, the dbDomain
entry would be resolved from the hibernate 1st level cache to the passed entity instance updated
, so that there would not be any property differences.)
When passing the detached instance updated
to the save
method, the entity manager will merge it into the persistence context. Thereby, it will also check for another already attached instance of the same entity (same ID), and keep the other instance of the entity in sync by copying all values from the newly passed instance to the other attached instance:
https://github.com/hibernate/hibernate-orm/blob/734b80c531bdb2a9bb33971cd57f51fce662e758/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java#L346
(Which makes sense, since you would otherwise end up with different values on the attached entities, and you could not decide which would be persisted to the DB at the end of the transaction.)
To prevent your dbDomain
instance being updated, you could manually detach it from the persistence context before calling save(updated)
:
T dbDomain = get(updated.getId());
entityManager.detach(dbDomain); // <--
T updatedData = repository.save(updated);
Alternatively, you may also deep-copy the relevant dbDomain
property values.
Upvotes: 1