在 Spring MVC webapp 中,我有一个表单和一个相应的 Controller 。在 Controller 中,我需要知道用户是否更改了表单中的任何值,以便我知道是否更新数据库中状态表中的“changed_on”列。
我不想知道哪个字段发生了变化,无论如何我都会保存用户的数据。但是,如果用户在提交之前更改了 A->B,然后又回到了 A,我不想将其视为更改。我只想在用户实际更改数据时更新日期/时间列,因为稍后我需要知道用户最后一次更改数据的时间。
实现此目标的最佳方法是什么?在后端还是在前端更好?这似乎是一个非常的普遍问题:如何判断用户是否正在更改表单上的数据。
我想出了一些可能的解决方案。我的表单字段是 boolean 值、整数、字符串和枚举。
前端解决方案
- 使用 Javascript 中的
onChange
或onBlur
监听器设置隐藏的表单字段。 - 使用 jQuery callback function使用
$("form :input").change()
,并使用.data()
将changed
值设置为真
。
后端解决方案
在 Web 表单绑定(bind)的表单类中(通过我的 Controller 中的
@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); }
现在,我知道反射很慢;在我的测试中,比上面的
Objects.hashCode()
方法慢大约 8-12 倍。但是,我只有 ~4 个表单字段,并且在我的本地计算机上进行的测试中,基于反射的方法每次调用大约需要 3.5 微秒。反射的另一个缺点是我们需要注意向表单类(或其父类)添加任何我们不想想要包含的新实例变量比较。
最佳答案
对于前端解决方案,您可以实现更通用的示例,可以应用于任何页面。在页面加载时,遍历 form
元素,然后遍历表单的每个字段并保存其名称和值
例如,
["form1", {field1: "value1"}, {field2 : "value2"}]
等
在提交之前,您可以检查新表单值是否等于旧表单值,并添加额外的属性“已更改”
每个前端解决方案都很丑陋。当然最好在服务器端制作它
你可以将旧对象存储在httpSession中,或者你可以第二次请求它(注意hibernate默认不使用缓存)。使用 hashCode
而不是 equals
有什么意义?当不同的对象返回相同的 hashCode 时,有很小的冲突可能性
最直接的方法 - 第二次从 Db 请求您的对象,并使用 equals 比较它们
关于java - 在基于 Spring MVC 的 Web 应用程序中,如何判断用户是否提交了具有更改值的表单?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10400418/