Java 如何避免用空值覆盖非空值?

Java 如何避免用空值覆盖非空值?,java,hibernate,jpa,entitymanager,Java,Hibernate,Jpa,Entitymanager,我使用SpringMVC从客户机接收JSON并自动创建一个对象。问题在于,客户端并没有向服务器发送实体中的所有字段,但有些字段为null,并覆盖调用userDao.persist(user)的现有值。例如,我有一个实体: @Entity public class User { @Id @GeneratedValue private int id; private String username; private String password; pri

我使用SpringMVC从客户机接收JSON并自动创建一个对象。问题在于,客户端并没有向服务器发送实体中的所有字段,但有些字段为null,并覆盖调用userDao.persist(user)的现有值。例如,我有一个实体:

@Entity
public class User {

    @Id @GeneratedValue
    private int id;

    private String username;
    private String password;
    private String email;

但是用户从未向我发送密码,因此从JSON构建的对象的“password”字段为空。我不希望密码字段被空值覆盖。有一种方法可以说休眠“如果您发现一个空值,请忽略它,不要覆盖保存在数据库中的值?”。我不敢相信对于这个看似简单的问题没有一个简单的解决方案。

为您的属性实现setter并在那里进行检查。

检查项目,它可以用于在DAO级别验证您的对象,我认为问题的根源在于,从JSON解析返回的对象中从未包含实际值。它是一个只有JSON中设置的值的bean

您需要从DB加载实体,然后将JSON中的非空字段设置到加载的实体上。这样,将只设置JSON中提供的字段

我建议在保存DB版本之前使用某种适配器“合并”(而不是JPA合并)DB版本和JSON版本


添加@NotNull约束和Bean验证将确保在尝试保存时值不为null。不幸的是,它们无法帮助您将值保存到实体中。

我是一名未过期的学生,写下了这个答案。今天我的答案与@James DW的答案相似。另外,从术语
userDao
,我假设它是某种ORM/ODM。在这种情况下,绝对值得搜索“ORM/ODM的利弊”

原始答案(2011年接受): 如果您的问题只是数据库,那么我建议您使用一个存储过程,它检查该值是否为null,然后不更改现有值。这样,您仍然可以发送一个空值,并且您的验证在服务器端进行,这更为健壮。

我也有同样的问题。 我在一个星期内解决了它

import org.apache.log4j.LogManager;
导入org.apache.log4j.Logger;
导入java.lang.reflect.Field;
导入java.util.Hashtable;
公共类更新程序{
私有最终静态记录器log=LogManager.getLogger(Updater.class);
公共静态E更新程序(E旧实体、E新实体){
字段[]newEntityFields=newEntity.getClass().getDeclaredFields();
哈希表newHT=fieldsToHT(newEntityFields,newEntity);
类oldEntityClass=oldEntity.getClass();
Field[]oldEntityFields=oldEntityClass.getDeclaredFields();
用于(字段:oldEntityFields){
字段。setAccessible(true);
对象o=newHT.get(field.getName());
如果(o!=null){
试一试{
Field f=oldEntityClass.getDeclaredField(Field.getName());
f、 setAccessible(true);
log.info(“设置”+f.getName());
f、 集合(旧实体,o);
}捕获(非法访问例外e){
e、 printStackTrace();
}捕获(无此字段例外){
e、 printStackTrace();
}
}
}
返回旧实体;
}
私有静态哈希表fieldsToHT(字段[]字段,对象obj){
Hashtable Hashtable=新的Hashtable();
用于(字段:字段){
字段。setAccessible(true);
试一试{
objectretrievedObject=field.get(obj);
如果(retrievedObject!=null){
log.info(“扫描”+field.getName());
hashtable.put(field.getName(),field.get(obj));
}
}捕获(非法访问例外e){
e、 printStackTrace();
}
}
返回哈希表;
}
}

这显然是一个解决办法,但似乎工作顺利。。。在接下来的几天里,我想我将编写递归部分。

是的,您可以集中精力解决这个问题。重点是我需要知道哪个是“要合并的某种适配器”。因为另一种方法是(正如你所说的)“将JSON中的非空字段设置到加载的实体上”,这对于一个有20个字段的实体来说是非常枯燥和不雅的。@Fabio不幸的是,我们有时不得不做枯燥的事情。我们的想法是做一次!我建议创建一个单独的对象(也包括接口)来进行合并,这样可以封装合并逻辑。然后你可以在需要的地方测试和重用它。我怎么能只做一次呢?合并的实现对于每个实体都必须是不同的…@Fabio True,除非您想使用反射,否则您将需要对每个实体进行合并。问问你自己,我需要为多少个实体这样做?每个实体(包括测试)执行一次是否会花费您那么长的时间?您的意思是:public void update(User jsonUser){User dbUser=userDao.getUserById(jsonUser.getIdApp());if(jsonUser.getName()!=null)dbUser.setName()=jsonUser.getName();}抱歉前面的评论,我不知道如何在注释中使用缩进:-我认为注释中的缩进是不可能的。不管怎样,读吧。这是一个关于存储过程的教程。一般来说,它类似于由DB执行的一个小函数,因此您可以100%确定所有数据都已在那里检查过。不管您最初使用什么语言,只需调用该过程,而不是以某种方式调用“insert into…”yes。但在存储过程中,不再处理JSON对象中的内容,而是使用分离的元素。如果您的问题是如何不更新
密码
字段,那么只需使用
插入(用户名,字段1,…)值(…)
并跳过密码字段即可。我建议你(如果是这样的话)重新考虑一下你的设计,因为你不应该一直写信给我
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import java.lang.reflect.Field;
import java.util.Hashtable;



public class Updater {

private final static Logger log = LogManager.getLogger(Updater.class);

public static <E> E updater(E oldEntity, E newEntity) {

    Field[] newEntityFields = newEntity.getClass().getDeclaredFields();
    Hashtable newHT = fieldsToHT(newEntityFields, newEntity);

    Class oldEntityClass = oldEntity.getClass();
    Field[] oldEntityFields = oldEntityClass.getDeclaredFields();

    for (Field field : oldEntityFields){
        field.setAccessible(true);
        Object o = newHT.get(field.getName());
        if (o != null){
            try {
                Field f = oldEntityClass.getDeclaredField(field.getName());
                f.setAccessible(true);
                log.info("setting " + f.getName());
                f.set(oldEntity, o);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }

        }

    return oldEntity;
    }



private static Hashtable<String, Object> fieldsToHT(Field[] fields, Object obj){
    Hashtable<String,Object> hashtable = new Hashtable<>();
    for (Field field: fields){
        field.setAccessible(true);
        try {
            Object retrievedObject = field.get(obj);
            if (retrievedObject != null){
                log.info("scanning " + field.getName());
                hashtable.put(field.getName(), field.get(obj));
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    return hashtable;
}
}