保存时禁用 ManyToOne 关系 JPA 中的级联

Disable Cascade in ManyToOne relationship JPA when saving

我在数据库中保存实体时遇到问题。

我有类似的东西(非常简单):

@Entity
public class Building {

    @OneToMany(mappedBy = "building", fetch = FetchType.EAGER)
    private List<Employee> employees;

}  

@Entity
public class Employee {

    @NotNull
    @ManyToOne
    @JoinFetch(INNER)
    @JoinColumn
    private Building building;

    @PostLoad
    private void onLoad(){
          if (this.plannedOrder == null) {
            //For old entities in this DB, update plannedOrder if null
            if (this.order < FIRST.getCode()) {
                this.plannedOrder = FIRST;
            } else if (this.order >= FIRST.getCode() && this.order < SECOND.getCode()) {
                this.plannedOrder = SECOND;
            } else if (this.order >= DEFAULT.getCode() && this.order < SEC_LAST.getCode()) {
                this.plannedOrder = DEFAULT;
            } else if (this.order >= SEC_LAST.getCode() && this.order < LAST.getCode()) {
                this.plannedOrder = SEC_LAST;
            } else if (this.order >= LAST.getCode()) {
                this.plannedOrder = LAST;
            } else {
                this.plannedOrder = DEFAULT;
            }
    }

}

问题出在我保存 Employee 实体时。在 Building 实体中,您将修改所有员工,因为 @PostLoad 注释,因此 JPA 将尝试更新这些实体。但是,我只想更新 Employee 实体。

有没有什么方法可以在不改变模型关系的情况下做到这一点?可能是删除 onLoad 函数的解决方案?

谢谢!

已编辑:

添加了PostLoad的PostLoad代码。这是为数据库中的旧实体执行的修复,保存时没有更新此字段。

已编辑 2

这就是我使用 EntityManager 保存实体 Employee 的方式(如果是新对象,则坚持,否则更新它)

    if (entity.getId() == null) {
        entityManager.persist(entity);
        return entity;
    }
    return entityManager.merge(entity);        

加载 Employee 实体时,您可能还会加载 Building 实体(取决于您的 JPA-provider 和版本,fetchtype EAGER 或 LAZY 是默认值),并且在加载 Building 实体时,您还将加载与其连接的所有 Employees

这意味着加载一个 Employee-实体 will/might 加载所有连接到相同 BuildingEmployee,因此 运行 @PostLoad-所有这些的方法。当这种情况发生在事务内部时,我会假设 @PostLoad 方法所做的更改在事务提交时会保留。

只是扩展了 Tobb 的回答: 发生这种情况是因为您正在使用合并调用加载 Employee 。该实体与建筑有 1:1 关系,默认为急切获取,强制建筑也被加载。

Building 也有一个 1:M 给 Employees,你用 fetch = FetchType.EAGER 标记了它,强制所有员工也被加载。

如果您不想加载所有员工,从而强制 JPA 调用他们的 postLoad 方法,您需要阻止加载这些关系中的一个或多个。选项:

  1. 将一个或多个标记为 fetch = FetchType.LAZY
    • 这对于集合来说是微不足道的,但是 1:1 映射可能需要额外的支持。例如 EclipseLink 需要编织
  2. 从实体中删除一个或多个映射。
  3. 删除更改实体的 postLoad 逻辑。这似乎不是操纵实体的最佳位置,因为即使事务中可能没有其他更改,对它们的任何访问都会强制更新。您可能想出一种不同的策略,例如一次性批量更新遗留数据,而不是长期影响您的应用程序的策略。如果必须,可以改用@PreUpdate

如果您选择使用延迟提取,这仍然可以根据需要通过提取连接、查询提示和/或 fetch/load 组在逐个查询的基础上被覆盖。