【问题标题】:Why is OpenJPA calling UPDATE before DELETE?为什么 OpenJPA 在 DELETE 之前调用 UPDATE?
【发布时间】:2013-06-28 03:04:27
【问题描述】:

我正在尝试使用entityManager.remove(parent) 删除具有一对多父子关系的父实体。但是,从 SQL 日志中,我看到 OpenJPA 首先调用 UPDATE 并尝试为 NOT NULL 字段设置 null。

executing prepstmnt 773394836 UPDATE child SET parent_id = ? WHERE parent_id = ? [params=(null) null, (long) 16]
executing prepstmnt 1127292223 DELETE FROM parent WHERE id = ? [params=(long) 16]
executing prepstmnt 1297966852 DELETE FROM child WHERE lookup_id = ? AND parent_id = ? [params=(int) 1, (long) 16]

如何配置我的类以防止第一次UPDATE 调用?

我的课:

@Entity
@Table(name = "parent")
public class Parent {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private long id;

    private String name;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,
        orphanRemoval = true)
    @JoinColumn(name = "parent_id")
    private List<Child> children;

    // getters, etc.
}

@Entity
@Table(name = "child")
@IdClass(ChildPrimaryKey.class)
public class Child {

    @Id
    @Column(name = "parent_id", nullable = false, updatable = false)
    private long parentId;

    @Id
    @Column(name = "lookup_id")
    private int lookupId;

    @ManyToOne
    @JoinColumn(name = "parent_id", referencedColumnName = "id",
        nullable = false, insertable = false, updatable = false)
    private Parent parent;

    // getters, etc.
}

我的数据库表(在 MySQL 中):

CREATE  TABLE IF NOT EXISTS `parent` (
  `id` BIGINT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(50) NOT NULL ,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;

CREATE  TABLE IF NOT EXISTS `child` (
  `lookup_id` INT NOT NULL ,
  `parent_id` BIGINT NOT NULL ,
  PRIMARY KEY (`parent_id`, `lookup_id`) ,
  INDEX `fk_library_code_idx` (`parent_id` ASC) ,
  CONSTRAINT `fk_library_code`
    FOREIGN KEY (`parent_id` )
    REFERENCES `parent` (`id` )
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;

【问题讨论】:

    标签: java jpa one-to-many openjpa


    【解决方案1】:

    我不确定确切是什么导致 JPA 决定执行这些查询,但是,我确实注意到您的映射存在一些问题。

    首先,您没有在 @OneToMany 注释上指定 mappedBy 元素(即,您没有告诉 OpenJPA Child 是关系的所有者)。这将是我对您为什么会看到这种行为的第一个猜测。来自API specification

    如果关系是双向的,那么 mappedBy 元素必须是 用于指定实体的关系字段或属性 是关系的所有者。

    要解决此问题,请将Parent@OneToMany 更改为包含mappedBy 元素并删除@JoinColumn

      @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, 
                 fetch = FetchType.EAGER, orphanRemoval = true)
      private List<Child> children;
    

    此外,我注意到您正在尝试使用 Parent 的 ID 作为 Child 的 ID 的一部分,并且 Child 上的两个字段映射到同一个 PK 列(parent 和 @987654337 @)。处理此问题的首选方法是使用@MapsId:

      @Id
      private long parentId;
    
      // ...
    
      @MapsId("parentId")
      @ManyToOne
      @JoinColumn(name = "parent_id", referencedColumnName = "id", nullable = false)
      private Parent parent;
    

    【讨论】:

    • 使用 mappedBy 而不是 @JoinColumn 会导致插入异常,因为 OpenJPA 尝试将 0 作为 parent_id:Caused by: org.apache.openjpa.lib.jdbc.ReportingSQLException: Cannot add or update a child row: a foreign key constraint fails (``test``.``child``, CONSTRAINT ``fk_library_code`` FOREIGN KEY (``parent_id``) REFERENCES ``parent`` (``id``) ON DELETE CASCADE ON UPDATE CASCADE) {prepstmnt 972578961 INSERT INTO child (lookup_id, parent_id) VALUES (?, ?) [params=(int) 1, (long) 0]} [code=1452, state=23000]。我认为 mappedBy 用于连接表。
    • 经过更多调查,在这种情况下使用mappedBy 是正确的方法。但是,子实体和父实体必须分开管理。例如,parent_id 必须在子级上设置为正确的值,必须手动删除子实体等。在这种情况下不需要@MapsId
    • @VictorLyuboslavsky 如果您使用@MapsIdparentId 将自动为您从Parent 复制。您的用例正是documentation 中描述的内容:“指定一个多对一或单对一关系属性,该属性为……父实体的简单主键提供映射”。此外,您不必单独删除子项...您可以显示更新后的实体吗?
    • 如果我使用@MapsId,我会得到一个无意义的异常:com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 't1.lookupId' in 'field list'。我不认为@MapsId 打算与@IdClass 一起使用
    • @VictorLyuboslavsky 你可能是对的......我没有注意到你指定了@IdClass。您是否尝试过使用EmbeddedId?另外,我认为我的回答没有解决您的问题...您最终是如何解决您的问题的?
    【解决方案2】:

    这是一个假设。

    我认为正在发生的是 UPDATE 语句是为了避免在执行 DELETE 时违反约束。或者至少,这就是 OpenJPA 认为它需要进行更新的原因。

    有两种可能:

    • OpenJPA 是对的,UPDATE 是必要的
    • OpenJPA 错误,UPDATE 是多余的

    我建议您使用相关的“父”和“子”行设置数据库,然后尝试手动删除一个或另一行 进行更新。这将确定更新是否真的必要。

    根据结果,或者忽略问题,尝试更改架构和/或注释,或者查看 OpenJPA 问题跟踪器中是否有错误报告/解决方法。 (您也可以尝试一些实验来查看 Hibernate 的行为方式是否相同和/或这是否特定于 MySQL。)

    【讨论】:

    • 我怀疑更新失败(它试图将 PK/NOT NULL 列设置为 NULL)
    • 我在数据库中尝试了手动DELETE。只需要DELETE FROM parent WHERE id = ? 即可进行删除。不需要更新。孩子的第二次 DELETE 也是没有必要的。
    猜你喜欢
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 2018-03-27
    • 1970-01-01
    • 2013-08-29
    • 2012-08-01
    • 1970-01-01
    • 2015-07-31
    相关资源
    最近更新 更多