Spring MVC: атрибуты сессии и список - PullRequest
1 голос
/ 04 февраля 2011

У меня проблема с пружиной, и она выглядит следующим образом:

В SessionAttributes у меня есть объект person с атрибутом address, который является списком. Всякий раз, когда человек обновляется через контроллер, предыдущие записи все еще остаются. Так, например, если у меня были личные адреса: старый адрес 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 ]

2 голосов
/ 04 февраля 2011

Есть пара вопросов дизайна, которые я хотел бы решить.

  1. Вы используете один объект для привязки формы и данных домена. Это имеет тенденцию вызывать проблемы в точности как та, с которой вы столкнулись здесь. Проблема не в том, что форма не может «очистить» адреса объекта сеанса; проблема в том, что объект сеанса передает свою структуру данных в форму, что вызывает проблемы с привязкой.

  2. Форма имеет сведения о содержимом объекта Person. В частности, форма ожидает, что в списке person.getAddresses() будет три адреса. Подобно (1) выше, проблема заключается в утечке доменной структуры в слой вида.

Я рекомендую вам создать два разных класса "персона": один для представления данных домена (объект сеанса), а другой - для точного отражения структуры формы (объект привязки формы). Ваша форма будет содержать поля, которые отображаются непосредственно на свойства класса PersonForm, а в вашем TestController вы можете взять данные из PersonForm и соответствующим образом обновить сеанс Person. Тогда входные данные формы не должны быть рассчитаны на обработку различных состояний списка Person.addresses.

Этот подход требует немного больше кода, но не так уж и много, и экономия на сложности формы и разделении формы / домена того стоит.

0 голосов
/ 11 марта 2015

Недавно я столкнулся с той же проблемой и нашел очень простое решение. Так что вам нужно просто добавить скрытое поле для вашего списка. Сервис преобразования Spring установит пустой список из пустого строкового значения этого скрытого поля:)

Вы можете увидеть решение ниже:

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

И после этого введите другой код:

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

Примечания: Если вам нужно конвертировать String в Список ваших классов , и у вас нет собственных конверторов или редакторов для этого. Поэтому убедитесь, что по крайней мере ObjectToObjectConverter может это сделать, у YourClass должен быть конструктор с аргументом String или статический метод valueOf (String) .

0 голосов
/ 10 марта 2011

Еще одним возможным решением является использование перехватчика обработчика следующим образом.

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);
    }
}
0 голосов
/ 02 марта 2011

Я столкнулся с той же проблемой и решил ее, введя привязку инициализации в контроллер. Привязка init сбрасывает список адресов для атрибута сеанса. В зависимости от требований это может сделать более сложную работу.

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

Надеюсь, что это может помочь кому-то еще:)

0 голосов
/ 08 февраля 2011

Таким образом, решение выглядит следующим образом:

Добавив 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 "{}";
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...