删除然后创建记录会导致 Spring Data JPA 出现重复键冲突

2024-05-01

因此,我有这样的场景,我需要获取标头记录,删除它的详细信息,然后以不同的方式重新创建详细信息。更新细节太麻烦了。

我基本上有:

@Transactional
public void create(Integer id, List<Integer> customerIDs) {

    Header header = headerService.findOne(id);
    // header is found, has multiple details

    // Remove the details
    for(Detail detail : header.getDetails()) {
        header.getDetails().remove(detail);
    }

    // Iterate through list of ID's and create Detail with other objects
    for(Integer id : customerIDs) {
        Customer customer = customerService.findOne(id);

        Detail detail = new Detail();
        detail.setCustomer(customer);

        header.getDetails().add(detail);
    }

    headerService.save(header);
}

现在,数据库有如下约束:

Header
=================================
ID, other columns...

Detail
=================================
ID, HEADER_ID, CUSTOMER_ID

Customer
=================================
ID, other columns...

Constraint:  Details must be unique by HEADER_ID and CUSTOMER_ID so:

Detail  (VALID)
=================================
1, 123, 10
2, 123, 12

Detail  (IN-VALID)
=================================
1, 123, 10
1, 123, 10

好的,当我运行这个并传入 2、3、20 等客户时,它会创建所有Detail只要以前没有记录就可以了。

如果我再次运行它,传递不同的客户列表,我希望ALL首先要删除的详细信息,然后是列表NEW待创建的详细信息。

但发生的情况是,在创建之前似乎没有尊重删除。因为错误是重复键约束。重复的键就是上面的“IN-VALID”场景。

如果我用一堆详细信息手动填充数据库并注释掉CREATE details部分(仅运行删除)然后记录就被删除了。所以删除有效。创建作品。只是两者不能一起工作。

我可以提供更多需要的代码。我在用着Spring Data JPA.

Thanks

UPDATE

我的实体基本上注释如下:

