顯示具有 Spring MVC 標籤的文章。 顯示所有文章
顯示具有 Spring MVC 標籤的文章。 顯示所有文章

2014-02-10

@SessionAttributes 遇到多頁籤使用時,有很大的問題(Multiple Forms in One session)

Spring MVC 引入 Annotaion 時,將原本的 Controller 與 FormController 二合一,變成一隻 Controller 處理一個 Domain 的所有 Web request。

初期不習慣,久了也慢慢覺的好用,直到有一天,資料庫裡的資料暴走了,久思不得其解,最後 PM  點出,問題在使用者同時開啟多個頁籤編輯。

以前的 FormController 的作法是:
  • GET request進來,用 uid 到資料庫取 model 資料,然後再準備一些表單需要用到的資料,再將這些資料全部放到request裡。
  • 回到 JSP 後,從request裡取出資料顯示。
  • 使用者 submit 後,POST request進來,再用 uid 到資料庫取 model 資料,然後進行 data binding 與 validation,最後存到資料庫。
當初 FormController 也可以將 model 存在session中,但直覺怪怪的,所以並未使用過。

Annotation 以後的作法是:
  • GET request進來,用 uid 到資料庫取 model 資料,然後再準備一些表單需要用到的資料,再將這些資料全部放到request裡,另外將 model 資料放到session裡。
  • 回到 JSP 後,從request裡取出資料顯示。
  • 使用者 submit 後,POST request進來,直接到session裡取出 model 資料,然後進行 data binding 與 validation,最後存到資料庫。
問題就發生在POST reqeust進來後是到session取 model 資料,這在同時開啟多個頁籤時,必定爆炸。

2012-11-09

在 Spring MVC 3.1.2.RELEASE 與 Hibernate 4.1.7.Final 實作變動數量的值物件(Variable Component)

值物件?以前在 Hibernate 叫做 Component,現在 JPA 叫做 Embeddable,或者從 Annotation 的角度叫做 Element。

簡單講就是沒有 Primary Key(PK) 的資料,必須依附在有 PK 的資料下。

舉例來說,每個人(Person)有好幾種地址(Address),住家地址、公司地址、戶籍地與出生地等等,這些地址資料只對一個人有意義,脫離這個人就沒有存在的必要,或者說不能與其他人共用,既然不能共用,那就沒必要有 PK,與這個人共存亡。

2012-11-05

在 Spring MVC 3.1.2.RELEASE 產出 JasperReports 4.7.1 子報表(Subreport)

官方文件 17.7.4 Working with Sub-Reports 寫得不可思議的抽象,但運氣很好的,很快地在 Add example on how to configure JasperReports Sub-Reports with properties 找到解答。

Spring MVC 處理子報表並不像我之前的筆記 使用 Java 輸出資料到 JasperReports 子報表表身(Subreport Detail)那樣,由 Java 組出子報表再丟給主報表,而是在 Java 將子報表(Jasper)與子報表要用的 Data 一起丟給 Spring MVC 就可以了。

講得更精確,Data 是由 Java 丟的,而子報表 Jasper 則是透過設定的。

這邊一次就玩大一點,使用兩個子表,這樣可以引出一個有趣的問題。

Java 負責所有的 DataSource,包括主表和「所有」子表的。

在 Spring MVC 3.1.2.RELEASE 產出 JasperReports 4.7.1 報表

本篇筆記接續 Hello JasperReports 4.7.1 with Spring MVC 3.1.2。

前一篇筆記僅輸出標題,現在來輸出表身(Detail),官方文件為 17.7.3 Populating the ModelAndView。

表身資料可以使用兩種資料類型:JRDataSource  與 Collection,如果只有一組資料要傳給 Jasper,那就直接放在 Model 裡就可以了。

2012-11-02

輕輕鬆鬆使用 Spring MVC 3.1.2.RELEASE 產出 Excel 檔(AbstractExcelView)

第一次感覺到 Spring MVC View 機制好用!

使用 JstlView 輸出 JSP 要搭配使用 JSP 檔,使用 JasperReportsPdfView 輸出 JasperReports PDF 要搭配 japser 檔,那輸出 Excel 檔呢?當然沒有像 JSP 或 Jasper 這樣的範本檔,所以就直接用 View 輸出囉。

使用 Java 輸出 Excel 檔當然是用 Apache POI,所以得先加入 Dependency。
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi</artifactId>
  <version>3.8</version>
</dependency>
然後,在 Controller 直接實作抽象的 AbstractExcelView 就可以了。

2012-10-31

近看 Spring MVC 3.1.2.RELEASE 的 ContentNegotiatingViewResolver

誕生自 3.0 的 ContentNegotiatingViewResolver 從字義不太容易知道在做什麼或怎麼用,那就從 Source code 好好來研究一下。

