如何使用Spring Data JPA仅更新模型中的传入字段?

6

我正在使用Spring data jpa进行持久化。 假设我需要更新一个模型。 这个模型有'n'个字段和一个主键。

    {
    "some_model":{
        "id":"5527716",
        "field_one": "44248156",
        "field_two": "44248156",
        "field_three": "44248156",
        "field_four": "44248156",
        "field_five": "44248156",
        "field_six": "44248156",
        "field_seven": "44248156",
        "field_eight": "44248156",
        "field_nine":"65037768"     
    }
}

考虑上述json表示我的模型,我想要更新仅存在于该json中的字段(主键为Id,将始终存在)。有没有办法可以实现而不必明确检查每个字段并设置它们?

假设我收到了如下请求:

{
    "some_model":{
        "id":"5527716",
        "field_one": "44248156",
        "field_two": "44248156",
        "field_three": "44248156",

        "field_eight": "44248156",
        "field_nine":"65037768"     
    }
}

我希望通过仅使用repository.save或类似的方法来实现更新(upsert类型的操作)。目前,使用repository的save方法在内部使用primaryKey值更新模型,但是对于缺失字段,它会将其设置为null。我希望这些字段保持不变。
主要问题是:有数百个模型,每个模型大约有10-15个字段,显式检查每个字段并更新所有模型将是一场噩梦。
我还检查了@DynamicUpdate注释,但它似乎适用于显式设置,其中生成的sql进行了优化。
我将感谢任何关于此的想法。

1
你想忽略空值属性对吧? - surendrapanday
@surendrapanday 是的,我也检查了那个答案。普遍共识似乎是我们不能不采取明确执行或编写HQL的方式来解决这个问题,而我不想这样做。 - Sudip Bhandari
@VeselinDavidov 我在 org.springframework.data.repository.Repository.JpaRepository 中没有看到任何名为 merge 的方法。 - Sudip Bhandari
1
简单的回答:这是不可能的。因为反序列化后,如果字段缺失,则值为null。Java是一种静态类型语言,与您的要求不太匹配。 - Simon Martinelli
显示剩余7条评论
4个回答

4
通常情况下,合并操作对你来说是可行的,因为你有一个分离的副本——即从 JSON 中获取的副本和一个持久化的副本(来自仓库)。合并操作会精确地更新持久化属性,使用分离副本中的值,如果没有持久化属性,则创建它(我不确定这部分是否是可取的)。但是,如果自动化,您将失去自己编写的灵活性。
使用 JPA,合并方法是 EntityManager 的一部分。因此,您需要注入它并像 em.merge(detachedEntity) 这样调用它。
除此之外,还有一些库可以完成此操作,但我没有使用过任何库。最好的解决方案需要编写一个方法来手动更新字段 ;)

0

我发现我的问题的确切要求无法满足。Dozer Bean Mapper似乎是一个选项,但它更多地涉及到不同类的bean对象映射,我们可以将字段对应到要映射的字段,这不是我的情况。

解决方案是改变方法,在执行更新时发送所有所需信息。

正如Simon在评论中指出的:

简单的答案:这是不可能的。因为反序列化后,如果字段缺失,则值为null。Java是一种静态类型语言,与您的要求不太匹配。


0
这是Spring Boot JPA的一大令人失望之处,而在Node.js中,我只需要执行以下操作:
User.query().findById(id).patch({name: 'Peter'})
在SpringBoot中,我看到了需要安装库、100行代码的解决方案,非常失望!

0

看看 dozer - 一个 bean 映射库。您可以使用自定义字段映射器配置 dozer,以便在字段为空时不进行复制。

您可以从数据库中检索对象,将请求对象的字段值复制到从数据库中检索的对象中(使用不映射空值的自定义字段映射器),然后保存该对象。

    User user1 = new User(); // Request body
    user1.setFirstName("newFn");

    User user2 = new User(); // Entity retrieved from the database
    user2.setFirstName("fn");
    user2.setLastName("ln");

    DozerBeanMapper dozerBeanMapper = new DozerBeanMapper();
    dozerBeanMapper.setCustomFieldMapper(
            (source, destination, sourceFieldValue, classMap, fieldMapping) -> sourceFieldValue == null);
    dozerBeanMapper.map(user1, user2);
    System.out.println(user2); // User(firstName=newFn, lastName=ln)

参考:https://dev59.com/PnfZa4cB1Zd3GeqPVcCq#36716023

如果您不想将Dozer作为依赖项添加,您也可以使用BeanUtils仅复制那些不为空的属性。请查看this问题以获取详细信息。


1
我不建议使用Dozer,因为与其他映射库(如MapStruct或Selma)相比,其性能相当差。 - рüффп

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接