@Entity
@Table
public class Header {
...
    @OneToMany(mappedBy = "header", orphanRemoval = true, cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
    private Set<Detail> Details = new HashSet<>();

...
}

@Entity
@Table
public class Detail {
...
    @ManyToOne(optional = false)
    @JoinColumn(name = "HEADER_ID", referencedColumnName = "ID", nullable = false)
    private Header header;
...
}

UPDATE 2

@克劳斯·格伦贝克

事实上,我最初并没有提到这一点,但我第一次就这样做了。另外,我正在使用 Cascading.ALL,我假设它包括 PERSIST。

只是为了测试,我已将代码更新为以下内容:

@Transactional
public void create(Integer id, List<Integer> customerIDs) {

    Header header = headerService.findOne(id);

    // Remove the details
    detailRepository.delete(header.getDetails());       // Does not work

    // I've also tried this:
    for(Detail detail : header.getDetails()) {
        detailRepository.delete(detail);
    }


    // Iterate through list of ID's and create Detail with other objects
    for(Integer id : customerIDs) {
        Customer customer = customerService.findOne(id);

        Detail detail = new Detail();
        detail.setCustomer(customer);
        detail.setHeader(header);

        detailRepository.save(detail)
    }
}

再次...我想重申...如果我之后没有立即创建,则删除将起作用。如果我没有在创建之前立即进行删除,则创建将起作用。但是,如果它们在一起,则由于数据库中的重复键约束错误,两者都不起作用。

我尝试过使用和不使用级联删除的相同场景。


请耐心等待,因为这是一个相当长的解释,但是当我查看您的代码时,您似乎缺少一些有关 JPA 如何工作的关键概念。

首先,将实体添加到集合或从集合中删除实体并不意味着在数据库中会发生相同的操作,除非使用级联或 orphanRemoval 来传播持久性操作。

对于要添加到数据库的实体,您必须调用EntityManager.persist()直接或通过级联持续。这基本上就是里面发生的事情JPARepository.save()

如果你想删除一个实体,你必须调用EntityManager.remove()直接或通过级联操作,或通过JpaRepository.delete().

如果您有一个托管实体(加载到持久性上下文中的实体),并且您修改了事务内的基本字段(非实体、非集合),那么当事务提交时,此更改将写入数据库,甚至如果你没有打电话persist/save。持久化上下文保留每个已加载实体的内部副本,当事务提交时,它会循环遍历内部副本并与当前状态进行比较,任何基本的归档更改都会触发更新查询。

如果您已将新实体 (A) 添加到另一个实体 (B) 上的集合,但未对 A 调用 persist,则 A 将不会保存到数据库中。如果你在B上调用persist,将会发生以下两种情况之一,如果persist操作是级联的,A也会被保存到数据库中。如果持久性未级联,您将收到错误,因为托管实体引用非托管实体,这会在 EclipseLink 上给出此错误:“在同步期间,通过未标记为级联 PERSIST 的关系找到了新对象”。级联持久化是有意义的,因为您经常同时创建父实体及其子实体。

当您想要从另一个实体 B 上的集合中删除实体 A 时,您不能依赖级联,因为您没有删除 B。相反,您必须直接在 A 上调用删除,从 B 上的集合中删除它并不会没有任何效果,因为没有在 EntityManager 上调用持久化操作。您还可以使用 orphanRemoval 来触发删除,但我建议您在使用此功能时要小心,特别是因为您似乎缺少一些有关持久性操作如何工作的基本知识。

通常,考虑持久性操作以及它必须应用于哪个实体会有所帮助。如果是我编写的话,代码会是这样的。

@Transactional
public void create(Integer id, List<Integer> customerIDs) {

    Header header = headerService.findOne(id);
    // header is found, has multiple details

    // Remove the details
    for(Detail detail : header.getDetails()) {
        em.remove(detail);
    }

    // em.flush(); // In some case you need to flush, see comments below

    // Iterate through list of ID's and create Detail with other objects
    for(Integer id : customerIDs) {
        Customer customer = customerService.findOne(id);

        Detail detail = new Detail();
        detail.setCustomer(customer);
        detail.setHeader(header);  // did this happen inside you service?
        em.persist(detail);
    }
}

首先,没有理由保留标头,它是一个托管实体,您修改的任何基本字段都将在事务提交时更改。 Header 恰好是 Details 实体的外键,这意味着重要的是detail.setHeader(header); and em.persist(details),因为你必须设置所有的外交关系,并坚持任何新的Details。 同样,从标头中删除现有详细信息与标头无关,定义关系(外键)位于详细信息中,因此从持久性上下文中删除详细信息就是将其从数据库中删除。您还可以使用 orphanRemoval,但这需要为每个事务添加额外的逻辑,并且在我看来,如果每个持久性操作都是显式的,则代码更容易阅读,这样您就不需要返回实体来阅读注释。

最后:代码中的持久性操作顺序不会转换为针对数据库执行的查询的顺序。 Hibernate 和 EclipseLink 都会先插入新实体,然后删除现有实体。根据我的经验,这是“主键已存在”的最常见原因。如果删除具有特定主键的实体,然后添加具有相同主键的新实体,则将首先发生插入,并导致键冲突。可以通过告诉 JPA 将当前持久性状态刷新到数据库来修复此问题。em.flush()会将删除查询推送到数据库,因此您可以插入与已删除的行具有相同主键的另一行。

信息量很大,如果有什么不明白或者需要我澄清的地方请告诉我。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

删除然后创建记录会导致 Spring Data JPA 出现重复键冲突 的相关文章

  • 如何打印整个字符串池?

    我想打印包含文字的整个字符串池String使用添加的对象intern 就在垃圾收集之前 JDK有没有隐式的方法来进行这样的操作 我们如何检查字符串池 EDIT The comment suggests that there may be a
  • Java 泛型/类型调度问题

    考虑以下程序 import java util List import java util ArrayList public class TypeTest public static class TypeTestA extends Type
  • 如何解决 onEditCommit 事件上的类型不匹配错误?

    我在 Fxml 中使用 onEditCommit 事件在用户编辑数据后检索数据 FXML 代码
  • Java 重写 hashCode() 得到 StackOverflowError

    所以我不太熟悉重写 hashCode 并且我似乎在 hashCode 方法中以某种方式进行了一些无限递归 这是我的场景 我有一个 DuplicateCache 类 它是一个缓存对象 用于检查系统中的重复对象 我有一个静态内部类 Duplic
  • Java 变量的作用域

    我不明白为什么这段代码的输出是10 package uno public class A int x 10 A int x 12 new B public static void main String args int x 11 new
  • Spring Stomp over Websocket:流式传输大文件

