【问题标题】:JPA Hibernate remove child object of entity, connected with dual PK triggers SQL update instead of delete?JPA Hibernate删除实体的子对象,连接双PK触发SQL更新而不是删除?
【发布时间】:2020-06-06 00:15:29
【问题描述】:

我的表结构如下:

  • 每个聊天机器人可以有多个版本。
  • 每个版本都可以部署到环境中。
  • 版本具有双主键,其中一个是从 chat_bot 表中引用的。
  • 环境表只有主键。其中两个是从版本表中引用的。

我有以下类结构:

@Entity
@AllArgsConstructor
@Table(name = "chat_bot")
@NoArgsConstructor
public class Bot {

    @Id
    private String id;

    @LazyCollection(LazyCollectionOption.FALSE)
    @OneToMany(mappedBy = "botId", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<BotVersion> versions = new LinkedHashSet<>();
}

@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class BotVersionPK implements Serializable {
    private String botId;
    private int version;
}


@Entity
@AllArgsConstructor
@Table(name = "chat_bot_version")
@NoArgsConstructor
@IdClass(BotVersionPK.class)
public class BotVersion {

    @Id
    private String botId;
    @Id
    private int version;

    @Type(type = "text")
    private String json;

    @OneToMany(fetch = FetchType.LAZY, orphanRemoval = true)
    @JoinColumns({
            @JoinColumn(name = "botId", referencedColumnName = "botId"),
            @JoinColumn(name = "version", referencedColumnName = "version")
    })
    private Set<BotEnvironments> environments = new LinkedHashSet<>();
}

@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class BotEnvironmentsPK implements Serializable {
    private String botId;
    private int version;
    private String environmentCode;
}

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "chat_bot_environments")
@IdClass(BotEnvironmentsPK.class)
public class BotEnvironments {
    @Id
    private String botId;
    @Id
    private int version;
    @Id
    private String environmentCode;
}

我使用 JpaRepository 来加载和保存信息。

问题: 如果我从“环境”集中删除一个项目并将其持久化,Hibernate 不会触发 DELETE 操作,而是触发 UPDATE 操作:

update chat_bot_environments set bot_id=null, version=null where bot_id=? and version=?

我得到以下错误,这是有效的:

Column 'bot_id' cannot be null

为什么 Hibernate JPA 触发器更新而不是 DELETE?如何删除通过非空 PK 连接的子元素?

【问题讨论】:

  • 你没有错过 OneToMany 环境中的级联属性吗?
  • 结果没有区别。
  • 你是如何创建架构的?如果用sql,能否提供脚本?

标签: java sql spring set spring-data


【解决方案1】:

您的映射中有一些错误。

  1. 连接列总是在子端

    @OneToMany(fetch = FetchType.LAZY, orphanRemoval = true)
    @JoinColumns({
            @JoinColumn(name = "botId", referencedColumnName = "botId"),
            @JoinColumn(name = "version", referencedColumnName = "version")
    })
    private Set<BotEnvironments> environments = new LinkedHashSet<>();
    

此映射应移至子 (BotEnvironments)。

  1. mappedBy 应该指向与实体相同类型的字段。您已经定义了mappedBy = "botId",但botId 的类型为String(它是一个外键)。这不会映射实体之间的关系。

  2. 不要害怕双向关系。它们不会导致性能开销 - 实际上它们通常可以优化持久性性能。

我还纠正了其他一些小问题。为简洁起见,我省略了 Lombok 注释和获取类型。这是正确的映射(生成的结果数据库模式是相同的):

机器人

@Entity
@Table(name = "chat_bot")
public class Bot {

    @Id
    private String id;

    @OneToMany(mappedBy = "bot", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<BotVersion> versions = new LinkedHashSet<>();

}

BotVersion

@Entity
@Table(name = "chat_bot_version")
@IdClass(BotVersionPK.class)
public class BotVersion {

    @Id
    private String botId;
    @Id
    private int version;

    @ManyToOne
    @JoinColumn(name = "botId", insertable=false, updatable=false)
    private Bot bot;

    @Type(type = "text")
    private String json;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "botVersion")
    private Set<BotEnvironments> environments = new LinkedHashSet<>();

}

机器人环境

@Entity
@Table(name = "chat_bot_environments")
@IdClass(BotEnvironmentsPK.class)
public class BotEnvironments {
    @Id
    private String botId;
    @Id
    private int version;
    @Id
    private String environmentCode;

    @ManyToOne
    @JoinColumns({
            @JoinColumn(name = "botId", referencedColumnName = "botId", insertable=false, updatable=false),
            @JoinColumn(name = "version", referencedColumnName = "version", insertable=false, updatable=false)
    })
    private BotVersion botVersion;

}

Id 类没有改​​变。

这是一个简单的测试,检查它是否有效:

@Test
public void test() {
    // given
    BotEnvironments botEnvironments = new BotEnvironments("bid", 1, "env");
    BotEnvironments botEnvironments1 = new BotEnvironments("bid", 1, "env1");
    BotVersion botVersion = new BotVersion("bid", 1, "json", Set.of(botEnvironments, botEnvironments1));
    Bot bot = new Bot("bid", Set.of(botVersion));
    botRepository.save(bot);

    // when
    Bot savedBot = botRepository.findAll().get(0);
    savedBot.getVersions().iterator().next().getEnvironments().remove(savedBot.getVersions().iterator().next().getEnvironments().iterator().next());
    botRepository.save(savedBot);

    // then
    assertEquals(1, botEnvironmentsRepository.findAll().size());
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-02
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多