每個 ViewResolver 的重點就是 resolveViewName()。
public View resolveViewName(String viewName, Locale locale) throws Exception {
  RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
  Assert.isInstanceOf(ServletRequestAttributes.class, attrs);
  List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
  if (requestedMediaTypes != null) {
    List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
    View bestView = getBestView(candidateViews, requestedMediaTypes);
    if (bestView != null) {
      return bestView;
    }
  }
  if (this.useNotAcceptableStatusCode) {
    // ...
    return NOT_ACCEPTABLE_VIEW;
  }
  else {
    logger.debug("No acceptable view found; returning null");
    return null;
  }
}
得到三個線索,分別為 getMediaTypes()、getCandidateViews() 與 getBestView()。

2012-10-29

Hello JasperReports 4.7.1 with Spring MVC 3.1.2

先從可以生出報表開始,只傳一個字串到 Jasper 檔裡。

做報表先。


兩個重點,我常忘記的事情。

第一個是報表使用的程式語言,預設是 Groovy,要改成 Java。

第二個是當沒有 Detail 資料時,不管是真的沒資料,或者是根本沒傳過來,都要將「When No Data」設成「All Sections, No Detail」,不然就會怎麼執行都生出空白報表來。


再來寫程式。

先加入 dependency。
<dependency>
  <groupId>net.sf.jasperreports</groupId>
  <artifactId>jasperreports</artifactId>
  <version>4.7.1</version>
</dependency>
再加入 ViewResolver,因為 InternalResourceViewResolver 只用來處理 JSP,所以得加入 ResourceBundleViewResolver 來處理 PDF 的需求,並使用較少的 order 值來確保 ResourceBundleViewResolver 在 InternalResourceViewResolver 之前執行。
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
  <property name="basename" value="views" />
  <property name="order" value="0" />
</bean>

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
  <property name="prefix" value="/WEB-INF/views/" />
  <property name="suffix" value=".jsp" />
  <property name="order" value="1" />
</bean>
再加入 views.properties 設定 jasper 檔的位置,聽說可以用未編譯的 jrxml 取代已編譯的 jasper 檔,Spring 會幫忙編譯。
helloReport.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
helloReport.url=/WEB-INF/reports/helloReport.jasper
最後就是 Controller,中文問題請看 用 Java 產出中文的 JasperReports PDF。
@RequestMapping("/helloReport")
public String helloReport(Model model) {
  model.addAttribute("title", "This is a 標題...");
  return "helloReport";
}
完成,可以用 http://.../helloReport 來產出 PDF 檔,或可以用 http://.../helloReport.pdf 來明示瀏覽器這是個 PDF 檔。


---
---
---

2012-10-14

SpringMVC form tag 的 modelAttribute 與 commandName 差異

在使用 spring-form.tld 的 form tag 時發現,有些地方用 commandName(官方文件),有些地方用 modelAttribue,功能好像都是用來指定 formBackingObject 是誰,但為何有兩種用法?

Open Source 的好處,翻 Source Code 去。

先看 spring-webmvc-3.1.2.RELEASE.jar\META-INF\spring-form.tld。

2012-10-12

為什麼要用 SpringMVC 的 SessionStatus

改用 SpringMVC 的 annotaion 之後第一次遇到的問題。

簡單的說,就是 @SessionAttributes("cmd") 加上 @InitBinder("cmd"),以及 binder.setValidator(new UserValidator()) 所造成的問題。

直接來看程式,這是一段還算標準的用法,用 get 進表單,用 post 儲存修改。

2012-08-21

建立 Google App Engine + Spring MVC + Tiles + Maven + Eclipse 專案

  • 建立專案
    • 使用 Eclipse -> New Web Application Project
      • 輸入 Project name 與 Package
      • 取消勾選 Use Google Web Toolkit
      • 確認勾選 Google App Engine

如何在 Google App Engine 使用 Spring MVC 3 上傳檔案

會有這問題原因在於 GAE 不許操作檔案,也就是說不能存取 GAE 的 Filesystem,若使用 Spring MVC 正常的作法:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- property name="maxUploadSize" value="100000" / -->
</bean>
<form:form modelAttribute="cmd" method="post" enctype="multipart/form-data">
<input type="file" name="file"/>
</form:form>
public String sync(@RequestParam(value = "file", required = false) MultipartFile uploadFile) throws IOException {
 // ...
}

2012-08-01

mvc:resources in Spring MVC 3.1.2

Request 從 client side 進到 Server Web App 之後,會先到 web.xml 報到,由 <servlet-mapping /> 決定下一步,以前都是用 *.do 之類的 <url-pattern />,所以為檔名不為 do 的 request 都不會進到 Spring 的地盤,直接在 web.xml 就轉到 jpg、js 或 css 了。

