【问题标题】:How to only update JSON defined properties in hibernate如何仅在休眠中更新 JSON 定义的属性
【发布时间】:2019-12-02 00:00:00
【问题描述】:

我正在创建一个 SpringBoot/Angular 8 应用程序,但在尝试更新解耦的前端/后端对象时遇到了一些问题。当我发送包含角度模型的 json 发布请求时,@JsonIgnore'd 或其他缺失值将更新为 null 而不是被忽略。

相关问题:

这个堆栈溢出问题密切相关,推荐的解决方案确实有效,但它破坏/绕过了一堆 Jackson/Hibernate 注释(例如 @Inheritance(strategy=InheritanceType.JOINED)@Transient),所以我想寻找替代方案如果可能,解决方案: hibernate partially update when entity parsed with json

Java POJO 示例

@Entity
@Table(name = "users")
@DynamicUpdate
public class User implements Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String a;

    @Column
    private String b;

    @Column (nullable = false)
    @JsonIgnore
    private String c;

   // standard getters and setters
}

Java Rest API 示例

@RequestMapping(value = "/api/user", method = RequestMethod.POST)
public @ResponseBody ResponseEntity<User> saveOrUpdate(@RequestBody User user, BindingResult result, HttpServletRequest request) {
    if(user.getId()!=null){
        user=ws.update(user);
    }else{
        user=us.create(user);
    }
    return new ResponseEntity<>(user,HttpStatus.OK);
}

Angular 服务方法示例

saveUser(user:User): Observable<User>{
    const body = JSON.stringify(user);
    return this.http.post<User>('/api/user', body).pipe(map( response => response));
}

现有数据库模型:

{ 
  id:1,
  a:"some value a"
  b:"some value b"
  c:"some value c"
}

角柱体:

{ 
  id:1,
  a:"some value a2"
  b:"some value b2"
}

预期的休眠查询:

update users set a=?,b=? where id=?

实际休眠查询:

update users set a=?,b=?,c=? where id=? 在这种情况下,C 是一个空值。

【问题讨论】:

  • 你需要先找到使用User foundUser=ws.findById(id);之后 BeanUtils.copyProperties(newUser, foundUser);并保存后。
  • 如果用户实际存在与否,您是否不必验证您的数据库?
  • 这仍然是一张未解决的票@spring-data-jpa...stackoverflow.com/q/43780226/592355,Johnatan Fox 的方法听起来很简单!

标签: angular spring hibernate spring-boot jackson


【解决方案1】:

不要对从 angular 发送的对象使用 update 方法,因为它会替换数据库中的整个原始对象。在更新的情况下,我建议您从数据库中检索现有对象(例如使用 spring-data 存储库 findOne 方法),然后仅复制修改后的字段而忽略空值。您可以使用 Apache 公共库中的 BeanUtils copyProperties 方法。原始方法在复制时不会忽略空值,因此您必须稍微覆盖它:

public class NullAwareBeanUtilsBean extends BeanUtilsBean {
    @Override
    public void copyProperty(Object dest, String name, Object value) throws IllegalAccessException, InvocationTargetException {
        if (value == null)
            return;
        super.copyProperty(dest, name, value);
    }
}

这个测试重现了你想要做的事情:

类更新:

public class A {

    private String foo;

    private String bar;

    public String getFoo() {
        return foo;
    }

    public void setFoo(String foo) {
        this.foo = foo;
    }

    public String getBar() {
        return bar;
    }

    public void setBar(String bar) {
        this.bar = bar;
    }
}

测试类:

import java.lang.reflect.InvocationTargetException;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.lang3.builder.ToStringBuilder;

import com.zpavel.utils.NullAwareBeanUtilsBean;

public class MyTest {

    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
        A a1 = new A();
        a1.setFoo("foo");
        A a2 = new A();
        a2.setBar("bar");
        BeanUtilsBean notNull = new NullAwareBeanUtilsBean();
        notNull.copyProperties(a2, a1);
        System.out.println(ToStringBuilder.reflectionToString(a2));
    }
}

【讨论】:

    【解决方案2】:

    我没有尝试过,但设置 dynamicUpdate = true 应该可以完成工作。 (如果您不知道将更改哪些属性,那么它可能是唯一干净的选项,因为前端仅发送更改的属性)。请按照本教程,让我知道它是否正常工作:Hibernate – dynamic-update attribute example

    【讨论】:

      【解决方案3】:

      我最终修改了我现有的解决方案,并使用反射为有问题的注释添加了排除项。

      这个解决方案基本上只是从会话中提取现有模型并复制所有非空属性。然后上面提到的@DynamicUpdate 属性确保只更新修改后的属性。

      protected String[] getNullPropertyNames (T source) {
              final BeanWrapper src = new BeanWrapperImpl(source);
              java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
              Set<String> emptyNames = new HashSet<String>();
              boolean skipProperty;
              for(java.beans.PropertyDescriptor pd : pds) {
                  skipProperty=false;
                  if(pd.getReadMethod()!=null) {
                      for (Annotation a : pd.getReadMethod().getAnnotations()) {
                          if (a.toString().equals("@javax.persistence.Transient()")) {/*Skip transient annotated properties*/
                              skipProperty = true;
                          }
                      }
                  }else{
                      skipProperty=true;
                  }
                  if(!skipProperty){
                      Object srcValue = src.getPropertyValue(pd.getName());
                      if (srcValue == null) emptyNames.add(pd.getName());
                  }
              }
              String[] result = new String[emptyNames.size()];
              return emptyNames.toArray(result);
          }
      
          // then use Spring BeanUtils to copy and ignore null
          public void copyUpdatedProperties(T newEntity, T existingEntity) {
              BeanUtils.copyProperties(newEntity, existingEntity, getNullPropertyNames(newEntity));
          }
      

      这解决了我所有的紧迫问题,但是我怀疑 @Inheritance(strategy=InheritanceType.JOINED) 的注释可能会在以后引起问题。

      【讨论】:

        【解决方案4】:

        如果表单特定于实体上的数据子集,则创建单独的模型。正如您所说,您希望获取用户 FirstName、LastName、其他应该在 Person 模型中的个人详细信息以及 LoginAccount 模型中的所有登录帐户相关部分

        所以你的模型变成这样:

        User Entity
        
        id
        a
        b
        c
        etc.
        
        FormModel
        
        id
        a
        b
        etc... and other fields that should only be visible and updatable within this specific form.
        

        例如,一个用户实体可能包含许多关于用户的不同属性。登录表单只需要用户名和密码。用户注册表单将仅包含该用户的基本信息,因此注册很容易。配置文件编辑表单将包含用户可以更新的更多属性。您可能希望将密码更新表单保留在仅包含密码字段的单独页面上。最后,您可能有一个管理员用户,他应该能够启用或禁用用户,但不应访问敏感信息,例如社会安全号码。使用将在每一种形式中提供完整用户 JSON 对象的用户实体是没有意义的。相反,为每个视图模型创建一个包含所需特定字段的视图模型。

        【讨论】:

        • 您的建议是有道理的,但它并没有解决我面临的核心问题。那就是 @JsonIgnore 和 Lazy Loaded 原语在发布时设置为空。您的建议意味着我只在检索数据时使用这些注释,而不是在推送数据时使用这些注释,这只是避免了我试图解决的问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-24
        • 1970-01-01
        相关资源
        最近更新 更多