Spring Data JPA:如何优雅地更新模型?

Posted

技术标签:

【中文标题】Spring Data JPA:如何优雅地更新模型?【英文标题】:Spring Data JPA: How to update a model elegantly? 【发布时间】:2015-10-24 10:31:22 【问题描述】:

    我的模型是这样的:

    @Entity
    @Table(name = "user")
    public class User 
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        @Column(name="email")
        private String email;
    
        @Column(name = "weblink")
        private String webLink;
    
        //getter & setter
    
    

    我们通过 http 请求收集表单或移动数据,springmvc 会将这些数据提供给 Model 类用户。

    例如,我有一个这样的请求:

    http://localhost:8080/update?id=1919&email=xx@google.com

    在控制器中,请求的 url 及其参数将自动转换为 User 对象。

    @RequestMapping(method = RequestMethod.GET, value = "/update0")
    public User update(@ModelAttribute("User") User user)
    
        System.out.println(user.getId());
        System.out.println(user.getEmail());
        System.out.println(user.getWebLink());
    
        return userRepository.save(test);
    
    

    如果我在mysql中有一条id为1919的记录,并且列(id、email、weblik)都有值。

    如你所见,通过 web 或 mobile 传递的用户对象有两个属性

    http://localhost:8080/update?id=1919&email=xx@google.com

    id 和 email 有值,而 weblink 没有。

    所以,如果我执行save方法,列email会更新为xx@google.com,weblik字段也会更新为NULL,但是我不想更新这个字段,我只是想要更新电子邮件字段。

    我有两种方法可以解决这个问题,但都不是优雅的。

    5.1 先加载用户对象并更新

    User userExist = userRepository.findOne(user.getId());
    userExist.setEmail(user.getEmail());
    
    //or using 
    //BeanUtil.copyProprty(formDto,modle)
    
    userRepository.save();
    

    5.2 使用@DynamicUpdate,但它不起作用。

    有没有其他方法可以更新用户模型并且不做一些额外的工作。

    提前致谢。

【问题讨论】:

spring JPA CRUD Repository and updating a record的可能重复 【参考方案1】:

最简单的方法是适当设置控制器方法:

@RequestMapping(value = "/users/user", method = RequestMethod.PATCH)
public … updateUser(@ModelAttribute User user)  … 

根据reference documentation调用此方法时发生以下步骤:

    需要获取User 的实例。这基本上需要一个从StringUserConverter 向Spring MVC 注册,以便将从URI 模板中提取的路径段转换为User。如果你是例如使用 Spring Data 并启用其reference documentation 中所述的 Web 支持,这将开箱即用。 获取现有实例后,请求数据将绑定到现有对象。 绑定的对象将被交给方法。

其他提示

不要使用GET 作为更新的HTTP 方法。 GET 被定义为安全操作(无副作用),而更新则不是。 PATCH 是正确的方法,因为它被定义为允许对现有资源进行部分更新。 要将表单数据提交到PUTPATCH 请求中,您需要按照here 的说明向应用程序注册HttpPutFormContentFilter。我提交了issue with Spring Boot 以默认注册。

【讨论】:

@oliver-gierke 我得到了第 1 步的工作,但我不能让第 2 步工作,我的意思是对象是从作为参数传递给方法的 db 获得的,但是请求似乎被忽略了。根据我的理解,我发送到请求正文中的任何内容都应该应用到获取的对象中。有什么建议?你有一个可行的例子来看看吗?谢谢! 算了,我发现了问题。 HttpPutFormContentFilter 没有被创建,因为我正在扩展 WebMvcConfigurerAdapter,这会阻止 WebMvcAutoConfiguration 的实例化。现在我还有一个问题,为什么HttpPutFormContentFilter只在内容类型为application/x-www-form-urlencoded时才应用?是否可以应用相同的过程但使用 application/json 内容类型?谢谢! @MauroMonti 你有没有找到一种优雅的方式来为 JSON 和/或 XML 实现这个? 无论如何要从 authenticationObject 中获取 Id 以进行实例化?目前在您发布的解决方案中,它要求在"/users/user" 中发送userId。我需要这个,因为当 id 为 1 的用户登录时,我只希望他们转到页面 /user 而不是 /user/1。当他们想要更新某些内容时,他们仍然会转到 PATCH /user 而不是 PATCH /user/1 【参考方案2】:

首先我要说@Oliver GierkePatch 解决方案真的很棒。

而在我之前的项目中,我的解决方案是: 1、创建抽象类实现JpaRepository,实现save()功能。 2. 在保存函数中,从数据库中获取实体。它不存在,只是保存它。否则,使用反射从更新的实体中获取所有 NOT NULL 字段,并从 DB 中设置为实体。 (和你做的很相似,但我是为所有的保存功能做的)

【讨论】:

以上是关于Spring Data JPA:如何优雅地更新模型?的主要内容,如果未能解决你的问题,请参考以下文章

使用 spring data jpa 更新单个字段

Spring Data JPA Repository:如何有条件地获取子实体

Spring data jpa 插入多个表以避免锁定表

如何更新 Spring Data JPA @Modifying @Query 查询中的 JPA/Hibernate @Version 字段?

如何将自定义sql附加到Spring Data JPA插入/更新

Spring Data JPA 中的 FetchMode 是如何工作的