Java 在基于SpringMVC的webapp中,如何判断用户是否提交了具有更改值的表单?

Java 在基于SpringMVC的webapp中,如何判断用户是否提交了具有更改值的表单?,java,forms,spring-mvc,Java,Forms,Spring Mvc,在SpringMVCwebapp中,我有一个表单和一个相应的控制器。在控制器中,我需要知道用户是否更改了表单中的任何值,以便知道是否更新数据库中状态表中的“changed_on”列 我不想知道哪个字段被更改了,我会保存用户的数据。但是如果用户在提交之前更改了-> B,然后返回到A,我不想考虑这一变化。我只想在用户实际更改数据时更新日期/时间列,因为我需要在以后知道用户上次更改数据的时间 实现这一目标的最佳方式是什么?在后端还是在前端做比较好?这似乎是一个非常普遍的问题:如何判断用户是否正在更改表

在SpringMVCwebapp中,我有一个表单和一个相应的控制器。在控制器中,我需要知道用户是否更改了表单中的任何值,以便知道是否更新数据库中状态表中的“changed_on”列

我不想知道哪个字段被更改了,我会保存用户的数据。但是如果用户在提交之前更改了-> B,然后返回到A,我不想考虑这一变化。我只想在用户实际更改数据时更新日期/时间列,因为我需要在以后知道用户上次更改数据的时间

实现这一目标的最佳方式是什么?在后端还是在前端做比较好?这似乎是一个非常普遍的问题:如何判断用户是否正在更改表单上的数据

我想出了一些可能的解决办法。我的表单字段是布尔、整数、字符串和枚举

前端解决方案
  • 使用Javascript中的
    onChange
    onBlur
    侦听器设置隐藏表单字段
  • 使用带有
    $(“form:input”).change()
    的jQuery,并使用
    .data()
    changed
    值设置为
    true
  • 后端解决方案
  • 在web表单绑定到的表单类中(通过我的控制器中的
    @InitBinder(“formName”)
    ),基于我感兴趣的所有字段重写
    hashCode()
    。因为我没有在任何集合中使用我的表单类,也没有传递到Hibernate,所以我不认为这会产生任何不良的副作用。下面是一个使用Guava的实现:

    @Override
    public int hashCode() {
        return Objects.hashCode(this.field1, this.field2, etc.);
    }
    
    然后,我可以将旧表单对象(在
    get()
    方法中创建)与
    WebDataBinder
    根据提交的表单值创建的新表单对象进行比较。如果哈希值不同,则用户更改了某些内容

    这种方法的问题是,如何保留旧的表单对象?有没有办法把它塞进页面的
    ModelAndView
    ,这样就可以从文章中保存下来?或者我可以在我的
    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微秒。反射的另一个缺点是,我们需要注意向表单类(或其父类)添加任何我们不想包含在比较中的新实例变量


  • 对于前端解决方案,您可以实现更通用的示例,它可以应用于任何页面。在页面加载时,迭代
    表单
    元素,然后迭代表单的每个字段并保存其名称和值

    比如说,

    [“form1”、{field1:“value1”}、{field2:“value2”}]

    在提交之前,您可以检查新表单值是否等于旧表单值,并添加附加属性“changed”

    每个前端解决方案都是丑陋的。当然,最好是在服务器端实现

    您可以将旧对象存储在httpSession中,也可以再次请求它(注意hibernate默认情况下不使用缓存)。使用
    hashCode
    而不是
    equals
    有什么意义?当不同的对象返回相同的哈希代码时,发生冲突的可能性很小


    最直接的方法是,第二次从Db请求对象,并使用equals将它们与重写
    equals()
    进行比较,我更担心会弄乱依赖于对象相等性的东西。也就是说,如果Spring获取了我的表单对象的多个实例,并且依赖
    equals()
    来比较它们,我将改变这种行为。而对于
    hashCode()
    ,我并没有以一种可以想象的方式重写任何行为,这可能会搞乱任何事情。我只是对得到假阳性/假阴性的可能性感到满意,我认为这是非常非常小的。你不应该担心这一点(不可能破坏任何东西)。毕竟,如果您仍然担心,只需创建一个新方法,该方法将逐个字段比较您的对象,而不会覆盖任何内容。我最终为此使用了一个后端解决方案:
    equalbuilder\reflectionEquals()
    。它的性能比使用
    EqualsBuilder
    硬编码每个字段慢8-10倍,但速度仍然非常快,可能会增加几微秒的请求时间。它还使我不必在所有15个表单类中实现
    equals()
    ,并在添加、重命名或删除表单字段时使它们保持最新。谢谢你的帮助。