Spring MVC:SessionAttributes和List

时间:2011-02-04 17:44:57

标签: list session spring-mvc attributes

我面临春天的问题,如下:

在SessionAttributes中,我有一个对象人,其属性地址是一个列表。每当通过控制器更新人员时,之前的条目仍然存在。因此,例如,如果我有亲自地址:旧地址1,旧地址2,旧地址3和我通过表单更新人只有一个新地址,地址列表变为:新地址1,旧地址2,旧地址3而预期的行为是只有“新地址1”。我似乎找不到解决这个问题的方法。我使用的是Spring 3.0.X。

请在下面找到显示手头问题的所有相关代码。

Person.java:

package com.convert.dashboard.web.test;

import java.util.List;

public class Person {

private String name;

private Integer age;

private List<String> addresses;

public Person(List<String> addresses) {
    this.addresses = addresses;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public Integer getAge() {
    return age;
}

public void setAge(Integer age) {
    this.age = age;
}

public List<String> getAddresses() {
    return addresses;
}

public void setAddresses(List<String> addresses) {
    this.addresses = addresses;
}

}

TestController.java

package com.convert.dashboard.web.test;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/test")
@SessionAttributes("person")
public class TestController {

@RequestMapping(value = "/")
public ModelAndView xyz() {
    ModelAndView mav = new ModelAndView();
    List<String> abc = new ArrayList<String>();
    abc.add("old address1");
    abc.add("old address2");
    abc.add("old address3");
    Person person = new Person(abc);
    mav.addObject("person", person);
    mav.setViewName("cForm");
    return mav;
}

@RequestMapping("/save")
public @ResponseBody
String process(@ModelAttribute("person") Person person) {
    return "<body>" + " Name:" + person.getName() + "  Age: " + person.getAge() + " Addresses: " + person.getAddresses();
}
}

cForm.jsp:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"       "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>populate form</title>
</head>
<body>
<form:form modelAttribute="person" action="/dashboard/test/save">

<form:hidden path="name" value="X" />
<form:hidden path="age" value="20" />
<form:hidden path="addresses[0]" value="New address" />
<input type="Submit" value="Submit" />
</form:form>
</body>
</html>

5 个答案:

答案 0 :(得分:2)

我想解决一些设计问题。

  1. 您正在为表单绑定和域数据使用单个对象。这往往会产生与您在此遇到的问题完全相同的问题。问题不在于表单无法“清除”会话对象的地址;问题是会话对象将其数据结构泄露到表单,这会导致绑定问题。

  2. 表单了解Person对象的内容。具体来说,表单要求person.getAddresses()列表中有三个地址。与上面的(1)一样,问题是域结构泄漏到视图层中。

  3. 我建议您创建两个不同的“人”类:一个用于表示域数据(会话对象),另一个用于完全镜像表单结构(表单绑定对象)。您的表单将包含直接映射到PersonForm类中的属性的字段,而在TestController中,您可以从PersonForm获取数据并相应地更新会话Person。然后,表单输入不需要设计为处理Person.addresses列表的不同状态。

    这种方法确实需要更多的代码,但不是非常多,并且形式复杂性和表单/域解耦的节省非常值得。

答案 1 :(得分:0)

所以解决方案如下:

通过在客户端附加删除AJAX调用并将以下代码添加到控制器。

    @RequestMapping("/remeveAddress")
    public @ResponseBody String removeElement(@ModelAttribute("person") Person person, @RequestParam Integer addressId, Model model){
    person.getAddresses().remove(addressId);
    model.addAttribute("person", person);
    return "{}";
    }

答案 2 :(得分:0)

我遇到了同样的问题,并通过向控制器引入init绑定器来解决它。 init绑定器重置会话属性的地址列表。 根据要求,它可以做一些更复杂的工作。

@InitBinder
public void initBinder(HttpSession session) {
    Person person = (Person)session.getAttribute("person");
    person.getAddresses().clear();
}

希望可以帮助别人:)

答案 3 :(得分:0)

最近我遇到了同样的问题,而且我找到了一个非常简单的解决方案。 因此,您只需为列表添加隐藏字段即可。 Spring转换服务将从此隐藏字段的空字符串值中设置空列表:)

您可以看到以下解决方案:

<input type='hidden' name='addresses' value='' />

然后把你的其他代码:

<form:input path="addresses[0]" />
<form:input path="addresses[n]" />

注意: 如果您需要将字符串转换为 YourClass列表,并且您还没有拥有转换器或编辑器。 因此,请确保至少ObjectToObjectConverter可以执行此操作,YourClass必须具有 String arg的构造函数或静态方法 valueOf(String)

答案 4 :(得分:-1)

另一种可能的解决方案是使用处理程序拦截器,如下所示。

servlet.xml中:

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/test/**" />
        <bean class="com.convert.dashboard.web.test.PersonInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

PersonInterceptor.java:

public class PersonInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {

        Person person = (Person)request.getSession().getAttribute("person");
        if (person != null)
            person.getAddresses().clear();

        return super.preHandle(request, response, handler);
    }
}