Spring整合SpringMVC和SpringDataJPA更新前端表单提交的数据后,出现的数据丢失问题

版权声明:引用本文章请申明参考来源!!! https://blog.csdn.net/zb201666/article/details/88770475

我们在做项目中,经常会遇到这样一个问题,后端使用SpringMVC接收前端提交的更新数据表单的参数后,调用Service层的更新方法更新数据后,发现前端展示中,刚刚更新的那个对象的数据展示不完整了,某些属性的值直接就消失了。博主在最近的项目中就遇到了这个问题,现我将出现这个问题的原因以及解决方案总结如下。

原因分析

我们在发生数据更新时,编辑表单中没有将需要更新编辑的对象的所有属性进行表单回填,往往我们在更新某个对象时都只会关注需要更新的属性信息,对于不发生更改的属性数据直接略过,这样就会造成我们在后端接收的时候,接收到的对象属性不完整,一旦我们调用了更新方法【博主采用的是SpringDataJPA中的更新方法,采用原生的SQL语句去更新特定的字段这种方式不会出现这种数据丢失问题】对数据库的数据进行更新的时候,就会将原本某些列字段【不需要更新的列】的数据置为空。然后在前端进行查询展示对象数据的时候就会发现刚刚修改的对象数据不完整,造成了数据丢失的严重问题。

解决方案

  1. 采用@Column(updatable = false)注解
    在这里插入图片描述
    在我们的实体类中的某些不需要进行更新的字段上加上@Column(updatable = false)注解,从而使得该字段在数据库更新时自动忽略,无法被修改。但是这种方式只适用于某些特定的固定不变的字段属性,一般情况下实体类的属性都会存在更新的可能,如果我们用这种方式直接限制死不利于我们的项目后期的维护和迭代,造成的影响也是不容忽视的。

  2. 表单中隐藏其它不需要修改的字段的值
    在这里插入图片描述
    明白了方案1的缺陷之后,我们可以采取在更新表单中隐藏其它字段的方式,这样表单在回填时就会将所有字段的信息都回填上,后端在接收时就会将所有字段信息都封装起来,再调用更新方法更新数据库时就不会将原本有的字段信息设置为空。这种方式虽然能够解决数据丢失的问题,但是操作起来未免有些麻烦,一旦字段过多,需要隐藏的信息就多,前端界面写起来就不够简洁。

  3. 先查询数据库,获取持久状态的对象,然后把页面的数据set到对象里面
    有这样一种思想:前端编辑表单还是之前那种写法,后端在执行更新方法之前先根据主键查询一下数据库,将已经持久化的对象查询出来,然后调用实体类的set方法将编辑表单提交的数据set到查询到的持久化对象中,然后在执行更新方法时将设值完毕的持久化对象更新回去,这样就能在不改变前端界面的前提下将对象进行更新,同时保留住没有发生修改的对象信息。
    但是如果set操作交由我们程序员直接来完成就未免显得不够nice,毕竟字段过多就会显得比较麻烦。不过不用担心,SpringMVC中为我们提供了一种解决方法,那就是使用@ModelAttribute注解。
    该注解运用在方法上,会在每一个@RequestMapping标注的方法前执行,如果有返回值,则自动将该返回值加入到ModelMap中;运用在参数上,会将客户端传递过来的参数按名称注入到指定对象中,并且会将这个对象自动加入ModelMap中,便于View层使用;由于我只是解决对象更新时发生的数据丢失问题,并不牵涉其它业务,所以可以将该注解的两种用法结合一下。
    在这里插入图片描述在这里插入图片描述注意:此时更新方法更新的对象是先行方法查询的持久化对象按照前端传入的信息设置完之后的对象了,已经具有了修改前和修改后【主要针对于那些不需要修改的属性】的所有信息。

  4. PS
    如果此时修改的对象中包含了另一个对象【比如说数据库中员工表通过外键去关联了另一张部门表】,一旦我们去修改外键值,就会抛出n to n 异常,因为在SpringDataJPA中已经持有化的对象的主键/外键值不能被直接修改。为了解决这个问题,我们需要在修改之前解决两个对象之间的关联关系,比如当前我要去修改员工对应的部门,就需要解除当前员工已有的部门信息:
    在这里插入图片描述
    【getOne方法我是根据需要拓展了SpringDataJPA原有的查询方法,底层还是用了SpringDataJPA的查询方法。所有此时查询出来的就是处于持久化状态的对象。】

通过添加@ModelAttribute这种方式,我们能够很好的解决更新过程中引发的数据丢失问题,从而避免发生一系列的严重问题。

猜你喜欢

转载自blog.csdn.net/zb201666/article/details/88770475