我正在创建一个SpringBoot / Angular 8应用程序,并且在尝试更新解耦的前端/后端对象时遇到一些问题。当我发送包含角度模型的json发布请求时,将@JsonIgnore
或其他缺失值更新为null而不是被忽略。
这个堆栈溢出问题密切相关,并且推荐的解决方案确实起作用,但是它破坏/绕开了Jackson / Hibernate批注(例如@Inheritance(strategy=InheritanceType.JOINED)
和@Transient
),所以我想寻找可能的替代解决方案:
hibernate partially update when entity parsed with json
@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
}
@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为空值。
答案 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));
}
}