【问题标题】:hibernate updating entity attributes to null which are not in form submit休眠将不在表单提交中的实体属性更新为null
【发布时间】:2017-11-17 07:14:29
【问题描述】:

我正在使用 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";
    }
}

道:

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

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

在提交表单上,调用控制器中的 POST 方法,该方法调用服务层,然后执行 DAO 中的合并方法。

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

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

有人可以告诉我以上我说的是否正确吗?

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

有没有更好的方法来做到这一点?我不会只使用 JDBC 模板吗?我知道我会在那里编写样板代码,但我也会在这里为 UI 的每次往返编写 getter 和 setter。

【问题讨论】:

  • Hibernate 对表单一无所知。我见过的典型模式是从数据库加载实体,将表单中的字段映射到实体,然后持久化实体。我相信有一些弹簧管道可以帮助解决这个问题,但我不记得谷歌应该找到它的具体细节。

标签: java spring hibernate spring-mvc


【解决方案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

【讨论】:

  • 让我试试这个,如果我发现有任何问题,我会告诉你的。看起来这可行。
【解决方案2】:

您可以将以下代码放在一个 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 的支持。希望对您有所帮助。

【讨论】:

  • 是的,但这不是重点,重点是,如果这是休眠的预期行为,我想知道其他人是如何处理的?如果这不是预期的行为,在这种情况下应该发生什么?
  • @user641887 在这种情况下我实际上不知道。我仅在使用 DTO 设计模式的 restful web servides 中使用过 hibernate,并且此问题与 rest 方法无关。实际上,在 rest 中直接从视图层持久化对象并不是一个好习惯。
【解决方案3】:

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

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

【讨论】:

  • 我不想精确地做到这一点,对于一个用户来说,我有多个属性,比我在表单中拥有的属性要多得多..那将是完全多余的..你不这么认为吗?
  • 这是大多数框架(如 Grails)中非常常用的策略。如果您真的想避免放置多个隐藏字段,您可能需要查看动态插入。 mkyong.com/hibernate/hibernate-dynamic-insert-attribute-example
  • 但我的是对现有记录的更新操作.. 不是插入
  • 注解支持插入和更新。我将基于此在下面添加一个新答案。这是相关的Javadoc
猜你喜欢
  • 2012-01-14
  • 2020-02-16
  • 2018-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-24
  • 2021-12-04
  • 1970-01-01
相关资源
最近更新 更多