2011-02-04 65 views
1

我面臨着和春天有個問題,它會如下:Spring MVC的:SessionAttributes和列表

在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> 
+0

我決不是專家,但默認情況下,類的範圍是單身。也許在你的情況下@Scope(「原型」)會更合適? – Bill 2011-02-04 17:51:04

+1

添加@Scope(「prototype」)不能解決問題。 – Eizenhooven 2011-02-04 18:01:54

+0

你爲什麼想讓這個人留在會議中。除非幾乎必要,否則在會話中存儲某些內容並不是一個好主意。 – Javi 2011-02-04 18:08:04

回答

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

有幾個設計問題我想解決的問題。

  1. 對於表單綁定和域數據都使用單個對象。這往往會導致問題,就像你在這裏遇到的問題。問題不在於表單未能「清除」會話對象的地址;問題在於會話對象將其數據結構泄露給表單,這會導致綁定問題。

  2. 該表單具有關於Person對象內容的知識。具體來說,表單預計在person.getAddresses()列表中有三個地址。像上面(1)一樣,問題是域結構泄漏到視圖層。

我建議創建兩個不同的「人」類:一個表示該域的數據(所述會話對象),以及一個準確鏡像形式的結構(形式結合對象)。您的表單將包含直接映射到PersonForm類中的屬性的字段,在您的TestController中,您可以從PersonForm中獲取數據並適當地更新會話Person。然後,表單輸入不需要被設計爲處理Person.addresses列表的不同狀態。

這種方法確實需要更多的代碼,但不是非常多,並且節省表單複雜性和表單/域解耦是非常值得的。

0

我面臨同樣的問題,並通過引入一個init粘結劑控制器解決它。 init binder重置會話屬性的地址列表。 根據需求,它可以做一些更復雜的工作。

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

希望可以幫助別人:)

-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); 
    } 
} 
0

最近,我面臨着同樣的問題,我已經找到了一個非常簡單的解決方案。 所以你只需要爲你的列表添加一個隱藏的字段。 春季轉換服務將設置空單從這個隱藏字段:)

的空字符串值,你可以看到下面的解決方案:

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

而在這之後把你的其他代碼:

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

注: 如果你需要轉換字符串YourClass'es列表,你沒有自己的轉換器或編輯器。 因此,確保至少ObjectToObjectConverter可以做到這一點,YourClass必須有一個字符串 ARG或靜態方法valueOf(String)的構造函數。