【问题标题】:How to prevent automatic update in Spring Data JPA?如何防止 Spring Data JPA 中的自动更新?
【发布时间】:2021-07-24 00:33:51
【问题描述】:

在我的 Spring Boot 批处理应用程序中,我正在从 Tasklet 调用 JPA 存储库类。 JPA 调用从 DB 中检索特定值(实体对象)。问题是,如果我更新实体对象中的某些值,一旦控件退出 Tasklet,即使我没有调用任何保存操作,它也会自动更新到 DB。如何防止这种情况?默认的 JPA 实现是 Hibernate。

Tasklet 类

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

存储库

@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
@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
}

即使我没有调用 .save() 操作,它也会自动将地址表状态更新为“无效”

【问题讨论】:

  • 如果您不想更改,为什么要首先更新值?
  • 请分享您的小任务代码以及您用于该小任务的事务管理器的配置。 tasklet 将在 Spring Batch 驱动的事务中执行(请参阅docs.spring.io/spring-batch/docs/4.3.x/reference/html/…),在 JPA 的情况下,如果提交事务,则对附加到持久性上下文的对象的任何更改都可以刷新到数据库。跨度>

标签: java spring-boot jpa spring-batch


【解决方案1】:

发生这种情况是因为实体未处于分离状态。在 EJB 中,我们可以通过以下方式做到这一点。

EJB 解决方案

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

这将使交易关闭。您对实体所做的更改不会保存在数据库中

Spring JPA

经过一番研究,我发现 JPA 不提供开箱即用的分离功能。

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

为了让它工作,我们可以有一个覆盖分离方法的自定义 JPA 存储库。此链接中给出了一个示例。

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

【讨论】:

  • @TransactionAttribute 似乎是 EJB 注释。它如何适合这里
【解决方案2】:

使用深度克隆解决您的问题。

首先在您的 Address 类中覆盖 clone 方法,如下所示。 注意:请通过添加您的类属性来自定义clone()方法的实现。由于您没有提到类地址的结构,我已经使用自己定义的类属性实现了解决方案。

地址类

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

}

然后像下面这样重新构建你的类Tasket。

Tasket 类

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

【讨论】:

    猜你喜欢
    • 2020-06-14
    • 2016-06-19
    • 1970-01-01
    • 2012-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-13
    • 2018-03-31
    相关资源
    最近更新 更多