简体   繁体   中英

How to prevent automatic update in Spring Data JPA?

In my Spring boot batch application, I am calling a JPA repository class from Tasklet. The JPA call retrieves a particular value (Entity object) from DB. The problem is, If I update some value in the entity object, once the control goes out of Tasklet, it automatically updates to DB even though I am not calling any save operation. How to prevent this? Default JPA implementation is Hibernate.

Tasklet class

Employee employee = employeeRepository.fetchEmployee(employeeName);
List<Address> addressList = employee.getAddress();
addressList.forEach(e -> e.setStatus(Status.INVALID.toString()));

Repository

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    @Query("select em from Employee em where em.employeeName = :employeeName")
    public Employee fetchEmployee(@Param("employeeName") Long employeeName);
}

Entity class

@Entity
@Table(name = "Employee")
public class Employee implements java.io.Serializable {

    private static final long serialVersionUID = -3769636546619492649L;
    private Long id;
    private List<Address> address;
    private String employeeName;
    
    // Getters and setters
    // @OneToMany mapping to Address
}

Even though I am not calling a.save() operation, it automatically updates Address table Status to "INVALID"

This happen because the entity is not in detached state. In EJB we can do this in the following way.

EJB solution

@Query(value = "select * from Employee WHERE EmployeeName = ?1", nativeQuery = true)
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List<Employee> fetchEmployee(String employeeName);

This will make the transaction closed. Changes you make to entity will not get saved in DB

Spring JPA

After a bit of research i found JPA doesn't provide the detach functionality out of the box.

Refer: https://github.com/spring-projects/spring-data-jpa/issues/641

To make it work we can have a custom JPA repository which overrides detach method. An example is given in this link.

https://www.javaer101.com/en/article/1428895.html

Use Deep cloning to solve your issue.

First override the clone method inside your Address class like below. Note: Please customize the implementation of clone() method by adding your class attributes.Since you didn't mention the structure of the class Address, I have implemented the solution with my own defined class attributes.

Address class

public class Address {

    private String country;
    private String city;
    private String district;
    private String addressValue;

    public Address() {
        super();

    }

    public Address(String country, String city, String district, String addressValue) {
        super();
        this.country = country;
        this.city = city;
        this.district = district;
        this.addressValue = addressValue;
    }
 //Getters and Setters 

   @Override
    protected Object clone()  {
        try {
            return (Address) super.clone();
        } catch (CloneNotSupportedException e) {
            return new Address(this.getCountry(), this.getCity(), this.getDistrict(),this.getAddressValue());
        }
    }

}

Then re construct your class Tasket like below.

Tasket Class

Employee employee = employeeRepository.fetchEmployee(employeeName);
List<Address> addressList = employee.getAddress();
List<Address> clonedAddressList = new ArrayList<>();
addressList.forEach(address -> clonedAddressList.add((Address)address.clone()) );
clonedAddressList.forEach(address -> address.setStatus(Status.INVALID.toString()));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM