【问题标题】:How to update only the incoming fields in a model using spring data jpa?如何使用spring data jpa仅更新模型中的传入字段?
【发布时间】:2018-11-09 07:21:48
【问题描述】:

我正在使用 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 或类似的东西来实现更新(升级插入之类的东西)。目前使用存储库的 save 方法在内部使用 primaryKey 值更新模型,但将缺失字段的值设置为 null。我希望这些字段保持不变。

主要问题是:有数百个模型,每个模型大约有 10-15 个字段,显式检查每个字段并更新所有模型将是一场噩梦。

我还检查了@DynamicUpdate 注释,但它似乎适用于优化生成的 sql 的显式设置。

我将不胜感激有关此的任何想法。

【问题讨论】:

  • 您想忽略具有空值的属性吗?
  • @surendrapanday 是的
  • @surendrapanday 是的,我也检查了那个答案。普遍的共识似乎是,我们不得不求助于明确地做或写我不想做的 hql。
  • 简单的回答:这是不可能的。因为在反序列化后,如果缺少字段,则值为 null。 Java 是一种静态类型语言,不能很好地满足您的要求。

标签: java spring hibernate spring-boot spring-data-jpa


【解决方案1】:

通常合并对您有用,因为您获得了分离的副本 - 您从 JSON 获得的副本和持久的副本(来自存储库)。合并正是这样做的 - 使用分离属性的值更新持久属性,如果没有,它会创建它(我不确定该部分是否是可取的)。仍然让它自动化,你失去了自己编写它的灵活性。

对于 JPA,merge 方法是 EntityManager 的一部分。所以你需要注入它并像 em.merge(detachedEntity); 一样调用它

除此之外,还有一些库可以做到这一点,但我没有使用任何库。需要一些编码的最佳解决方案是编写一个手动更新字段的方法;)

【讨论】:

    【解决方案2】:

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

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

        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://stackoverflow.com/a/36716023/1377058

    如果您不想将 dozer 添加为依赖项,您还可以使用 BeanUtils 仅复制那些不为 null 的属性。请查看this question 了解如何操作的详细信息。

    【讨论】:

    • 我不推荐 Dozer,因为与看起来更好的 MapStruct 或 Selma 等其他映射库相比,Dozer 的性能相当差。
    【解决方案3】:

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

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

    正如Simon在评论中指出的那样:

    简单的答案:这是不可能的。因为反序列化后如果 字段丢失,值为空。 Java是一种静态类型语言 并且不太符合您的要求。

    【讨论】:

      【解决方案4】:

      这让 Spring Boot JPA 非常失望,并且认为在 NodeJs 中我只执行以下操作:

      User.query().findById(id).patch({name: 'Peter'})

      在SpringBoot中见过需要安装库的解决方案,100行代码,很失望!

      【讨论】:

        猜你喜欢
        • 2015-05-25
        • 2015-10-24
        • 2019-09-21
        • 1970-01-01
        • 2017-10-07
        • 2020-05-11
        • 1970-01-01
        • 1970-01-01
        • 2012-08-06
        相关资源
        最近更新 更多