正确的方式找到什么是修改了一个spring-mvc控制器的帖子



这是一个相当普遍的问题,但我将给出一个简单的例子。假设我有一个Web CRUD应用程序,它管理存储在数据库中的简单实体,只不过是经典的:JSP视图、RequestMapping注释控制器、事务服务层和DAO。在更新时,我需要知道字段的先前值,因为业务规则要求a进行涉及旧值和新值的测试。

所以我正在寻找这个用例的最佳实践。

我认为spring代码比我自己的代码测试得更广泛,更健壮,我希望尽可能用spring的方式

这是我尝试过的:

1/在controller中加载一个空对象,并在service中管理更新:
Data.java:

class Data {
    int id; // primary key
    String name;
    // ... other fields, getters, and setters omitted for brevity
}

DataController

...
@RequestMapping("/data/edit/{id}", method=RequestMethod.GET)
public String edit(@PathVariable("id") int id, Model model) {
    model.setAttribute("data", service.getData(id);
    return "/data/edit";
}
@RequestMapping("/data/edit/{id}", method=RequestMethod.POST)
public String update(@PathVariable("id") int id, @ModelAttribute Data data, BindingResult result) {
    // binding result tests omitted ..
    service.update(id, data)
    return "redirect:/data/show";
}

DataService

@Transactional
public void update(int id, Data form) {
    Data data = dataDao.find(id);
    // ok I have old values in data and new values in form -> do tests stuff ...
    // and MANUALLY copy fields from form to data
    data.setName(form.getName);
    ...
}

它工作得很好,但在实际情况下,如果我有许多领域对象和许多字段在每个,很容易忘记一个…当spring WebDataBinder完成它时,包括控制器中的验证,而我不必编写除@ModelAttribute以外的任何单一内容!

2/我试图通过声明一个Converter
从数据库预加载数据DataConverter

public class DataConverter<String, Data> {
    Data convert(String strid) {
        return dataService.getId(Integer.valueOf(strid));
    }
}

太神奇了!data如果从数据库中完全初始化,并且表单中存在的字段被正确更新。但是…没有办法得到之前的值…

所以我的问题是:使用spring DataBinder魔术来访问我的域对象的先前值的方法是什么?

你已经找到了可能的选择,所以我只是在这里添加一些想法;)

我将从使用空bean并将值复制到加载实例的选项开始:

如您在示例中所示,这是一种简单的方法。创建通用解决方案非常容易。

您不需要手动复制属性!看一下'BeanWrapperImpl'类。这个spring对象允许您复制属性,实际上它是spring本身用来实现其魔力的对象。例如,它被ParameterResolvers使用。

所以复制属性是简单的部分。克隆加载的对象,填充加载的对象,并以某种方式比较它们。

如果你有一个或几个服务,这是你要走的路。

在我的例子中,我们需要在每个实体上都有这个功能。使用Hibernate时,我们会遇到这样的问题:一个实体可能不仅在一个特定的服务调用中发生了变化,而且理论上在所有地方都可能发生变化。

所以我决定创建一个'MappedSuperClass',所有实体都需要扩展。这个实体有一个'PostLoad'事件监听器,它在加载后直接在一个瞬态字段中克隆实体。(如果您不需要在请求中加载数千个实体,则可以使用此方法。)然后你还需要'PostPersist'和'PostUpdate'监听器来再次克隆新状态,因为你可能不会在另一次修改之前重新加载实体。

为了方便控制器映射,我已经实现了一个'StringToEntityConverter'做你所做的,只是广义地支持任何实体类型。

在一般化的方法中发现变化将涉及相当多的反思。这并不难,我现在没有可用的代码,但你也可以使用'BeanWrapper':

为两个对象创建包装器。获取所有的"PropertyDescriptors"并比较结果。最难的部分是知道什么时候该停下来。只比较第一级还是需要更深入的比较?

另一个解决方案可能是依赖Hibernate enver。如果在同一事务期间不需要更改,则可以使用此方法。当Envers在刷新期间跟踪更改并创建"修订"时,您可以"简单地"获取twp修订并比较它们。

在所有情况下,您都必须编写比较代码。我不知道有一个库,但可能在java世界里有一些东西:)

希望对你有帮助。

最新更新