在 Spring MVC webapp 中,我有一个表单和一个相应的控制器。在控制器中,我需要知道用户是否更改了表单中的任何值,以便知道是否更新数据库状态表中的“changed_on”列。
我不在乎知道哪个字段发生了变化,无论如何我都会保存用户的数据。但是,如果用户在提交之前更改了 A->B,然后又回到了 A,我不想认为这是一个更改。如果用户实际更改数据,我只想更新日期/时间列,因为稍后我需要知道用户上次更改数据的时间。
实现这一目标的最佳方法是什么?在后端还是前端更好?这似乎是一个非常普遍的问题:如何判断用户是否正在更改表单上的数据。
我想出了一些潜在的解决方案。我的表单字段是布尔值、整数、字符串和枚举。
前端解决方案
- 在 Javascript 中使用
onChange
或侦听器设置隐藏的表单字段。onBlur
- 使用 jQuery回调函数,
$("form :input").change()
并使用.data()
将changed
值设置为true
.
后端解决方案
在 Web 表单绑定到的表单类中(通过
@InitBinder("formName")
在我的控制器中),hashCode()
根据我有兴趣监视更改的所有字段进行覆盖。因为我没有在任何集合中使用我的表单类,也没有传递给 Hibernate,所以我不认为这会产生任何不良的副作用。这是使用 Guava 的实现:@Override public int hashCode() { return Objects.hashCode(this.field1, this.field2, etc.); }
然后我可以将旧表单对象(在
get()
方法中创建)与由WebDataBinder
提交的表单值创建的新表单对象进行比较。如果哈希值不同,则用户更改了某些内容。我对这种方法的问题是,如何保留旧的表单对象?有没有办法把它塞进页面中
ModelAndView
,从 GET 到 POST 都可以存活?doPost()
或者我可以通过重新加载基于数据库的持久实体在我的方法中重新创建它吗?如果 Hibernate 缓存了我的实体,那么在持久化任何新更改之前重新创建原始表单对象应该很快。类似于 #1,但使用反射而不是对单个字段的硬编码依赖。这样,如果添加了任何新的表单字段,我们就不会忘记将它们添加到
hashCode()
方法中。这是一个实现:public static boolean hasFormChanged(Object form1, Object form2) { return HashCodeBuilder.reflectionHashCode(form1) != HashCodeBuilder.reflectionHashCode(form2); }
现在,我知道反思很慢;在我的测试中,比上述方法慢了大约 8-12 倍
Objects.hashCode()
。但是,我只有大约 4 个表单字段,并且在我的本地机器上的测试中,基于反射的方法每次调用大约需要 3.5微秒。反射的另一个缺点是我们需要注意将任何新的实例变量添加到表单类(或其父类)中,我们不想将其包含在比较中。