【问题标题】:How to avoid overwriting of non-null values with null values?如何避免用空值覆盖非空值?
【发布时间】:2011-09-19 15:35:45
【问题描述】:

我正在使用 Spring MVC 从客户端接收 JSON 并自动从中创建一个对象。问题是客户端不会将实体中的所有字段都发送到服务器,但某些字段为空并覆盖调用 userDao.persist(user) 的现有值。例如,我有这个实体:

@Entity
public class User {

    @Id @GeneratedValue
    private int id;

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

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

【问题讨论】:

  • 在密码字段上使用@jsoningnore 注释

标签: java hibernate jpa entitymanager


【解决方案1】:

我认为您的问题的根源在于您从 JSON 解析中返回的对象从未包含实际值。它是一个 bean,只有 JSON 中的值集。

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

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

添加@NotNull 约束和Bean Validation 将确保尝试保存时值不为空。不幸的是,它们不会帮助您将值放入要保存的实体中。

【讨论】:

  • 是的,您关注的是问题。关键是我需要知道哪个是“某种要合并的适配器”。因为另一种方法是(如您所说)“将 JSON 中的非空字段设置到加载的实体上”,在具有 20 个字段的实体上手动完成可能非常无聊和不优雅。
  • @Fabio 不幸的是,我们有时不得不做一些无聊的事情。这个想法是做一次!我建议创建一个单独的对象(也是接口)来进行合并,这样您就可以封装合并逻辑。然后,您可以在需要的地方对其进行测试和重用。
  • 我怎样才能只做一次?每个实体的合并实现必须不同...
  • @Fabio 确实,除非您想使用反射,否则您需要按实体完成工作。问问自己,我需要为多少个实体执行此操作?每个实体(包括测试)执行一次会花费你那么长时间吗?
【解决方案2】:

我也有同样的问题。 我在this way解决了。

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;
}
}

这显然是一种解决方法,但它似乎工作顺利......在接下来的几天里,我想我会写递归部分。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      检查Hibernate Validation 项目,该项目可用于在DAO 级别以及Spring Web layer 上验证您的对象。

      【讨论】:

        【解决方案5】:

        我作为一个没有经验的学生写了这个答案。今天我的回答与@James DW 的回答相似。此外,从术语userDao 来看,我认为它是某种 ORM/ODM。在这种情况下,绝对值得搜索“ORM/ODM 的优缺点”。

        原始答案(早在 2011 年就被接受):

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

        【讨论】:

        • 你的意思是这样的: public void update(User jsonUser) { User dbUser = userDao.getUserById(jsonUser.getIdApp()); if(jsonUser.getName() != null) dbUser.setName() = jsonUser.getName(); }
        • 抱歉之前的评论,我不知道如何在 cmets 中使用缩进 :-D
        • 我认为 cmets 中的缩进是不可能的。无论如何阅读this。它是一个关于存储过程的教程。一般来说,它就像一个由数据库执行的小功能,因此您可以 100% 确定您的所有数据都已在那里检查过。不管你最初使用什么语言,只要调用那个过程,而不是调用'insert into ....'
        • 在某种程度上是的。但是在您的存储过程中,您不再处理 JSON 对象中的内容,而是拥有分离的元素。如果您的问题是如何不更新 password 字段 - 那么它可以简单地使用 INSERT (userName, field1, ...) VALUES(...) 并跳过密码字段。我建议你(如果是这样的话)重新考虑你的设计,因为你不应该一直写到包含密码字段的表中。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-26
        • 2023-02-20
        • 1970-01-01
        • 1970-01-01
        • 2015-03-01
        • 2013-07-01
        相关资源
        最近更新 更多