但是如果想讓 Spring 控管這類靜態資源,或者讓下載動態產生的 pdf 或者 csv 時可以使用正確的副檔名(pdf 或 csv),就得將這些 request 也轉到 Spring 控管。

2012-02-21

Spring MVC @PathVariable 最後一個點(dot)以後的字串(或說副檔名)被切掉了!

使用以下的 @PathVariable,當 key 值為 abc 或 123 時都沒有問題。
@RequestMapping(value = "/release/{key}", method = RequestMethod.GET)
public @ResponseBody
String release(@PathVariable String key) {
    log.debug("取得key值 {}", key);
    return release;
}

為什麼透過 @InitBinder 設定的 Spring Validator 會驗證 Model 裡所有的物件?

事情的起點當然是一個 Exception:
java.lang.IllegalStateException: Invalid target for Validator [idv.neil.model.ProductValidator@5ddffa]: idv.neil.web.Pager@11ba4a2
    at org.springframework.validation.DataBinder.setValidator(DataBinder.java:498)
    at idv.neil.web.ctrl.ProductController.formBackingObject(ProductController.java:100)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:104)
Spring MVC 3 以後可以使用 Annotation 將 MultiActionController 與 SimpleFormController 做在同一隻 controller:

Spring MVC 3.1.0 的兩種 Validation 方式

JSR-303 Validator 與 Spring Validator。

先來看我覺得不好用的 JSR-303 Validator。

在 Spring MVC 裡設定 JSR-303 Validator

第一、要有 JSR-303 實做(Provider),目前知道是 Hibernate Validator,Maven 定義如下:

2011-03-07

Spring Validation 3.0.5

  • Spring 3 支援三種驗證方式:
    • DataBinder
      • 每一個需要驗證的 model class 都應該要有一個相對應的 valitator。
      • 對於 reference class 也應該要用獨立的 validator。
      • ValidationUtils 提供一些基本的驗證 method。

Spring MVC 3.0.5 之三

  • 兩種使用 JSP & JSTL 的方式:
    • ResourceBundleViewResolver - 可以混合多種 view。
      <!-- the ResourceBundleViewResolver -->
      <bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
        <property name="basename" value="views"/>
      </bean>
      
      # And a sample properties file is uses (views.properties in WEB-INF/classes):
      welcome.(class)=org.springframework.web.servlet.view.JstlView
      welcome.url=/WEB-INF/jsp/welcome.jsp
      
      productList.(class)=org.springframework.web.servlet.view.JstlView
      productList.url=/WEB-INF/jsp/productlist.jsp
    • InternalResourceViewResolver
      <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
      </bean>

2011-03-03

Spring MVC 3.0.5 之二

  • Handler mappings
    • Spring 2.5 以後 DispatcherServlet 預設使用 DefaultAnnotaionHandlerMapping 去找 @Controller 裡的 @RequestMapping,所以不用在 xml 裡定義 handler mapping 了。
    • DefaultAnnotaionHandlerMapping 有些屬性可以設定:
      • interceptors
      • defaultHandler - 找不到符合的 handler 時,設定預設的 handler。
      • order - 設定多筆 handler mapping 時使用。
      • alwaysUseFullPath - 假設 servlet 過濾的 url pattern 為 /abc/*,一個 request 的 url 為 /abc/def.do,若設為 true,則用 /abc/def.do 去找 handler,若設為 false,則用 /def.do 去找,預設為 false。
      • urlDecode - 預設為 true。
      • lazyInitHandlers - 用來將 singleton handler lazy init,預設為 false。
        <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
            <property name="interceptors">
                <bean class="example.MyInterceptor"/>
            </property>
        </bean>

2011-03-02

Spring MVC 3.0.5 之一

  • Spring MVC 主要流程為 DispatcherServlet 讀取可設定的 hander mapping,將 request 傳給 handler(controller),然後再透過 view resolution 產生 response,並支援 locale 與 theme resolution 以及檔案上傳。
  • @Controller 與 @RequestMapping 組成預設的 handler。
  • Spring 3.0 以後,新增 @PathVariable等功能用來建立 RESTful 與一般 web app。
  • Spring MVC 特色:
    • 使用 POJO 作為 form backing object,不用與 Spring 有任何關聯。
    • 在 data binding 時,將型別錯誤視為驗證錯誤,而非較嚴重且難處理的系統錯誤。
    • 在 business object 的自動型別與 form field 的字串型別之間是無痛轉換的。
    • Controller 可以直接寫到 response,也可以透過 ModelAndView 來利用 view resolutaion,並使用 map 來存放 form object 或 reference data。
    • 透過 @RequestParam、@RequestHeader、 @PathVariable 等 annotation,讓 controller method 的 signature 不受任何限制。
  • Spring 的一個特色就是 Open for extension, close for modification,意思就是「擴充很容易,但不能修改」。