【问题标题】:Disable Cascade in ManyToOne relationship JPA when saving保存时禁用多对一关系 JPA 中的级联
【发布时间】:2016-01-20 10:22:18
【问题描述】:

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

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

@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 代码。这是对 DB 中的旧实体执行的修复,在保存时没有更新此字段。

已编辑 2

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

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

【问题讨论】:

  • 你没有在这里定义任何级联。显示@PostLoad的代码..
  • 您提供的代码中没有任何内容可以将Employee 的一个更新级联到BuildingBuilding 的另一个Employees。显示你“保存”了Employee
  • 是的,我同意你的看法。我添加了如何保存此实体。
  • 会不会是你在一个事务中也修改了另一个Employees?请记住,事务中检索到的对象的任何更改都将被持久化,而无需调用merge-方法。另外,当你说员工被“修改”时,这是什么意思?
  • 当我说员工实体被修改时,我的意思是从我的员工实体中检索建筑物时 PostLoad 方法所做的更改。我不认为我正在更改 Transaction 中的任何 Employee,只是 PostLoad,但我会仔细研究一下。

标签: java jpa eclipselink cascade many-to-one


【解决方案1】:

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

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

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

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

如果您选择使用延迟提取,则仍可以根据需要通过提取连接、查询提示和/或提取/加载组逐个查询覆盖此功能。

【讨论】:

  • 问题是同一事务中的 PostLoad。将 PostLoad 逻辑删除到服务是解决方案。谢谢!
【解决方案2】:

加载Employee-entity 时,您可能还加载Building-entity(取决于您的JPA-provider 和版本,fetchtype EAGER 还是 LAZY 是默认值),以及加载Building-您还将加载所有连接到它的Employees 实体。

这意味着加载一个Employee-entity 将/可能加载连接到同一个Building 的所有Employees,因此为所有这些运行@PostLoad-方法。当这种情况发生在事务中时,我会假设@PostLoad-方法所做的更改在事务提交时会保持不变。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-21
    • 2010-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多