如何仅在休眠状态下更新JSON定义的属性

时间:2019-07-23 21:45:51

标签: angular spring hibernate spring-boot jackson

我正在创建一个SpringBoot / Angular 8应用程序,并且在尝试更新解耦的前端/后端对象时遇到一些问题。当我发送包含角度模型的json发布请求时,将@JsonIgnore或其他缺失值更新为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);
}

示例角度服务方法

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为空值。

4 个答案:

答案 0 :(得分:0)

如果表单特定于实体上的数据子集,则创建一个单独的模型。如您所说,您要获取用户的名字,姓氏,其他个人详细信息(应该在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.

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

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

<component-A :some-prop="foo">
     <component-B slot-scope="slotProps"></component-B>
</component-A>

这解决了我所有的紧迫问题,但是我怀疑烦恼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)); } 可能会在以后引起问题。

答案 3 :(得分:0)

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

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.koedia.shiva.common.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));
    }
}