V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
aragakiyuii
V2EX  ›  Java

spring jpa 更新 null 值的问题

  •  
  •   aragakiyuii · 2020-02-25 19:17:54 +08:00 · 4989 次点击
    这是一个创建于 1493 天前的主题,其中的信息可能已经有所发展或是发生改变。

    jpa 用 save 更新时,如果直接 save(entity)的话,并不会忽略掉 entity 中 null 字段,就导致数据库原有的字段被覆盖。

    问题我明白为什么,很难判断开发者需不需要更新 null 值。为啥 jpa 一直不加注解或者配置可以让开发者选择性忽略某些 null 字段?

    stackoverflow 上有相关问题,回答都是要先查出来,手动 set 修改字段,然后再 save 一下。这样会有点复杂。entity 要加 @DynamicUpdate 注解(如果不需要更新全部字段的话),字段多的 entity 还需要用 BeanUtils 的工具,并且要手动过滤掉值为 null 的字段。没有别的好点的办法嘛?

    ps: 题外话,用了几天感觉 jpa 学习成本比 mybatis 高好多。。不过写注解比写 sql 舒服多了

    12 条回复    2020-02-26 12:44:46 +08:00
    br00k
        1
    br00k  
       2020-02-25 19:23:16 +08:00
    有这需求你就 save 之前查一下,把不为 null 的值 set 回去不就好了。
    aragakiyuii
        2
    aragakiyuii  
    OP
       2020-02-25 19:32:33 +08:00   ❤️ 1
    @br00k #1 嗯,现在是这样做的。先把 null 的字段名查出来,再用的 BeanUtils.copyProperties。不过这样感觉被喂了一坨💩一样
    aristotll
        3
    aristotll  
       2020-02-25 19:47:25 +08:00
    这个是 by design 的,和 mybatis 不一样
    hantsy
        4
    hantsy  
       2020-02-25 19:51:27 +08:00
    跟用什么东西没有关系,

    一段伪代码,

    1. 首先,
    从 Web 取出数据,

    var toUpdate=controller.getFromRequestBody

    2. 查询数据库已经存在的数据

    var saved = repo.findOne(id)

    saved.setContent(toUpdate.getContent)

    3. 保存(不是必须的,在事务内可以自己同步)
    repo.save(saved)

    参考
    https://github.com/hantsy/spring-reactive-sample/blob/master/boot-data-r2dbc/src/main/java/com/example/demo/PostController.java#L37-L47
    yannxia
        6
    yannxia  
       2020-02-25 19:53:25 +08:00
    如何更新成 null 和 null 默认不处理,这两事情总有一个要麻烦点。你选··
    hantsy
        7
    hantsy  
       2020-02-25 19:58:15 +08:00
    >stackoverflow 上有相关问题,回答都是要先查出来,手动 set 修改字段,然后再 save 一下.
    这是最基本的策略问题, 和你用 JPA,MyBatis,Spring Data Mongo,还是 JDBC 什么的没有一点关系。
    aragakiyuii
        8
    aragakiyuii  
    OP
       2020-02-25 20:15:58 +08:00
    @hantsy #7 谢谢,我之前用 mybatis 是写<if test="">判断的😂
    br00k
        10
    br00k  
       2020-02-25 23:44:45 +08:00
    如果你有这种需求,而且要优雅一点。就在 onSaveBefore 或用 AOP 根据 Bean 或者方法上定义的注解统一处理就好了。
    rockyou12
        11
    rockyou12  
       2020-02-25 23:48:40 +08:00
    @aragakiyuii 用 mapstruct 这个库,可以生成代码来用一个 bean 更新另一个 bean

    @Mapper
    public interface CarMapper {

    void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
    }
    caotian
        12
    caotian  
       2020-02-26 12:44:46 +08:00
    我是这样处理的, 默认更新时忽略 null 值, 如果需要删除值的, 提供单独的方法来更新。
    忽略 null 值更新的方法: 提供 http patch 方法, 使用自定义的对象如 payload 来接受并验证, 然后后将 payload 对象序列化为字符串, 过程中忽略掉 null 值, 再用序列化后的字符串, 对原对象进行 json patch, 再将结果 objectMapper 回原对象, 就可以 save 了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3310 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 13:31 · PVG 21:31 · LAX 06:31 · JFK 09:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.