    我的SockJs客户端在网页中 发送帧大小为16K的消息 消息大小限制决定了我可以传输的文件的最大大小 以下是我在文档中找到的内容 Configure the maximum size for an incoming sub protoco
  • Java替换特定字符

    这是我在这个网站上的第一个问题 所以我会尽量不要成为一个十足的菜鸟 我目前正在用java 创建刽子手游戏 所以我问你的问题是我们是否被赋予了 幽灵 这个词 并将 Ghost 替换为 hiddenWord ghost length for i
  • 想要开发像 Facebook 这样的网站 - 处理数百万个请求 - 高性能 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我想用 Java 开发一个像 Fac
  • Install4j:如何在安装结束时执行命令行 java -jar filename.jar

    在 Intall4j 中 在安装结束时 我只想通过执行如下命令行来初始化某些内容 java jar filename jar 我怎样才能归档这个任务install4j Thanks 将 运行可执行文件或批处理文件 操作添加到 安装屏幕 并设
  • 为什么 ConcurrentHashMap::putIfAbsent 比 ConcurrentHashMap::computeIfAbsent 更快?

    使用 ConcurrentHashMap 我发现computeIfAbsent 比putIfAbsent 慢两倍 这是简单的测试 import java util ArrayList import java util List import
  • 覆盖 MATLAB 默认静态 javaclasspath 的最佳方法

    MATLAB 配置为在搜索用户可修改的动态路径之前搜索其静态 java 类路径 不幸的是 静态路径包含相当多非常旧的公共库 因此如果您尝试使用新版本 您可能最终会加载错误的实现并出现错误 例如 静态路径包含 google collectio
  • 从 Java 日历迁移到 Joda 日期时间

    以前 当我第一次设计股票应用相关软件时 我决定使用java util Date表示股票的日期 时间信息 后来我体会到了大部分方法java util Date已弃用 因此 很快 我重构了所有代码以利用java util Calendar 然而
  • Apache Commons CLI:替代已弃用的 OptionBuilder?

    IntelliJ 显示此示例代码中不推荐使用 OptionBuilderhttp commons apache org proper commons cli usage html http commons apache org proper
  • OpenJDK 版本控制

    上下文 我想确保我们系统上安装的 Java 不受 CVE 2022 21449 的影响 java version 给出 openjdk version 11 0 7 2020 04 14 LTS OpenJDK Runtime Enviro
  • 如何在keycloak中动态编辑standalone.xml文件

    我正在尝试通过 docker 编辑standalone xml 并尝试添加 但 keycloak 正在使用它standalone xml 但我可以看到standalone xml 文件中的更改 我需要在standalone xml 文件中添
  • 我想要一个 Java 阿拉伯语词干分析器

    我正在寻找阿拉伯语的 Java 词干分析器 我找到了一个名为 AraMorph 的库 但它的输出是无法控制的 并且它会形成不需要的单词 还有其他阿拉伯语词干分析器吗 这是新的阿拉伯语词干分析器 Assem 的阿拉伯语轻词干分析器 http
  • struts 教程或示例

    我正在尝试在 Struts 中制作一个登录页面 这个想法是验证用户是否存在等 然后如果有错误 则返回到登录页面 错误显示为红色 典型的登录或任何表单页面验证 我想知道是否有人知道 Struts 中的错误管理教程 我正在专门寻找有关的教程 或
  • 从一个文本文件中获取数据并将其移动到新的文本文件

    我有一个文件 里面有数据 在我的主要方法中 我读入文件并关闭文件 我调用另一种方法 在原始文件的同一文件夹内创建一个新文件 所以现在我有两个文件 原始文件和通过我调用的方法生成的文件 我需要另一种方法 从原始文件中获取数据并将其写入创建的新
  • java中如何找到class文件的包

    我正在编写一个使用 class 文件的 java 程序 我希望能够读取文件系统上的 class 文件 使用 InputStream 并确定它所在的包 该 class 文件可能不在一个好的包目录结构中 它可能位于某个随机位置 我怎样才能做到这
  • 使用 eclipse IDE 配置 angularjs

    我想开始使用 AngularJs 和 Java Spring 进行开发 我使用 Eclipse 作为 IDE 我想配置我的 Eclipse 以使这些框架无缝工作 我知道我可能要求太多 但相信我 我已经做了很多研究 你们是我最后的选择 任何帮

随机推荐