hibernate将实体属性更新为null,这些属性不在表单提交中

时间:2017-11-16 17:57:10

标签: java spring hibernate spring-mvc

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

这是我到目前为止所拥有的。

用户界面

<form role="form" th:action="@{/user/{userId}/official(userId=${userId})}" th:object="${user}" method="post">

            <!--  first form group  -->
            <div class="form-group">

                <label class="control-label col-xs-2">First Name</label>
                <div class="col-xs-2">
                    <input type="text" class="form-control" th:field="*{firstName}" placeholder="First Name" />
                </div>

                <label class="control-label col-xs-2">Last Name</label>
                <div class="col-xs-3">
                    <input type="text" class="form-control" th:field="*{lastName}" placeholder="Last Name" />


            <!--  first form group end  -->
            </div>
            <br/><br/>

            <!--  third form group  -->
                <div class="form-group">
                    <label class="control-label col-xs-2">Email Address</label>
                    <div class="col-xs-2">
                        <input type="text" class="form-control" th:field="*{emailAddress}" placeholder="Email Address" />
                    </div>

            </div>

            <div class="form-group">
                                    <div class="col-xs-2">
                                        <input type="submit" value="Update" class="btn btn-primary" />
    </form>

控制器:

@Controller
public class UserController {

    @Autowired
    private IUserService userServiceImpl;

    @RequestMapping(value = "/user/{userId}/official", method = RequestMethod.GET)
    public String getUserOfficialInfo(@PathVariable("userId") Integer userId, Model model) throws ServiceBusinessException {

                    UserBO userBO = userServiceImpl.findUserByUserId(userId);
                    model.addAttribute("user", userBO);
                    model.addAttribute("userId", userId);
                    model.addAttribute("genders", EnumSet.allOf(Gender.class));
                    return "official";
    }


    @RequestMapping(value = "/user/{userId}/official", method = RequestMethod.POST)
    public String updateUserOfficialInfo(@PathVariable("userId") String userId, @ModelAttribute("user") UserBO user,BindingResult result, Model model) throws ServiceBusinessException {

                    userServiceImpl.updateUser(user);
                    UserBO userBO = userServiceImpl.findUserByUserId(Integer.parseInt(userId));
                    model.addAttribute("user", userBO);
                    model.addAttribute("userId", userId);
                    model.addAttribute("genders", EnumSet.allOf(Gender.class));
                    return "official";
    }
}

DAO:

@Override
    public void updateUser(UserEntity user) throws DaoException {
        entityManager.merge(user);
    }

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

在表单Submit上,调用控制器中的POST方法,调用服务层,然后执行DAO中的merge方法。

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

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

有人可以告诉我上述内容是否正确?

如果是的话,那么我的下一个问题就是这不是一件非常繁琐而又需要更多努力的问题。我的意思是在我不希望在表单中显示整个对象的情况。也不在隐藏的领域。我很惊讶没有办法解决这个问题,我必须遵循上面刚才描述的方法。

有更好的方法吗?我不会只使用JDBC模板吗?我知道我会在那里写锅炉板代码但是我在这里为每次往返UI都写了getter和setter。

3 个答案:

答案 0 :(得分:1)

考虑使用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
}

如果您使用的是4.x版本的Hibernate,则可能需要使用@DynamicUpdate,因为最近已弃用@org.hibernate.annotations.Entity

参考文献:

https://www.mkyong.com/hibernate/hibernate-dynamic-update-attribute-example/ https://docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/annotations/Entity.html https://docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/annotations/DynamicInsert.html https://docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/annotations/DynamicUpdate.html

答案 1 :(得分:0)

您可以将以下代码放在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();
        }
    }

因此,您可以在服务上执行此操作:

public UserBO updateUser(String userId, UserBO user ) {
                    UserBO reference = findOne(Integer.parseInt(userId));
                    fillNotNullFields(reference, user);
                    return updateUser(reference);
    }

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

答案 2 :(得分:0)

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

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