User_1940878
User_1940878

Reputation: 361

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"

Upvotes: 0

Views: 3051

Answers (2)

Justin Mathew
Justin Mathew

Reputation: 1046

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

Upvotes: 3

Hasindu Dahanayake
Hasindu Dahanayake

Reputation: 1491

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()));

Upvotes: 1

Related Questions