Java hibernate将表单submit中未包含的实体属性更新为null

Java hibernate将表单submit中未包含的实体属性更新为null,java,spring,hibernate,spring-mvc,Java,Spring,Hibernate,Spring Mvc,我正在使用thymeleaf、Spring和Hibernate构建一个MVC应用程序。我这里的问题更多的是关于冬眠而不是春天 这就是我目前所拥有的 用户界面 道: 控制器中的GET方法将用户对象获取到视图。但是在视图中,我只是在表单中显示了用户对象的一些属性 <input type='hidden' th:field="*{dateOfBirth}" /> 在表单Submit上,调用控制器中的POST方法,该方法调用服务层,然后执行DAO中的merge方法 现在我观察到的是,实体管

我正在使用thymeleaf、Spring和Hibernate构建一个MVC应用程序。我这里的问题更多的是关于冬眠而不是春天

这就是我目前所拥有的

用户界面

道:

控制器中的GET方法将用户对象获取到视图。但是在视图中,我只是在表单中显示了用户对象的一些属性

<input type='hidden' th:field="*{dateOfBirth}" />
在表单Submit上,调用控制器中的POST方法,该方法调用服务层,然后执行DAO中的merge方法

现在我观察到的是,实体管理器上的这个合并方法正在将表单中不存在的属性更新为null

我认为这是预期的行为,因为对象在从POST方法调用时是分离的。因此,正确的做法是首先从数据库中获取实体对象,然后向该对象设置表单中更改的字段,然后调用merge方法

有人能告诉我上面我说的是否正确吗

如果是的话,那么我的下一个问题是,这不是很乏味,需要付出更多的努力。我的意思是,在某些情况下,我不想在表单中显示整个对象。也不在隐藏字段中。我很惊讶没有办法处理这个问题,每次我都必须遵循我刚才描述的方法


有更好的方法吗?我不应该改用JDBC模板吗?我知道我会在那里编写锅炉板代码,但我在这里编写getter和setter,以及每次往返到UI时的getter和setter。

您可以将以下代码放入util类中,并在希望基于另一个引用对象填充对象时调用它:

public static <T> void  fillNotNullFields(T reference, T source) {
        try {
            Arrays.asList(Introspector.getBeanInfo(reference.getClass(), Object.class)
                    .getPropertyDescriptors())
                    .stream()
                    .filter(pd -> Objects.nonNull(pd.getReadMethod()))
                    .forEach(pd -> {
                        try {
                            Object value = pd.getReadMethod().invoke(source);
                            if (value != null)
                                pd.getWriteMethod().invoke(reference, value);

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    });
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }
    }

我找到了这个答案的支持。希望有帮助。

您需要将不必要的bean属性存储在隐藏字段中,以便在表单发布到控制器时重新映射它们。例如,如果您不想在页面上显示出生日期(假设出生日期已经是用户的属性),只需在表单中添加以下标记即可

<input type='hidden' th:field="*{dateOfBirth}" />

考虑使用
org.hibernate.annotations.entity
注释实体

@Entity
@Table(name = "user")
@org.hibernate.annotations.Entity(
        dynamicInsert = true, dynamicUpdate=true
)
public class User implements java.io.Serializable {
 // your properties
}
如果您使用的是Hibernate的4.x版本,您可能希望改用
@DynamicUpdate
,因为
@org.Hibernate.annotations.Entity
的用法最近已被弃用

参考资料:


Hibernate对表单一无所知。我看到的典型模式是从db加载实体,将表单中的字段映射到实体,然后持久化实体。我相信有一些spring管道可以帮助解决这个问题,但我记不起谷歌应该找到它的具体细节。是的,但这不是重点,重点是,如果这是hibernate的预期行为,我想知道其他人是如何处理的?如果这不是预期的行为,那么在这种情况下会发生什么?@user641887在这种情况下,我实际上不知道。我只有在使用DTO设计模式的restful web服务中使用hibernate的经验,这个问题与rest方法无关。实际上,在rest中直接从视图层持久化对象并不是一个好的做法。我不想精确地这样做,对于一个用户,我有多个属性,比表单中的属性多得多。。那完全是多余的。。你不这么认为吗?这是大多数框架(比如Grails)中非常常用的策略。如果您确实想避免放置多个隐藏字段,您可能希望查看动态插入。但我的是对现有记录的更新操作。。非插入注释支持插入和更新。基于此,我将在下面添加一个新答案。这是相关的,让我试试,如果我发现有任何问题,我会告诉你。。看起来这样行得通。
public UserBO updateUser(String userId, UserBO user ) {
                    UserBO reference = findOne(Integer.parseInt(userId));
                    fillNotNullFields(reference, user);
                    return updateUser(reference);
    }
<input type='hidden' th:field="*{dateOfBirth}" />
@Entity
@Table(name = "user")
@org.hibernate.annotations.Entity(
        dynamicInsert = true, dynamicUpdate=true
)
public class User implements java.io.Serializable {
 // your properties
}