星期日, 12月 03, 2006

Eclipse eUML 備忘記

eUML2 2.1.0 提供免費的 Eclipse class diagram 模組化介面.
並能實時地與 java code 結合在一起.


開始備忘記:
[1] 安裝 jdk 5
[2] 安裝 Eclipse 及 相關 plugins
[3] 實例測試:


[1] 安裝 jdk 5:
下載 jdk-1_5_0_07-nb-5_0-win-ml.exe
http://java.sun.com/j2se/1.5.0/download-netbeans.html
安裝至 D:\jdk1.5.0_07
新增環境變數 JAVA_HOME=D:\jdk1.5.0_07
D:\jdk1.5.0_07\bin 加入至 PATH 中
D:\jdk1.5.0_07\lib\dt.jar 及 D:\jdk1.5.0_07\lib\tools.jar 加入至 CLASSPATH 中
執行 D:\>java -version
輸出 java version "1.5.0_07" 即安裝成功.


[2] 安裝 Eclipse 及 相關 plugins:
安裝 Eclipse:

下載 eclipse-SDK-3.2.1-win32.zip
http://www.eclipse.org/downloads/
http://ftp.jaist.ac.jp/pub/eclipse/eclipse/downloads/drops/R-3.2.1-200609210945/eclipse-SDK-3.2.1-win32.zip
解壓縮至 c:\eclipse_tptp


安裝 Eclipse Callisto plugins:
點擊 c:\eclipse_tptp\eclipse.exe 執行 Eclipse
選擇 Help -> Software Updates -> Find and Install -> Search for new features to install
按 Next 後 點選 Callisto Discovery Site 後 按 Finish
然後選擇最接近的下載點安裝, 然後 隨 C and C++ Developement 外, 全部安裝.
如下圖所示

這裡下載安裝使用超過20分鐘. 最好選擇較接近的下載點.


安裝 UML2 plugin:
選擇 Help -> Software Updates -> Find and Install -> Search for new features to install
按 Next 後 點選 New Remote Site
Name : UML2 Update site
URL : http://download.eclipse.org/tools/uml2/updates/site.xml
按 Finish
然後選擇最接近的下載點及安裝.


安裝 eUML plugin:
選擇 Help -> Software Updates -> Find and Install -> Search for new features to install
按 Next 後 點選 New Remote Site
Name : eUML Update site
URL : http://www.soyatec.com/update
按 Finish
然後選擇最接近的下載點及安裝.


[3] 實例測試:
Eclipse : File -> New -> Project -> Java Project -> Next
Project Name : UMLSample
Project layout 選擇 Create separate source and output folders
按 Finish


新增 Person.java 及 Student.java 兩個 class 作測試.


/************************ Person.java ********************/
package test.uml;


public class Person {
private String Name;


public String getName() {
return Name;
}


public void setName(String name) {
Name = name;
}
}
/************************ Person.java ********************/


/************************ Student.java ********************/
package test.uml;


public class Student extends Person {
private String studentNo;


public String getStudentNo() {
return studentNo;
}


public void setStudentNo(String studentNo) {
this.studentNo = studentNo;
}
}
/************************ Student.java ********************/


右鍵點選 Student.java -> eUML2 -> Class Inheritance explorer
出現如下圖所示:


參考資料:
http://www.soyatec.com/main.html

Eclipse Tomcat Jasper 備忘記

本備忘記主要是以 tomcat jasper 2 編譯 jsp 檔案為 servlet 的 java 源檔案.
就像 JBuilder 產生 Servlet 的功能, 有助於 jsp 開者 debug.


開始備忘記:
[1]
安裝 jdk 5
[2] 下載 Tomcat
[3] 安裝 Eclipse 及 相關 plugins
[4] 實例測試


[1] 安裝 jdk 5:
下載 jdk-1_5_0_07-nb-5_0-win-ml.exe
http://java.sun.com/j2se/1.5.0/download-netbeans.html
安裝至 D:\jdk1.5.0_07
新增環境變數 JAVA_HOME=D:\jdk1.5.0_07
D:\jdk1.5.0_07\bin 加入至 PATH 中
D:\jdk1.5.0_07\lib\dt.jar 及 D:\jdk1.5.0_07\lib\tools.jar 加入至 CLASSPATH 中
執行 D:\>java -version
輸出 java version "1.5.0_07" 即安裝成功.


[2] 下載 Tomcat:
下載 apache-tomcat-5.5.20.zip
http://www.eng.lsu.edu/mirrors/apache/tomcat/tomcat-5/v5.5.20/bin/apache-tomcat-5.5.20.zip
解壓縮至 C:\apache-tomcat-5.5.20


[3] 安裝 Eclipse 及 相關 plugins:
安裝 Eclipse:
下載 eclipse-SDK-3.2.1-win32.zip
http://www.eclipse.org/downloads/
http://ftp.jaist.ac.jp/pub/eclipse/eclipse/downloads/drops/R-3.2.1-200609210945/eclipse-SDK-3.2.1-win32.zip
解壓縮至 c:\eclipse_tptp


安裝 Eclipse Callisto plugins:
點擊 c:\eclipse_tptp\eclipse.exe 執行 Eclipse
選擇 Help -> Software Updates -> Find and Install -> Search for new features to install
按 Next 後 點選 Callisto Discovery Site 後 按 Finish
然後選擇最接近的下載點安裝, 然後 隨 C and C++ Developement 外, 全部安裝.
如下圖所示

eclipse_tptp_1.gif
這裡下載安裝使用超過20分鐘. 最好選擇較接近的下載點.


[4] 實例測試:
執行 Eclipse c:\eclipse_tptp\eclipse.exe
切換至 J2EE Perpective
Window -> Open Perpective -> Other ->> J2EE
按 OK


建立測試項目:
File -> New -> Project ->> Web -> Dynamic Web Project 按 Next
Project name : Jasper2Sample
然後按 Finish


右鍵點選 Jasper2Sample -> Properties -> Java Build Path -> Source
按 Add Folder 再按 Create New Folder
Folder name : output_servlet
然後按 Finish 及 OK 如下圖所示


切換至 Library [Tab]
按 Add External JARs
加入
C:\apache-tomcat-5.5.20\common\lib\servlet-api.jar
C:\apache-tomcat-5.5.20\common\lib\jsp-api.jar
C:\apache-tomcat-5.5.20\common\lib\jasper-runtime.jar
然後按 OK
如下圖所示


建立 Jasper Ant Build File:
右鍵點選 Jasper2Sample -> New -> File
File name : build_servlet.xml
然後按 Finish


內容為:

<!--------------------- build_servlet.xml -------------------->
<project name="Webapp Precompilation" default="jspc" basedir=".">
<property name="tomcat.home" value="C:\apache-tomcat-5.5.20" />
<property name="output.servlet" value="output_servlet" />
<property name="webapp.path" value="WebContent" />


<target name="jspc">
<taskdef classname="org.apache.jasper.JspC" name="jasper2">
<classpath id="jspc.classpath">
<pathelement location="${java.home}/../lib/tools.jar" />
<fileset dir="${tomcat.home}/bin">
<include name="*.jar" />
</fileset>
<fileset dir="${tomcat.home}/server/lib">
<include name="*.jar" />
</fileset>
<fileset dir="${tomcat.home}/common/lib">
<include name="*.jar" />
</fileset>
</classpath>
</taskdef>


<jasper2 validateXml="false" uriroot="${webapp.path}" outputDir="${output.servlet}" />


</target>


<target name="cleanup">
<delete>
<fileset dir="${output.servlet}" />
<fileset dir="build/classes/org/apache/jsp" />
</delete>
</target>


</project>
<!--------------------- build_servlet.xml -------------------->


上面
<property name="tomcat.home" value="C:\apache-tomcat-5.5.20" />
為 tomcat 主目錄位置


<property name="output.servlet" value="output_servlet" />
為 servlet 輸出目錄位置


<property name="webapp.path" value="WebContent" />
為 Web root 位置



建立測試 jsp 檔案:
右鍵點選 WebContent -> New -> File
File name : index.jsp
然後按 Finish


內容為:

<%------------------- index.jsp ----------------------%>
<%@ page language="java" contentType="text/html; charset=BIG5"
pageEncoding="BIG5"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=BIG5">
<title>Jasper2 Sample</title>
</head>
<body>
<%="Hello Joeyta" %>
</body>
</html>
<%------------------- index.jsp ----------------------%>



產生 Servlet 源檔案:
右鍵點選 build_servlet.xml -> Run As -> Ant Build
然後 Refresh Jasper2Sample Project
就會顯示 output_servlet -> org.apache.jsp.index_jsp.java


內容如下所示:


/********************** index_jsp.java **********************/
package org.apache.jsp;


import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;


public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {


private static java.util.List _jspx_dependants;


public Object getDependants() {
return _jspx_dependants;
}


public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {


JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;



try {
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html; charset=BIG5");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;


out.write("\n<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=BIG5\">\n<title>Jasper2 Sample</title>\n</head>\n<body>\n");
out.print("Hello Joeyta" );
out.write("\n</body>\n</html>");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
}
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
/********************** index_jsp.java **********************/


目錄結構如下所示:


參考文檔:
http://tomcat.apache.org/tomcat-5.5-doc/jasper-howto.html


星期六, 12月 02, 2006

Eclipse TPTP 備忘記

Eclipse Test & Performance Tools Platform (TPTP) 為 Eclipse 基金會的開源項目.
這個項目的主要目的為在 Eclipse IDE 平台集成 性能測試, 調節, 監察 等功能.
這次備忘記是學習如何增加讀取 xml 的執行性能.


開始備忘記:
[1] 安裝 jdk 5
[2] 安裝 Eclipse 及 Eclipse TPTP
[3] 性能調節的案例實踐


[1] 安裝 jdk 5:
下載 jdk-1_5_0_07-nb-5_0-win-ml.exe
http://java.sun.com/j2se/1.5.0/download-netbeans.html
安裝至 D:\jdk1.5.0_07
新增環境變數 JAVA_HOME=D:\jdk1.5.0_07
D:\jdk1.5.0_07\bin 加入至 PATH 中
D:\jdk1.5.0_07\lib\dt.jar 及 D:\jdk1.5.0_07\lib\tools.jar 加入至 CLASSPATH 中
執行 D:\>java -version
輸出 java version "1.5.0_07" 即安裝成功.


[2] 安裝 Eclipse 及 Eclipse TPTP:
安裝 Eclipse:

下載 eclipse-SDK-3.2.1-win32.zip
http://www.eclipse.org/downloads/
http://ftp.jaist.ac.jp/pub/eclipse/eclipse/downloads/drops/R-3.2.1-200609210945/eclipse-SDK-3.2.1-win32.zip
解壓縮至 c:\eclipse_tptp


安裝 Eclipse TPTP:
點擊 c:\eclipse_tptp\eclipse.exe 執行 Eclipse
選擇 Help -> Software Updates -> Find and Install -> Search for new features to install
按 Next 後 點選 Callisto Discovery Site 後 按 Finish
然後選擇最接近的下載點安裝, 然後 隨 C and C++ Developement 外, 全部安裝.
如下圖所示

這裡下載安裝使用超過20分鐘. 最好選擇較接近的下載點.


[3] 性能調節的案例實踐:
這裡主要是參考以下網站內容:
http://www.eclipse.org/articles/Article-TPTP-Profiling-Tool/tptpProfilingArticle.html
project 下載點為:
http://www.eclipse.org/articles/Article-TPTP-Profiling-Tool/productCatalogSample.zip
解壓縮至 C:\eclipse_tptp\workspace\productsample


/******************* Product.java **************************/
package com.sample.product;


import java.io.File;


import com.sample.product.util.ProductCatalog;


public class Product {


public static void main(String[] args) {

if(args.length == 0)
{
System.out.println("enter catalog location in this form x:\\path\\Product");
return;
}


Product catalog = new Product();
catalog.readCatalogFromFolder(args[0]);
}


public void readCatalogFromFolder(String name) {

if(name == null)
{
System.out.println("invalid folder name");
return;
}

File file = new File(name);
if(!file.exists() !file.isDirectory())
{
System.out.println("invalid folder name " + name);
return;
}

ProductCatalog info = new ProductCatalog();
info.readData(name);

System.out.println(info.getContent());

}


}
/******************* Product.java **************************/


/******************* ProductCatalog.java **************************/
package com.sample.product.util;


import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;


import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;


import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;


public class ProductCatalog extends DefaultHandler{


String content="";
public ProductCatalog() {
super();
}


protected void parseContent(File file)
{
try {
SAXParser parser = createParser();
InputStream is = new FileInputStream(file);


if (is == null) {
return;
}

parser.parse(is, this);

}
catch (Exception e) {

e.printStackTrace();

}

return;

}

protected SAXParser createParser() throws ParserConfigurationException, SAXException {
SAXParserFactory f = SAXParserFactory.newInstance();
f.setValidating(false);
return f.newSAXParser();
}


public void startElement(String namespaceURI, String localName, String elementName, Attributes atts) throws SAXException {

content+="\n"+elementName + ":\n";

for(int idx=0; idx<atts.getLength(); idx++)
{
content+="\t"+atts.getQName(idx) + "=" + atts.getValue(idx)+"\n";
}
}

public String getContent() {
return content;
}


public void readData(String name) {

if(name == null)
{
System.out.println("invalid folder name");
return;
}

File file = new File(name);
if(!file.exists() !file.isDirectory())
{
System.out.println("invalid folder name " + name);
return;
}

File[] files = file.listFiles();
for(int idx=0; idx<files.length; idx++)
parseContent(files[idx]);
}

}
/******************* ProductCatalog.java **************************/


下載測試檔案 xmlProductFiles.zip
http://www.eclipse.org/articles/Article-TPTP-Profiling-Tool/xmlProductFiles.zip
解壓縮至 c:\products


裡面包含 24 個 xml 檔案, 內容像下面這些:
<!---------------- apple.xml ------------------------>
<?xml version="1.0" encoding="UTF-8"?>
<content>
<product name="Apple" price="1.99" type="Golden" production="Canada"/>
</content>
<!---------------- apple.xml ------------------------>


<!---------------- corn.xml ------------------------>
<?xml version="1.0" encoding="UTF-8"?>
<content>
<product name="Corn" price="0.45" production="Chile">
</product>
</content>
<!---------------- corn.xml ------------------------>


<!---------------- potato.xml ------------------------>
<?xml version="1.0" encoding="UTF-8"?>
<content>
<product name="Potato" price="0.35" production="Ontario">
</product>
</content>
<!---------------- potato.xml ------------------------>


建立 java project 名為 productsample
File -> New -> Project -> Java Project -> Next
Project name 為 productsample
然後按 Finish
productsample project 裡只有 ProductCatalog.java 及 Product.java 兩個 class
如下圖所示:


然後右鍵點選 Product -> Profile As -> Java Application
選擇 Arguments [Tab]
program arguments: 輸入 c:\products
如下圖所示


切換至 Monitor [Tab]
選擇 Java Profiling -> Execution Time Analysis
如下圖所示


點選 Execution Time Analysis 後按 Edit Options
選擇 Collect boundary classes excluded by the filter set
Boundary class depth 輸入 3 [這是呼叫 method 的深度]
然後按 Finish
如下圖所示


然後切換至 Profiling and Logging [Perspective]
可以看到將 c:\products 裡的所有 xml 全部讀取進來.
出現如下圖所示


右鍵點擊 com.sample.product.Product -> Open With -> Execution Statistics
如下圖所示


上圖中 createParser() 的 Cumulative Time(Seconds) 為 0.268445
由於這個 method 執行時間不小, 懷疑這裡出現性能問題.
右鍵點選 createParser() -> Show Method Invocation Details
如下圖所示


上圖可看出 new SAXParser() 及 new Instance() 被呼叫 24 次.
即建立了 24 個 Instance.
估計可使用 singleton 模式將其減至只建立 1 個 Instance 以提高性能.


所以右鍵點擊 createParser() -> Open Source
如下圖所示


將 createParser() 裡建立的 Instance 改至 singleton 模式, 如下圖所示


再次 點擊 Product.java -> Profile As -> Application
下圖為改良後的結果


上圖中改良後的 createParser() 的 Cumulative Time(Seconds) 為 0.057422
這個 method 調節後足足提高了 5 倍執行時間的性能.


經過上面簡單的備忘記後, 有助於測試以下官方的教學:
http://www.eclipse.org/tptp/home/downloads/quicktour/v42/quick_tour.html


參考文檔:
http://www.eclipse.org/tptp/
http://www.eclipse.org/articles/Article-TPTP-Profiling-Tool/tptpProfilingArticle.html


星期六, 10月 07, 2006

spring lab 備忘記

最近很想參加 CISCO 的安全課程, 最後還是暫緩了, 打算過一陣子才參加.
現在的課程只餘下 Spring 及 CISSP 了,
Spring 卻在星期天的早上上課, 實在不習慣, 上堂的時候都在釣魚.
參與這個課程主要是想更了解 Spring 的一些設定及概念.
雖然本人亦看了不少 Spring 的書藉, 但卻很少深入實作.


這課程使用的書藉剛好是林信良的 Spring 技術手冊, 省了一筆錢.
這本書當初在台灣發行的時候就訂了一本回家. 亦簡略的看了一次.
我記得只有 Spring Web MVC 好像沒甚麼看, 因為對它沒興趣.


如果要深入了解 Spring,
書藉推薦 Wrox Professional Java Development with the Spring Framework
以及參看官方文檔 http://www.springframework.org/documentation
如果想加速入門, 就推薦林信良的 Spring 技術手冊.


這次備忘記主要是記錄 Spring course lab1 的過程.


lab1 requirement 的下載位址是:
http://blog.matrix.org.cn/resources/joeyta/spring_lab_01.zip

源程式下載位址是:
http://blog.matrix.org.cn/resources/joeyta/cpttm_spring_lab.zip


要求是使用 Spring 建立一個 File Synchronizer 的項目.


開始備忘記:
[1]
安裝 jdk 5
[2] 安裝 Eclipse WTP
[3] 下載 Spring 及裝 Spring IDE
[4] 使用 Eclipse WTP 建立第一個 Spring Project
[5] 建立 Unit Test


[1] 安裝 jdk 5:
下載 jdk-1_5_0_07-nb-5_0-win-ml.exe
http://java.sun.com/j2se/1.5.0/download-netbeans.html
安裝至 D:\jdk1.5.0_07
新增環境變數 JAVA_HOME=D:\jdk1.5.0_07
D:\jdk1.5.0_07\bin 加入至 PATH 中
D:\jdk1.5.0_07\lib\dt.jar 及 D:\jdk1.5.0_07\lib\tools.jar 加入至 CLASSPATH 中
執行 D:\>java -version
輸出 java version "1.5.0_07" 即安裝成功.


[2] 安裝 Eclipse WTP:
下載 wtp-all-in-one-sdk-R-1.5.0-200606281455-win32.zip
http://www.eclipse.org/webtools/
http://www.eclipse.org/downloads/download.php?file=/webtools/downloads/drops/R1.5/R-1.5.0-200606281455/wtp-all-in-one-sdk-R-1.5.0-200606281455-win32.zip&url=ftp://ftp.jaist.ac.jp/pub/eclipse/webtools/downloads/drops/R1.5/R-1.5.0-200606281455/wtp-all-in-one-sdk-R-1.5.0-200606281455-win32.zip&mirror_id=105
解壓縮至 D:\cpttm\eclipse


[3] 下載 Spring 及裝 Spring IDE:
下載 spring-framework-1.2.8-with-dependencies.zip
http://www.springframework.org/download
http://superb-east.dl.sourceforge.net/sourceforge/springframework/spring-framework-1.2.8-with-dependencies.zip
解壓縮至 D:\CPTTM\spring-framework-1.2.8


安裝 Spring IDE:
Eclipse:Help -> Software Updates -> Find and Install -> Search for new features to install
按 New Remote Site
Name: Spring IDE
URL: http://springide.org/updatesite/
選擇最新的版本然後安裝.


建立測試目錄:
D:\cpttm\dir1
D:\cpttm\dir2
D:\cpttm\dir3
D:\cpttm\dir4


[4] 使用 Eclipse WTP 建立第一個 Spring Project
Eclipse: File -> New -> Project ->> Java Project
Project Name: cpttm_spring_lab
Project layout: 選擇 Create separate source and output folders
按 Finish 完成


右鍵點擊 spring_lab -> properties ->> Java Build Path -> Libraries
按 Add External JARs 選擇 D:\cpttm\spring-framework-1.2.8\dist\spring.jar
按 Add External JARs 選擇 D:\cpttm\spring-framework-1.2.8\lib\jakarta-commons\commons-logging.jar
按 Add External JARs 選擇 D:\cpttm\spring-framework-1.2.8\lib\junit\junit.jar
按 OK 完成


右鍵點擊 spring_lab -> Add Spring Project Nature


/******************* FileLister.java *********************/
package mo.org.cpttm.spring.lab1;


import java.util.List;


public interface FileLister {
public List listFiles(String pathname);
}
/******************* FileLister.java *********************/


/******************* LocalFileLister.java *********************/
package mo.org.cpttm.spring.lab1;


import java.io.File;
import java.util.ArrayList;
import java.util.List;


public class LocalFileLister implements FileLister {


public List listFiles(String pathname) {
List filenames = new ArrayList();
File dir = new File(pathname);
File[] files = dir.listFiles();
for(int i=0; i<files.length; i++){
if(files[i].isFile()){
filenames.add(files[i].getName());
}
}
return filenames;
}

}
/******************* LocalFileLister.java *********************/


/******************* FileCopier.java *********************/
package mo.org.cpttm.spring.lab1;


import java.io.IOException;


public interface FileCopier {
public void copyFile(String srcDir, String destDir, String filename) throws IOException;
}
/******************* FileCopier.java *********************/


/******************* LocalFileCopier.java *********************/
package mo.org.cpttm.spring.lab1;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;


public class LocalFileCopier implements FileCopier {


public void copyFile(String srcDir, String destDir, String filename)
throws IOException {
File srcFile = new File(srcDir, filename);
File destFile = new File(destDir, filename);
FileChannel srcChannel = new FileInputStream(srcFile).getChannel();
FileChannel destChannel = new FileOutputStream(destFile).getChannel();
srcChannel.transferTo(0, srcChannel.size(), destChannel);
srcChannel.close();
destChannel.close();
}


}
/******************* LocalFileCopier.java *********************/


/******************* FileSynchronizer.java *********************/
package mo.org.cpttm.spring.lab1;


import java.io.IOException;
import java.util.Iterator;
import java.util.List;


public class FileSynchronizer {
String srcDir;


String destDir;


FileLister fileLister;


FileCopier fileCopier;


public void setDestDir(String destDir) {
this.destDir = destDir;
}


public void setFileCopier(FileCopier fileCopier) {
this.fileCopier = fileCopier;
}


public void setFileLister(FileLister fileLister) {
this.fileLister = fileLister;
}


public void setSrcDir(String srcDir) {
this.srcDir = srcDir;
}


public void synchronize() throws IOException {
List srcList = fileLister.listFiles(srcDir);
List destList = fileLister.listFiles(destDir);
for (Iterator iter = srcList.iterator(); iter.hasNext();) {
String element = (String) iter.next();
if (!destList.contains(element)) {
fileCopier.copyFile(srcDir, destDir, element);
}
}


}
}
/******************* FileSynchronizer.java *********************/


/******************* FileSynchronizerDemo.java *********************/
package mo.org.cpttm.spring.lab1;


import java.io.IOException;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;


public class FileSynchronizerDemo {
FileSynchronizer fs;
public FileSynchronizerDemo(){
ApplicationContext context = new FileSystemXmlApplicationContext("beans-config.xml");
fs = (FileSynchronizer)context.getBean("fileSynchronizer");
}

public void synUniDir(String srcDir, String destDir) throws IOException{
fs.setSrcDir(srcDir);
fs.setDestDir(destDir);
fs.synchronize();
}

public void synBiDir(String srcDir, String destDir) throws IOException{
synUniDir(srcDir, destDir);
synUniDir(destDir, srcDir);
}

public static void main(String[] args) throws IOException{
FileSynchronizerDemo fsDemo = new FileSynchronizerDemo();
fsDemo.synUniDir("/cpttm/dir1", "/cpttm/dir2");
fsDemo.synBiDir("/cpttm/dir3", "/cpttm/dir4");
}
}
/******************* FileSynchronizerDemo.java *********************/


######################## log4j.properties ########################
# Configure logging for testing: optionally with log file
log4j.rootLogger=INFO, stdout
# log4j.rootLogger=WARN, stdout, logfile


log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n


log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
######################## log4j.properties ########################


<!---------------------------- beans-config.xml -------------------------->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "
http://www.springframework.org/dtd/spring-beans.dtd">


<beans>
<bean id="fileLister"
class="mo.org.cpttm.spring.lab1.LocalFileLister">
</bean>
<bean id="fileCopier"
class="mo.org.cpttm.spring.lab1.LocalFileCopier">
</bean>
<bean id="fileSynchronizer"
class="mo.org.cpttm.spring.lab1.FileSynchronizer">
<property name="fileLister">
<ref bean="fileLister"/>
</property>
<property name="fileCopier">
<ref bean="fileCopier"/>
</property>
</bean>

</beans>
<!---------------------------- beans-config.xml -------------------------->


[5] 建立 Unit Test:
/******************* FileSynchronizerTest.java *********************/
package mo.org.cpttm.spring.lab1;


import java.io.File;
import java.io.IOException;
import java.util.Random;


import junit.framework.TestCase;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;


public class FileSynchronizerTest extends TestCase {
FileSynchronizer fs;
String dir1 = "/cpttm/dir1/";
String dir2 = "/cpttm/dir2/";
String textFile;
File file;


protected void setUp() throws Exception {
super.setUp();
ApplicationContext context = new FileSystemXmlApplicationContext("beans-config.xml");
fs = (FileSynchronizer)context.getBean("fileSynchronizer");

textFile = "file_" + new Random().nextInt(999) + ".txt";
file = new File(dir1+textFile);
if(!file.exists()){
file.createNewFile();
}
}

public void testSynchronize() throws IOException{
fs.setSrcDir(dir1);
fs.setDestDir(dir2);
fs.synchronize();
assertTrue(new File(dir2 + textFile).exists());
}


}
/******************* FileSynchronizerTest.java *********************/


右鍵點選 FileSynchronizerTest -> Run As -> Junit Test
輸出如下圖所示


項目結構如下圖所示





這次備忘記主要是記錄 Spring course lab2 的過程.


lab2 requirement 的下載位址是:
http://blog.matrix.org.cn/resources/joeyta/spring_lab_02.zip

源程式下載位址是:
http://blog.matrix.org.cn/resources/joeyta/cpttm_spring_lab.zip


要求是使用 Spring 增強 lab1 的 File Synchronizer 的功能 .

由於 lab 中需要使用多國的 properties 檔案.
為方便起見, 故需要安裝 Eclipse property editor

開始備忘記:
[1] 安裝 Eclipse Propertie File Editor
[2] 開始實作 lab 2

[1] 安裝 Eclipse Propertie File Editor:
下載 epfe-1.0.5.4.zip:
http://sourceforge.net/project/showfiles.php?group_id=117908

解壓後將 org.sourceforge.eclpropfileedit 目錄複製至 D:\cpttm\eclipse\plugins
重新啟動 Eclipse 就完成安裝. 如下圖所示



[2] 開始實作 lab 2:
############# messages_en_US.properties ############
copy_status=Coping file {0} from {1} to {2} duration {3}
############# messages_en_US.properties ############

############# messages_zh_TW.properties ############
copy_status=\u8907\u88FD\u6A94\u6848 {0} \u7531 {1} \u81F3 {2} \u9700\u6642 {3}
############# messages_zh_TW.properties ############

<!-------------------- beans-config-lab2.xml ---------------------->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "
http://www.springframework.org/dtd/spring-beans.dtd">


<beans>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>mo/org/cpttm/spring/lab2/messages</value>
</property>
</bean>

<bean id="fileCopyMonitorListener" class="mo.org.cpttm.spring.lab2.FileCopyMonitorListener" />


<bean id="directoryCorrector" class="mo.org.cpttm.spring.lab2.DirectoryCorrector" />


<bean id="fileLister"
class="mo.org.cpttm.spring.lab2.LocalFileLister">
</bean>
<bean id="fileCopier"
class="mo.org.cpttm.spring.lab2.LocalFileCopier">
</bean>

<bean id="uniFileSynchronizer"
class="mo.org.cpttm.spring.lab2.FileSynchronizer" autowire="byName" dependency-check="all">
<property name="srcDir">
<value>/aa/dir1/</value>
</property>
<property name="destDir">
<value>/aa/dir2/</value>
</property>
<property name="biDirectorySyn">
<value>false</value>
</property>
<property name="allowExtensionSet">
<set>
<value>.java</value>
<value>.txt</value>
<value>.exe</value>
</set>
</property>
</bean>

<bean id="biFileSynchronizer"
class="mo.org.cpttm.spring.lab2.FileSynchronizer" autowire="byName" dependency-check="all">
<property name="srcDir">
<value>/aa/dir3/</value>
</property>
<property name="destDir">
<value>/aa/dir4/</value>
</property>
<property name="biDirectorySyn">
<value>true</value>
</property>
<property name="allowExtensionSet">
<set>
<value>.java</value>
<value>.txt</value>
<value>.exe</value>
</set>
</property>
</bean>

</beans>
<!-------------------- beans-config-lab2.xml ---------------------->

/****************** FileLister.java ********************/
package mo.org.cpttm.spring.lab2;


import java.util.List;


public interface FileLister {
public List listFiles(String pathname);
}
/****************** FileLister.java ********************/

/****************** LocalFileLister.java ********************/
package mo.org.cpttm.spring.lab2;


import java.io.File;
import java.util.ArrayList;
import java.util.List;


public class LocalFileLister implements FileLister {


public List listFiles(String pathname) {
List filenames = new ArrayList();
File dir = new File(pathname);
File[] files = dir.listFiles();
for(int i=0; i<files.length; i++){
if(files[i].isFile()){
filenames.add(files[i].getName());
}
}
return filenames;
}

}
/****************** LocalFileLister.java ********************/

/****************** FileCopier.java ********************/
package mo.org.cpttm.spring.lab2;


import java.io.IOException;


public interface FileCopier {
public void copyFile(String srcDir, String destDir, String filename) throws IOException;
}
/****************** FileCopier.java ********************/

/****************** LocalFileCopier.java ********************/
package mo.org.cpttm.spring.lab2;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.Calendar;


import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;


public class LocalFileCopier implements FileCopier, ApplicationContextAware {
private ApplicationContext context;
public void copyFile(String srcDir, String destDir, String filename)
throws IOException {
long startTime = Calendar.getInstance().getTimeInMillis();
File srcFile = new File(srcDir, filename);
File destFile = new File(destDir, filename);
FileChannel srcChannel = new FileInputStream(srcFile).getChannel();
FileChannel destChannel = new FileOutputStream(destFile).getChannel();
srcChannel.transferTo(0, srcChannel.size(), destChannel);
srcChannel.close();
destChannel.close();
long stopTime = Calendar.getInstance().getTimeInMillis();

FileCopiedEvent fileCopiedEvent = new FileCopiedEvent(context);
fileCopiedEvent.setStartTime(startTime);
fileCopiedEvent.setSrcDir(srcDir);
fileCopiedEvent.setDestDir(destDir);
fileCopiedEvent.setFilename(filename);
fileCopiedEvent.setStopTime(stopTime);
context.publishEvent(fileCopiedEvent);
}


public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}


}
/****************** LocalFileCopier.java ********************/


/****************** FileCopiedEvent.java ********************/
package mo.org.cpttm.spring.lab2;


import org.springframework.context.ApplicationEvent;


public class FileCopiedEvent extends ApplicationEvent {
private static final long serialVersionUID = -6891048310751026219L;
private String srcDir;
private String destDir;
private String filename;
private long startTime;
private long stopTime;

public String getDestDir() {
return destDir;
}


public void setDestDir(String destDir) {
this.destDir = destDir;
}


public String getFilename() {
return filename;
}


public void setFilename(String filename) {
this.filename = filename;
}


public String getSrcDir() {
return srcDir;
}


public void setSrcDir(String srcDir) {
this.srcDir = srcDir;
}


public long getStartTime() {
return startTime;
}


public void setStartTime(long startTime) {
this.startTime = startTime;
}


public long getStopTime() {
return stopTime;
}


public void setStopTime(long stopTime) {
this.stopTime = stopTime;
}


public FileCopiedEvent(Object source) {
super(source);
}
}
/****************** FileCopiedEvent.java ********************/

/****************** FileCopyMonitorListener.java ********************/
package mo.org.cpttm.spring.lab2;


import java.util.Locale;


import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;


public class FileCopyMonitorListener implements ApplicationListener {


public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof FileCopiedEvent) {
FileCopiedEvent fileCopiedEvent = (FileCopiedEvent) event;
ApplicationContext context = (ApplicationContext) fileCopiedEvent
.getSource();
Object[] arguments = new Object[] {
fileCopiedEvent.getFilename(),
fileCopiedEvent.getSrcDir(),
fileCopiedEvent.getDestDir(),
new Long(fileCopiedEvent.getStopTime()
- fileCopiedEvent.getStartTime()) };
System.out.println(context.getMessage("copy_status", arguments,
Locale.US));
System.out.println(context.getMessage("copy_status", arguments,
Locale.TAIWAN));
}


}


}
/****************** FileCopyMonitorListener.java ********************/

/****************** DirectoryCorrector.java ********************/
package mo.org.cpttm.spring.lab2;


import java.io.File;
import java.lang.reflect.Field;


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;


public class DirectoryCorrector implements BeanPostProcessor {


public Object postProcessBeforeInitialization(Object bean, String name)
throws BeansException {
return bean;
}


public Object postProcessAfterInitialization(Object bean, String name)
throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
if (fields[i].getType().equals(String.class) && fields[i].getName().endsWith("Dir")) {
if (!fields[i].isAccessible()) {
fields[i].setAccessible(true);
}
try {
String directoryName = (String) fields[i].get(bean);
File directory = new File(directoryName);
if(!directory.isDirectory()){
directory.mkdirs();
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}


}
}
return bean;
}


}
/****************** DirectoryCorrector.java ********************/

/****************** FileSynchronizer.java ********************/
package mo.org.cpttm.spring.lab2;


import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Set;


public class FileSynchronizer {
String srcDir;


String destDir;


FileLister fileLister;


FileCopier fileCopier;

boolean biDirectorySyn;

Set allowExtensionSet;


public void setDestDir(String destDir) {
this.destDir = destDir;
}


public void setFileCopier(FileCopier fileCopier) {
this.fileCopier = fileCopier;
}


public void setFileLister(FileLister fileLister) {
this.fileLister = fileLister;
}


public void setSrcDir(String srcDir) {
this.srcDir = srcDir;
}


public boolean checkExtension(String filename){
for (Iterator iter = allowExtensionSet.iterator(); iter.hasNext();) {
String extension = (String) iter.next();
if(filename.endsWith(extension)){
return true;
}
}
return false;
}

public void synchronize(String srcDir, String destDir) throws IOException {
List srcList = fileLister.listFiles(srcDir);
List destList = fileLister.listFiles(destDir);
for (Iterator iter = srcList.iterator(); iter.hasNext();) {
String srcFile = (String) iter.next();
if (!destList.contains(srcFile) && checkExtension(srcFile)) {
fileCopier.copyFile(srcDir, destDir, srcFile);
}
}


}

public void synchronize() throws IOException {
synchronize(srcDir, destDir);
if(biDirectorySyn){
synchronize(destDir, srcDir);
}
}

public void setAllowExtensionSet(Set allowExtensionSet) {
this.allowExtensionSet = allowExtensionSet;
}


public void setBiDirectorySyn(boolean biDirectorySyn) {
this.biDirectorySyn = biDirectorySyn;
}
}
/****************** FileSynchronizer.java ********************/

/****************** FileSynchronizerDemo.java ********************/
package mo.org.cpttm.spring.lab2;


import java.io.IOException;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class FileSynchronizerDemo {
public static void main(String[] args) throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext(
"mo/org/cpttm/spring/lab2/beans-config-lab2.xml");
FileSynchronizer fs = (FileSynchronizer) context
.getBean("uniFileSynchronizer");
fs.synchronize();


fs = (FileSynchronizer) context.getBean("biFileSynchronizer");
fs.synchronize();
}
}
/****************** FileSynchronizerDemo.java ********************/

/****************** FileSynchronizerTest.java ********************/
package mo.org.cpttm.spring.lab2;


import java.io.File;
import java.io.IOException;
import java.util.Random;


import junit.framework.TestCase;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class FileSynchronizerTest extends TestCase {
FileSynchronizer fs;
String dir1 = "/aa/dir1/";
String dir2 = "/aa/dir2/";
String srcFile;
File file;


protected void setUp() throws Exception {
super.setUp();
ApplicationContext context = new ClassPathXmlApplicationContext("mo/org/cpttm/spring/lab2/beans-config-lab2.xml");
fs = (FileSynchronizer)context.getBean("uniFileSynchronizer");
}

public void createFile(String extension) throws IOException{
srcFile = "file_" + new Random().nextInt(999) + extension;
file = new File(dir1+srcFile);
if(!file.exists()){
file.createNewFile();
}
}

public void testSynchronizeAllowExtensionFile() throws IOException{
fs.setSrcDir(dir1);
fs.setDestDir(dir2);


createFile(".java");
fs.synchronize();
assertTrue(new File(dir2 + srcFile).exists());

createFile(".txt");
fs.synchronize();
assertTrue(new File(dir2 + srcFile).exists());


createFile(".exe");
fs.synchronize();
assertTrue(new File(dir2 + srcFile).exists());
}

public void testSynchronizeDisallowExtensionFile() throws IOException{
fs.setSrcDir(dir1);
fs.setDestDir(dir2);

createFile(".pfx");
fs.synchronize();
assertFalse(new File(dir2 + srcFile).exists());
}


}
/****************** FileSynchronizerTest.java ********************/


執行結果如下圖所示:










這次備忘記主要是記錄 Spring course lab3 的過程.


lab3 requirement 的下載位址是:
http://blog.matrix.org.cn/resources/joeyta/spring_lab_03.zip

源程式下載位址是:
http://blog.matrix.org.cn/resources/joeyta/cpttm_spring_lab.zip


要求使用 Spring AOP 裡的4種 Advice 實作銀行存提轉款功能.
Spring AOP 四種 Advice 分別為:
MethodBeforeAdvice:method 呼叫前執行
AfterReturningAdvice:method 呼叫後執行
ThrowsAdvice:指定的 Throwable 出現時執行
MethodInterceptor:method 呼叫前後執行

開始備忘記:
[1] 開始實作 lab 3:
<!--------------------- beans-config-lab3.xml ----------------------->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "
http://www.springframework.org/dtd/spring-beans.dtd">


<beans>
<bean id="bankLogBeforeAdvice" class="mo.org.cpttm.spring.lab3.BankLogBeforeAdvice" />
<bean id="bankLogAfterAdvice" class="mo.org.cpttm.spring.lab3.BankLogAfterAdvice" />
<bean id="bankThrowsAdvice" class="mo.org.cpttm.spring.lab3.BankThrowsAdvice" />
<bean id="bankMethodInterceptor" class="mo.org.cpttm.spring.lab3.BankMethodInterceptor" />

<bean id="bank" class="mo.org.cpttm.spring.lab3.Bank" />


<bean id="bankProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>mo.org.cpttm.spring.lab3.IBank</value>
</property>
<property name="target">
<ref bean="bank"/>
</property>
<property name="interceptorNames">
<list>
<value>bankLogBeforeAdvice</value>
<value>bankLogAfterAdvice</value>
<value>bankThrowsAdvice</value>
<value>bankMethodInterceptor</value>
</list>
</property>
</bean>

</beans>
<!--------------------- beans-config-lab3.xml ----------------------->

/****************** AccountNotFoundException.java ********************/
package mo.org.cpttm.spring.lab3;


@SuppressWarnings("serial")
public class AccountNotFoundException extends Exception {


public AccountNotFoundException() {
super();
}


public AccountNotFoundException(String message) {
super(message);
}

}
/****************** AccountNotFoundException.java ********************/

/****************** BalanceException.java ********************/
package mo.org.cpttm.spring.lab3;


@SuppressWarnings("serial")
public class BalanceException extends Exception {


public BalanceException() {
super();
}


public BalanceException(String message) {
super(message);
}


}
/****************** BalanceException.java ********************/

/****************** NotAtOfficeHoursException.java ********************/
package mo.org.cpttm.spring.lab3;


@SuppressWarnings("serial")
public class NotAtOfficeHoursException extends Exception {


public NotAtOfficeHoursException() {
super();
}


public NotAtOfficeHoursException(String message) {
super(message);
}


}
/****************** NotAtOfficeHoursException.java ********************/

/****************** IBank.java ********************/
package mo.org.cpttm.spring.lab3;


public interface IBank {
public void deposit(String accountNo, int amount) throws AccountNotFoundException;
public void withdraw(String accountNo, int amount) throws AccountNotFoundException, BalanceException;
public void transfer(String fromAccountNo, String toAccountNo, int amount) throws AccountNotFoundException, BalanceException;
public int getBalance(String accountNo) throws AccountNotFoundException;
}
/****************** IBank.java ********************/


/****************** Bank.java ********************/
package mo.org.cpttm.spring.lab3;


import java.util.TreeMap;


public class Bank implements IBank {


TreeMap<String, Integer> account;
public Bank(){
account = new TreeMap<String, Integer>();
account.put("a100", new Integer(0));
account.put("a101", new Integer(100));
account.put("a102", new Integer(200));
}


public boolean checkAccount(String accountNo) throws AccountNotFoundException{
if(account.containsKey(accountNo)){
return true;
} else {
throw new AccountNotFoundException();
}
}

public void deposit(String accountNo, int amount)
throws AccountNotFoundException {
if(checkAccount(accountNo)){
int balance = account.get(accountNo).intValue();
balance += amount;
account.put(accountNo, new Integer(balance));
}
}


public int getBalance(String accountNo) throws AccountNotFoundException {
if(account.containsKey(accountNo)){
return account.get(accountNo).intValue();
} else {
throw new AccountNotFoundException();
}
}


public void transfer(String fromAccountNo, String toAccountNo, int amount)
throws AccountNotFoundException, BalanceException {
if(checkAccount(fromAccountNo) && checkAccount(toAccountNo)){
int fromBalance = account.get(fromAccountNo).intValue();
if(fromBalance >= amount){
fromBalance -= amount;
account.put(fromAccountNo, new Integer(fromBalance));

int toBalance = account.get(toAccountNo).intValue();
toBalance += amount;
account.put(toAccountNo, new Integer(toBalance));
} else {
throw new BalanceException();
}
}
}


public void withdraw(String accountNo, int amount)
throws AccountNotFoundException, BalanceException {
if(checkAccount(accountNo)){
int balance = account.get(accountNo).intValue();
if(balance >= amount){
balance -= amount;
account.put(accountNo, new Integer(balance));
} else {
throw new BalanceException();
}
}
}


}
/****************** Bank.java ********************/

/****************** BankLogBeforeAdvice.java ********************/
package mo.org.cpttm.spring.lab3;


import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;


import org.springframework.aop.MethodBeforeAdvice;


public class BankLogBeforeAdvice implements MethodBeforeAdvice {
private Logger logger = Logger.getLogger(this.getClass().getName());


public void before(Method method, Object[] args, Object target)
throws Throwable {
logger.log(Level.INFO, "Before call by : " + method.getName());
for (Object object : args) {
logger.log(Level.INFO, "Arguments : " + object.toString());
}
}
}
/****************** BankLogBeforeAdvice.java ********************/

/****************** BankLogAfterAdvice.java ********************/
package mo.org.cpttm.spring.lab3;


import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;


import org.springframework.aop.AfterReturningAdvice;


public class BankLogAfterAdvice implements AfterReturningAdvice {
private Logger logger = Logger.getLogger(this.getClass().getName());


public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
logger.log(Level.INFO, "After call by : " + method.getName());
for (Object object : args) {
logger.log(Level.INFO, "Arguments : " + object.toString());
}


if (returnValue != null) {
logger.log(Level.INFO, "Return Value : " + returnValue.toString());
}
}


}
/****************** BankLogAfterAdvice.java ********************/

/****************** BankThrowsAdvice.java ********************/
package mo.org.cpttm.spring.lab3;


import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;


import org.springframework.aop.ThrowsAdvice;


public class BankThrowsAdvice implements ThrowsAdvice {
private Logger logger = Logger.getLogger(this.getClass().getName());


public void afterThrowing(Method method, Object[] args, Object target,
AccountNotFoundException anfe) {
logger.log(Level.INFO,
"AccountNotFoundException found in ThrowsAdvice, method : "
+ method.getName());
for (Object object : args) {
if (object instanceof String) {
logger.log(Level.INFO,
"AccountNotFoundException found in ThrowsAdvice, acount : "
+ object.toString() + " not found!");
}
}
}


public void afterThrowing(Method method, Object[] args, Object target,
BalanceException anfe) {
for (Object object : args) {
if (object instanceof String) {
logger.log(Level.INFO,
"BalanceException found in ThrowsAdvice, acount : "
+ object.toString() + " not found!");
}
if (object instanceof Integer) {
logger.log(Level.INFO,
"BalanceException found in ThrowsAdvice, transaction amount : "
+ object.toString());
}
}
}
}
/****************** BankThrowsAdvice.java ********************/

/****************** BankMethodInterceptor.java ********************/
package mo.org.cpttm.spring.lab3;


import java.util.Calendar;
import java.util.GregorianCalendar;


import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;


public class BankMethodInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
Calendar calendar = new GregorianCalendar();
if (calendar.get(Calendar.DAY_OF_WEEK) < 2
calendar.get(Calendar.DAY_OF_WEEK) > 6
calendar.get(Calendar.HOUR_OF_DAY) < 9
calendar.get(Calendar.HOUR_OF_DAY) >= 18) {
throw new NotAtOfficeHoursException(
"Sorry, the transaction over the office hours!");
}


Object result = methodInvocation.proceed();


return result;
}
}
/****************** BankMethodInterceptor.java ********************/

/****************** BankDemo.java ********************/
package mo.org.cpttm.spring.lab3;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class BankDemo {
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext(
"mo/org/cpttm/spring/lab3/beans-config-lab3.xml");
IBank bank = (IBank) context.getBean("bankProxy");

System.out.println("Balance of a100 " + bank.getBalance("a100"));
bank.deposit("a100", 3000);
System.out.println("Balance of a100 " + bank.getBalance("a100"));
bank.withdraw("a100", 1000);
System.out.println("Balance of a100 " + bank.getBalance("a100"));
bank.transfer("a100", "a101", 1000);
System.out.println("Balance of a100 " + bank.getBalance("a100"));

try{
bank.deposit("a999", 200);
} catch (AccountNotFoundException e){
}

try{
bank.withdraw("a102", 2000);
} catch (BalanceException e){
}


}
}
/****************** BankDemo.java ********************/

執行結果如下所示:
資訊: Before call by : getBalance
資訊: Arguments : a100
資訊: After call by : getBalance
資訊: Arguments : a100
資訊: Return Value : 0
Balance of a100 0
資訊: Before call by : deposit
資訊: Arguments : a100
資訊: Arguments : 3000
資訊: After call by : deposit
資訊: Arguments : a100
資訊: Arguments : 3000
資訊: Before call by : getBalance
資訊: Arguments : a100
資訊: After call by : getBalance
資訊: Arguments : a100
資訊: Return Value : 3000
Balance of a100 3000
資訊: Before call by : withdraw
資訊: Arguments : a100
資訊: Arguments : 1000
資訊: After call by : withdraw
資訊: Arguments : a100
資訊: Arguments : 1000
資訊: Before call by : getBalance
資訊: Arguments : a100
資訊: After call by : getBalance
資訊: Arguments : a100
資訊: Return Value : 2000
Balance of a100 2000
資訊: Before call by : transfer
資訊: Arguments : a100
資訊: Arguments : a101
資訊: Arguments : 1000
資訊: After call by : transfer
資訊: Arguments : a100
資訊: Arguments : a101
資訊: Arguments : 1000
資訊: Before call by : getBalance
資訊: Arguments : a100
資訊: After call by : getBalance
資訊: Arguments : a100
資訊: Return Value : 1000
Balance of a100 1000
資訊: Before call by : deposit
資訊: Arguments : a999
資訊: Arguments : 200
資訊: AccountNotFoundException found in ThrowsAdvice, method : deposit
資訊: AccountNotFoundException found in ThrowsAdvice, acount : a999 not found!
資訊: Before call by : withdraw
資訊: Arguments : a102
資訊: Arguments : 2000
資訊: BalanceException found in ThrowsAdvice, acount : a102 not found!
資訊: BalanceException found in ThrowsAdvice, transaction amount : 2000

項目結構如下圖所示:




這次備忘記主要是記錄 Spring course lab4 的過程.


lab4 requirement 的下載位址是:
http://blog.matrix.org.cn/resources/joeyta/spring_lab_04.zip

源程式下載位址是:
http://blog.matrix.org.cn/resources/joeyta/cpttm_spring_lab.zip


要求使用 Spring AOP 裡的 Advisor 及 Auto proxy 實作圖書館系統簡單功能.
包括 FlowControlPointcut 及 Introduction.

開始備忘記:
[1] 開始實作 lab 4:
<!--------------------- beans-config-lab4.xml ------------------>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "
http://www.springframework.org/dtd/spring-beans.dtd">


<beans>
 <bean id="libraryLogBeforeAdvice" class="mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice" />


 <bean id="libraryAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
  <property name="mappedNames">
   <list>
    <value>borrowBook</value>
    <value>returnBook</value>
   </list>
  </property>
  <property name="advice">
   <ref bean="libraryLogBeforeAdvice" />
  </property>
 </bean>


 <bean id="libraryChinaService" class="mo.org.cpttm.spring.lab4.Library">
  <property name="libraryId">
   <value>china</value>
  </property>
 </bean>


 <bean id="libraryMacauService" class="mo.org.cpttm.spring.lab4.Library">
  <property name="libraryId">
   <value>macau</value>
  </property>
 </bean>


 <bean id="libraryHKService" class="mo.org.cpttm.spring.lab4.Library">
  <property name="libraryId">
   <value>hk</value>
  </property>
 </bean>


 <bean id="libraryNetworkService" class="mo.org.cpttm.spring.lab4.LibraryNetwork">
  <property name="members">
   <list>
    <ref bean="libraryChinaService"/>
    <ref bean="libraryMacauService"/>    
    <ref bean="libraryHKService"/>    
   </list>
  </property>
 </bean>


 <bean id="libraryNetworkFlowControlPointcut"
  class="org.springframework.aop.support.ControlFlowPointcut">
  <constructor-arg index="0">
   <value>mo.org.cpttm.spring.lab4.LibraryNetwork</value>
  </constructor-arg>
  <constructor-arg index="1">
   <value>searchBook</value>
  </constructor-arg>
 </bean>


 <bean id="conferenceCenterIntroduction" class="mo.org.cpttm.spring.lab4.ConferenceCenterIntroduction"></bean>
 
 <bean id="conferenceCenterAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor">
  <constructor-arg index="0">
   <ref bean="conferenceCenterIntroduction"/>
  </constructor-arg>
  <constructor-arg index="1">
   <value>mo.org.cpttm.spring.lab4.IConferenceCenter</value>
  </constructor-arg>
 </bean>
 
 <bean id="libraryNetworkAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
  <property name="advice">
   <ref bean="libraryLogBeforeAdvice"/>
  </property>
  <property name="pointcut">
   <ref bean="libraryNetworkFlowControlPointcut"/>
  </property>
 </bean>


 <bean id="libraryProxyCreator"
  class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  <property name="beanNames">
   <list>
    <value>*Service</value>
   </list>
  </property>
  <property name="interceptorNames">
   <list>
    <value>libraryAdvisor</value>
    <value>libraryNetworkAdvisor</value> 
    <value>conferenceCenterAdvisor</value>   
   </list>
  </property>
 </bean>


</beans>
<!--------------------- beans-config-lab4.xml ------------------>

/**************** LibraryLogBeforeAdvice.java *******************/
package mo.org.cpttm.spring.lab4;


import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;


import org.springframework.aop.MethodBeforeAdvice;


public class LibraryLogBeforeAdvice implements MethodBeforeAdvice {
 private Logger logger = Logger.getLogger(this.getClass().getName());


 public void before(Method method, Object[] args, Object target)
   throws Throwable {
  logger.log(Level.INFO, "Before call by : " + method.getName());
  if (args != null) {
   for (Object object : args) {
    logger.log(Level.INFO, "Arguments : " + object.toString());
   }
  }
 }
}
/**************** LibraryLogBeforeAdvice.java *******************/

/**************** Book.java *******************/
package mo.org.cpttm.spring.lab4;


public class Book {
 @Override
 public String toString() {
  return "Book id:" + this.id + " # name:" + this.name + " # status:"
    + this.status;
 }


 public static final int AVAILABLE = 1;


 public static final int UNAVAILABLE = 2;


 private String id;


 private String name;


 private int status;


 public Book() {
 }


 public Book(String id, String name, int status) {
  this.id = id;
  this.name = name;
  this.status = status;
 }


 public String getId() {
  return id;
 }


 public void setId(String id) {
  this.id = id;
 }


 public String getName() {
  return name;
 }


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


 public int getStatus() {
  return status;
 }


 public void setStatus(int status) {
  this.status = status;
 }
}
/**************** Book.java *******************/

/**************** ILibrary.java *******************/
package mo.org.cpttm.spring.lab4;


import java.util.List;


public interface ILibrary {
 public String getLibraryId();
 public void setLibraryId(String libraryId);
 public void borrowBook(String bookId);
 public void returnBook(String bookId);
 public List<Book> searchBook(String keyword);
}
/**************** ILibrary.java *******************/

/**************** Library.java *******************/
package mo.org.cpttm.spring.lab4;


import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;


public class Library implements ILibrary {
 private Map<String,Book> books;
 private String libraryId;
 public Library() {
  books = new TreeMap<String,Book>();
  books.put("b1",new Book("b1","springframework", Book.AVAILABLE));
  books.put("b2",new Book("b2","ejb3", Book.AVAILABLE));
  books.put("b3",new Book("b3","hibernate", Book.AVAILABLE));
  books.put("b4",new Book("b4","design pattern", Book.AVAILABLE));
  books.put("b5",new Book("b5","tapestry with ajax", Book.AVAILABLE));  
  books.put("b6",new Book("b6","webwork", Book.AVAILABLE));  
  books.put("b7",new Book("b7","jsf with ajax", Book.AVAILABLE));  
 }


 public void borrowBook(String bookId) {
  books.get(bookId).setStatus(Book.UNAVAILABLE);
 }


 public String getLibraryId() {
  return this.libraryId;
 }


 public void returnBook(String bookId) {
  books.get(bookId).setStatus(Book.AVAILABLE);
 }


 public List<Book> searchBook(String keyword) {
  List<Book> bookResultList = new ArrayList<Book>();
  for (Iterator iter = books.entrySet().iterator(); iter.hasNext();) {
   Entry entry = (Entry) iter.next();
   Book book = (Book)entry.getValue();
   if(book.getName().toLowerCase().indexOf(keyword.toLowerCase()) >= 0 ){
    bookResultList.add(book);
   }
  }
  return bookResultList;
 }


 public void setLibraryId(String libraryId) {
  this.libraryId = libraryId;
 }


}
/**************** Library.java *******************/

/**************** ILibraryNetwork.java *******************/
package mo.org.cpttm.spring.lab4;


import java.util.List;
import java.util.Map;


public interface ILibraryNetwork {
 public void setMembers(ILibrary[] members);
 public Map<String,List<Book>> searchBook(String keyword);
}
/**************** ILibraryNetwork.java *******************/

/**************** LibraryNetwork.java *******************/
package mo.org.cpttm.spring.lab4;


import java.util.List;
import java.util.Map;
import java.util.TreeMap;


public class LibraryNetwork implements ILibraryNetwork {
 ILibrary[] libraries;
 public Map<String,List<Book>> searchBook(String keyword) {
  Map<String, List<Book>> map = new TreeMap<String, List<Book>>();
  for (ILibrary library : libraries) {
   map.put(library.getLibraryId(), library.searchBook(keyword));
  }
  return map;
 }


 public void setMembers(ILibrary[] members) {
  this.libraries = members;
 }


}
/**************** LibraryNetwork.java *******************/

/**************** IConferenceCenter.java *******************/
package mo.org.cpttm.spring.lab4;


import java.util.Date;


public interface IConferenceCenter {
 public void book(Date date, String person);
}
/**************** IConferenceCenter.java *******************/

/************* ConferenceCenterIntroduction.java ****************/
package mo.org.cpttm.spring.lab4;


import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;


import org.springframework.aop.support.DelegatingIntroductionInterceptor;


public class ConferenceCenterIntroduction extends
  DelegatingIntroductionInterceptor implements IConferenceCenter {


 private static final long serialVersionUID = -2727120915723691792L;


 private Map<Date, String> map;


 public void book(Date date, String person) {
  if (map == null) {
   map = new TreeMap<Date, String>();
  }
  map.put(date, person);
  for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
   Entry entry = (Entry) iter.next();
   Date bookDate = (Date) entry.getKey();
   String bookPerson = (String) entry.getValue();
   System.out.println("Conference Book Date:" + bookDate + " # Person:" + bookPerson);
  }
 }


}
/************* ConferenceCenterIntroduction.java ****************/

/**************** LibraryDemo.java *******************/
package mo.org.cpttm.spring.lab4;


import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class LibraryDemo {
 public static void printBooks(String location,List<Book> list){
  for (Iterator<Book> iter = list.iterator(); iter.hasNext();) {
   Book book = iter.next();
   System.out.println(location +"#" + book);
  }
 }
 
 @SuppressWarnings("unchecked")
 public static void printBooks(Map<String,List<Book>> map){
  for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
   Entry entry = (Entry) iter.next();
   String key = (String)entry.getKey();   
   List<Book> value = (List<Book>) entry.getValue();
   printBooks(key,value);
  }
  
 }
 
 public static void main(String[] args) throws Exception {
  ApplicationContext context = new ClassPathXmlApplicationContext(
    "mo/org/cpttm/spring/lab4/beans-config-lab4.xml");
  ILibrary library = (ILibrary) context.getBean("libraryChinaService");
  library.borrowBook("b1");
  printBooks("China",library.searchBook("spring"));  


  library = (ILibrary) context.getBean("libraryMacauService");
  library.borrowBook("b1");
  library.returnBook("b1");
  library.borrowBook("b7");
  printBooks("Macau",library.searchBook("ajax"));
  
  library = (ILibrary) context.getBean("libraryHKService");
  library.borrowBook("b3");
  library.borrowBook("b4");
  library.borrowBook("b5");
  printBooks("HK",library.searchBook("e"));


  System.out.println("************************************************");;
  
  ILibraryNetwork libraryNetwork = (ILibraryNetwork) context.getBean("libraryNetworkService");
  printBooks(libraryNetwork.searchBook("e"));


  
  System.out.println("************************************************");;
 
  ((IConferenceCenter) libraryNetwork).book(new Date(), "joeyta");
 }
}
/**************** LibraryDemo.java *******************/

執行結果如下所示:
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : borrowBook
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Arguments : b1
China#Book id:b1 # name:springframework # status:22006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : borrowBook
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Arguments : b1
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : returnBook


2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Arguments : b1
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : borrowBook
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Arguments : b7
Macau#Book id:b5 # name:tapestry with ajax # status:1
Macau#Book id:b7 # name:jsf with ajax # status:2
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : borrowBook
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Arguments : b3
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : borrowBook
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Arguments : b4
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : borrowBook
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Arguments : b5
HK#Book id:b1 # name:springframework # status:1
HK#Book id:b2 # name:ejb3 # status:1
HK#Book id:b3 # name:hibernate # status:2
HK#Book id:b4 # name:design pattern # status:2
HK#Book id:b5 # name:tapestry with ajax # status:2
HK#Book id:b6 # name:webwork # status:1
**********************************************************
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : getLibraryId
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : searchBook
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Arguments : e
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : getLibraryId
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : searchBook
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Arguments : e
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : getLibraryId
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Before call by : searchBook
2006/10/13 上午 10:22:29 mo.org.cpttm.spring.lab4.LibraryLogBeforeAdvice before
資訊: Arguments : e
china#Book id:b1 # name:springframework # status:2
china#Book id:b2 # name:ejb3 # status:1
china#Book id:b3 # name:hibernate # status:1
china#Book id:b4 # name:design pattern # status:1
china#Book id:b5 # name:tapestry with ajax # status:1
china#Book id:b6 # name:webwork # status:1
hk#Book id:b1 # name:springframework # status:1
hk#Book id:b2 # name:ejb3 # status:1
hk#Book id:b3 # name:hibernate # status:2
hk#Book id:b4 # name:design pattern # status:2
hk#Book id:b5 # name:tapestry with ajax # status:2
hk#Book id:b6 # name:webwork # status:1
macau#Book id:b1 # name:springframework # status:1
macau#Book id:b2 # name:ejb3 # status:1
macau#Book id:b3 # name:hibernate # status:1
macau#Book id:b4 # name:design pattern # status:1
macau#Book id:b5 # name:tapestry with ajax # status:1
macau#Book id:b6 # name:webwork # status:1
**********************************************************
Conference Book Date:Fri Oct 13 10:22:29 GMT+08:00 2006 # Person:joeyta




這次備忘記主要是記錄 Spring course lab5 的過程.


lab5 requirement 的下載位址是:
http://blog.matrix.org.cn/resources/joeyta/spring_lab_05.zip


源程式下載位址是:
http://blog.matrix.org.cn/resources/joeyta/cpttm_spring_lab.zip


要求使用 Spring JDBC 裡的 JDBCTemplate 及 operation modeling object 實作簡單的用戶電話薄系統功能.


開始備忘記:
[1]
安裝 HSQLDB 資料庫
[2] 基本資料
[3] 使用 JDBC Template 實作
[4] 使用 JDBC operation modeling object 實作


[1] 安裝 HSQLDB 資料庫:
下載 hsqldb_1_8_0_1.zip
http://sourceforge.net/project/showfiles.php?group_id=23316
解壓至 D:\cpttm\hsqldb


建立資料庫啟動檔案 D:\cpttm\hsqldb\start_db.bat
內容為:
java -cp ./lib/hsqldb.jar org.hsqldb.Server -database.0 mis -dbname.0 mis


-database.0 mis 定義產生相關的資料庫檔按以 mis 為 prefix
-dbname.0 mis 定義建立資料庫名稱為 mis
上面那個 ".0" 表示建主第一個資料庫的意思


建立資料庫管理啟動檔案 D:\cpttm\hsqldb\start_manager.bat
內容為:
java -cp ./lib/hsqldb.jar org.hsqldb.util.DatabaseManager


執行 D:\cpttm\hsqldb\start_db.bat
如下圖所示:


執行 D:\cpttm\hsqldb\start_manager.bat
如下圖所示

Recent 選擇 url
URL 為 jdbc:hsqldb:hsql://localhost/mis
mis 為資料庫名稱 (即上面 -dbname.0)
HSQLDB 預設 user 為 sa, password 為沒有


按 Ok 後,如下圖所示:


然後執行:
drop table phone if exists;
drop table customer if exists;


create table customer(
 customer_id varchar(10) not null,
 first_name varchar(30) not null,
 last_name varchar(30) not null,
 birthday date,
 address varchar(100),
 primary key(customer_id)
);


create table phone(
 customer_id varchar(10) not null,
 phone_number varchar(10) not null,
 primary key(customer_id, phone_number),
 foreign key(customer_id) references customer
);


按 View -> Refresh Tree 後, 如下圖所示:


HSQLDB 參考文檔:
http://www.hsqldb.org/web/hsqlDocsFrame.html



[2] 基本資料:
<!------------------------- beans-config-lab5.xml ---------------------->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "
http://www.springframework.org/dtd/spring-beans.dtd">


<beans>


 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName">
   <value>org.hsqldb.jdbcDriver</value>
  </property>
  <property name="url">
   <value>jdbc:hsqldb:hsql://localhost/mis</value>
  </property>
  <property name="username">
   <value>sa</value>
  </property>
  <property name="password">
   <value></value>
  </property>
 </bean>
 
 <bean id="customerDaoTemplate" class="mo.org.cpttm.spring.lab5.CustomerDaoTemplateImpl">
  <property name="dataSource">
   <ref bean="dataSource"/>
  </property>
 </bean>
 
 <bean id="customerDaoObject" class="mo.org.cpttm.spring.lab5.CustomerDaoObjectImpl">
  <property name="dataSource">
   <ref bean="dataSource"/>
  </property>
 </bean> 


</beans>
<!------------------------- beans-config-lab5.xml ---------------------->


/***************************** Customer.java *********************/
package mo.org.cpttm.spring.lab5;


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


public class Customer {
 private String customerId;
 private String firstName;
 private String lastName;
 private Date birthday;
 private String address;
 private List phones;
 
 @Override
 public String toString() {
  String sOutput = "Customer ID: " + this.customerId + "\n";
  sOutput += "Name: " + this.firstName + " " + this.lastName + "\n";
  sOutput += "Birthday: " + this.birthday + "\n";
  sOutput += "Address: " + this.address+ "\n";
  sOutput += "Phones: " + this.getPhones().toString() + "\n";  
  return sOutput;
 }
 
 public Customer(String customerId, String firstName, String lastName, Date birthday, String address, List phones) {
  super();
  this.customerId = customerId;
  this.firstName = firstName;
  this.lastName = lastName;
  this.birthday = birthday;
  this.address = address;
  this.phones = phones;
 }


 public Customer() {
 }


 public String getAddress() {
  return address;
 }
 public void setAddress(String address) {
  this.address = address;
 }
 public Date getBirthday() {
  return birthday;
 }
 public void setBirthday(Date birthday) {
  this.birthday = birthday;
 }
 public String getCustomerId() {
  return customerId;
 }
 public void setCustomerId(String customerId) {
  this.customerId = customerId;
 }
 public String getFirstName() {
  return firstName;
 }
 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 public String getLastName() {
  return lastName;
 }
 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
 public List getPhones() {
  if(phones == null){
   phones = new ArrayList();
  }
  return phones;
 }
 public void setPhones(List phones) {
  this.phones = phones;
 }
}
/***************************** Customer.java *********************/


/************************** ICustomerDao.java *********************/
package mo.org.cpttm.spring.lab5;


import java.util.List;


public interface ICustomerDao {
 public void insert(Customer customer);
 public void update(Customer customer);
 public void delete(Customer customer); 
 public Customer findById(String customerId);
 public List findAll();
 public int countAll();
}
/************************** ICustomerDao.java *********************/



[3] 使用 JDBC Template 實作:


/*********************** CustomerDaoTemplateImpl.java ******************/
package mo.org.cpttm.spring.lab5;


import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


import javax.sql.DataSource;


import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;


public class CustomerDaoTemplateImpl implements ICustomerDao {
 private JdbcTemplate jdbcTemplate;


 public void setDataSource(DataSource dataSource) {
  jdbcTemplate = new JdbcTemplate(dataSource);
 }


 public int countAll() {
  return jdbcTemplate.queryForInt("select count(*) from customer");
 }


 public void delete(Customer customer) {
  jdbcTemplate.update("delete from phone where customer_id=?",
    new Object[] { customer.getCustomerId() });
  jdbcTemplate.update("delete from customer where customer_id=?",
    new Object[] { customer.getCustomerId() });
 }


 @SuppressWarnings("unchecked")
 public List findAll() {
  List rows = jdbcTemplate
    .queryForList("select * from customer,phone where customer.customer_id=phone.customer_id order by customer.customer_id");


  Customer customer = new Customer();
  List list = new ArrayList();
  String old_customer_id = "";


  for (Iterator iter = rows.iterator(); iter.hasNext();) {
   Map rowMap = (Map) iter.next();
   if (!old_customer_id.equals(rowMap.get("customer_id").toString())) {
    customer = new Customer();
    customer.setCustomerId(rowMap.get("customer_id").toString());
    customer.setFirstName(rowMap.get("first_name").toString());
    customer.setLastName(rowMap.get("last_name").toString());
    customer.setBirthday((Date) rowMap.get("birthday"));
    customer.setAddress(rowMap.get("address").toString());
   }
   customer.getPhones().add(rowMap.get("phone_number").toString());
   if (!old_customer_id.equals(rowMap.get("customer_id").toString())) {
    list.add(customer);
    old_customer_id = rowMap.get("customer_id").toString();
   }
  }


  return list;
 }


 @SuppressWarnings("unchecked")
 public Customer findById(String customerId) {
  SqlRowSet rs = jdbcTemplate.queryForRowSet(
    "select * from customer where customer_id=?",
    new Object[] { customerId });


  Customer customer = new Customer();


  if (rs.next()) {
   customer.setCustomerId(rs.getString("customer_id"));
   customer.setFirstName(rs.getString("first_name"));
   customer.setLastName(rs.getString("last_name"));
   customer.setBirthday(rs.getDate("birthday"));
   customer.setAddress(rs.getString("address"));
  }


  rs = jdbcTemplate.queryForRowSet(
    "select * from phone where customer_id=?",
    new Object[] { customerId });


  while (rs.next()) {
   customer.getPhones().add(rs.getString("phone_number"));
  }
  return customer;
 }



 public void insert(Customer customer) {
  jdbcTemplate
    .update(
      "insert into customer(customer_id,first_name,last_name,birthday,address) values(?,?,?,?,?)",
      new Object[] { customer.getCustomerId(),
        customer.getFirstName(),
        customer.getLastName(), customer.getBirthday(),
        customer.getAddress() });


  insertPhoneBatch(customer);
 }


 private void insertPhoneBatch(final Customer customer) {
  jdbcTemplate.batchUpdate(
    "insert into phone(customer_id,phone_number) values (?,?)",
    new BatchPreparedStatementSetter() {


     public int getBatchSize() {
      return customer.getPhones().size();
     }


     public void setValues(PreparedStatement pstmt, int rowIndex)
       throws SQLException {
      String phoneNumber = customer.getPhones().get(rowIndex)
        .toString();
      pstmt.setString(1, customer.getCustomerId());
      pstmt.setString(2, phoneNumber);
     }


    });
 }


 public void update(final Customer customer) {
  jdbcTemplate
    .update(
      "update customer set first_name=?,last_name=?,birthday=?,address=? where customer_id=?",
      new Object[] { customer.getFirstName(),
        customer.getLastName(), customer.getBirthday(),
        customer.getAddress(), customer.getCustomerId() });


  jdbcTemplate.update("delete from phone where customer_id=?",
    new Object[] { customer.getCustomerId() });


  insertPhoneBatch(customer);


 }


}
/*********************** CustomerDaoTemplateImpl.java ******************/


/*********************** CustomerTemplateDemo.java ******************/
package mo.org.cpttm.spring.lab5;


import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Random;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class CustomerTemplateDemo {
 public static void main(String[] args) throws Exception {
  ApplicationContext context = new ClassPathXmlApplicationContext(
    "mo/org/cpttm/spring/lab5/beans-config-lab5.xml");
  ICustomerDao dao = (ICustomerDao) context
    .getBean("customerDaoTemplate");


  Calendar calendar = new GregorianCalendar();
  calendar.set(1980, 11 - 1, 25);


  Customer customer = new Customer();
  String randomCustomerId = "c" + new Random().nextInt(999999);
  customer.setCustomerId(randomCustomerId);
  customer.setFirstName("joeyta");
  customer.setLastName("chan");
  customer.setAddress("Rua de Areia Preto");
  customer.setBirthday(calendar.getTime());
  customer.setPhones(Arrays.asList(new Object[] { "999", "234567" }));


  dao.insert(customer);


  System.out.println("\n Before modify:");
  System.out.println(dao.findById(randomCustomerId));


  System.out.println(" Print All:");
  System.out.println(dao.findAll());


  calendar.set(1985, 3 - 1, 15);
  customer.setFirstName("Jane");
  customer.setLastName("Law");
  customer.setAddress("Rua de Casa Forte");
  customer.setBirthday(calendar.getTime());
  customer.setPhones(Arrays.asList(new Object[] { "8888", "7890" }));


  dao.update(customer);


  System.out.println();
  System.out.println("\n After modified:");
  System.out.println(dao.findById(randomCustomerId));


  System.out.println("Total " + dao.countAll() + " records");


  dao.delete(customer);
  System.out.println("\n Print All(After deleted):");
  System.out.println(dao.findAll());
  System.out.println("Total " + dao.countAll() + " records");


 }
}
/*********************** CustomerTemplateDemo.java ******************/


執行結果如下所示:
 Before modify:
Customer ID: c701550
Name: joeyta chan
Birthday: 1980-11-25
Address: Rua de Areia Preto
Phones: [234567, 999]


 Print All:
[Customer ID: c701550
Name: joeyta chan
Birthday: 1980-11-25
Address: Rua de Areia Preto
Phones: [234567, 999]
]



 After modified:
Customer ID: c701550
Name: Jane Law
Birthday: 1985-03-15
Address: Rua de Casa Forte
Phones: [7890, 8888]


Total 1 records


 Print All(After deleted):
[]
Total 0 records



[4] 使用 JDBC operation modeling object 實作
/*********************** CustomerCountOperation.java ******************/
package mo.org.cpttm.spring.lab5;


import javax.sql.DataSource;


import org.springframework.jdbc.object.SqlFunction;


public class CustomerCountOperation extends SqlFunction {


 public CustomerCountOperation(DataSource dataSource) {
  super(dataSource, "select count(*) from customer");
  compile();
 }
 
}
/*********************** CustomerCountOperation.java ******************/


/*********************** CustomerInsertOperation.java ******************/
package mo.org.cpttm.spring.lab5;


import java.sql.Types;


import javax.sql.DataSource;


import org.springframework.jdbc.object.SqlUpdate;


public class CustomerInsertOperation extends SqlUpdate {
 public CustomerInsertOperation(DataSource dataSource) {
  super(
    dataSource,
    "insert into customer(customer_id,first_name,last_name,birthday,address) values(?,?,?,?,?)");
  int[] types = { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
    Types.DATE, Types.VARCHAR };
  setTypes(types);
  compile();
 }
}
/*********************** CustomerInsertOperation.java ******************/


/*********************** PhoneInsertOperation.java ******************/
package mo.org.cpttm.spring.lab5;


import java.sql.Types;


import javax.sql.DataSource;


import org.springframework.jdbc.object.SqlUpdate;


public class PhoneInsertOperation extends SqlUpdate {
 public PhoneInsertOperation(DataSource dataSource){
  super(dataSource, "insert into phone(customer_id,phone_number) values(?,?)");
  int [] types = {Types.VARCHAR, Types.VARCHAR};
  setTypes(types);
  compile();
 }
}
/*********************** PhoneInsertOperation.java ******************/


/*********************** CustomerDeleteOperation.java ******************/
package mo.org.cpttm.spring.lab5;


import java.sql.Types;


import javax.sql.DataSource;


import org.springframework.jdbc.object.SqlUpdate;


public class CustomerDeleteOperation extends SqlUpdate {
 public CustomerDeleteOperation(DataSource dataSource, String tableName){
  super(dataSource, "delete from " + tableName + " where customer_id=?");
  setTypes(new int[]{Types.VARCHAR});
  compile();  
 }
 
 public void delete(String customerId){
  Object[] params = new Object[]{customerId};
  update(params);
 }
}
/*********************** CustomerDeleteOperation.java ******************/


/*********************** CustomerUpdateOperation.java ******************/
package mo.org.cpttm.spring.lab5;


import java.sql.Types;


import javax.sql.DataSource;


import org.springframework.jdbc.object.SqlUpdate;


public class CustomerUpdateOperation extends SqlUpdate {
 public CustomerUpdateOperation(DataSource dataSource){
  super(dataSource, "update customer set first_name=?,last_name=?,birthday=?,address=? where customer_id=?");
  int [] types = {Types.VARCHAR, Types.VARCHAR, Types.DATE, Types.VARCHAR, Types.VARCHAR};
  setTypes(types);
  compile();
 }
}
/*********************** CustomerUpdateOperation.java ******************/


/*********************** CustomerDaoObjectImpl.java ******************/
package mo.org.cpttm.spring.lab5;


import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


import javax.sql.DataSource;


public class CustomerDaoObjectImpl implements ICustomerDao {


 private DataSource dataSource;


 public void setDataSource(DataSource dataSource) {
  this.dataSource = dataSource;
 }


 public int countAll() {
  CustomerCountOperation operation = new CustomerCountOperation(
    dataSource);
  return operation.run();
 }


 public void delete(Customer customer) {
  CustomerDeleteOperation customerPhoneDeleteOperation = new CustomerDeleteOperation(
    dataSource, "phone");
  customerPhoneDeleteOperation.delete(customer.getCustomerId());


  CustomerDeleteOperation customerDeleteOperation = new CustomerDeleteOperation(
    dataSource, "customer");
  customerDeleteOperation.delete(customer.getCustomerId());
 }


 @SuppressWarnings("unchecked")
 public List findAll() {
  CustomerQueryOperation cqQperation = new CustomerQueryOperation(
    dataSource);
  List customerList = cqQperation.execute();
  return combineCustomer(customerList);
 }


 @SuppressWarnings("unchecked")
 public List combineCustomer(List customerList) {
  Map customerMap = new HashMap();
  for (Iterator iter = customerList.iterator(); iter.hasNext();) {
   Customer customer = (Customer) iter.next();
   if (customerMap.containsKey(customer.getCustomerId())) {
    ((Customer) customerMap.get(customer.getCustomerId()))
      .getPhones()
      .add(customer.getPhones().get(0).toString());
   } else {
    customerMap.put(customer.getCustomerId(), customer);
   }
  }
  return Arrays.asList(customerMap.values().toArray(new Customer[] {}));
 } 
 
 @SuppressWarnings("unchecked")
 public Customer findById(String customerId) {
  CustomerQueryOperation cqQperation = new CustomerQueryOperation(
    dataSource, customerId);
  List customerList = cqQperation.execute();
  if (customerList.size() == 0) {
   return new Customer();
  }
  return (Customer) combineCustomer(customerList).get(0);
 }


 public void insert(Customer customer) {
  CustomerInsertOperation ciOperation = new CustomerInsertOperation(
    dataSource);
  ciOperation.update(new Object[] { customer.getCustomerId(),
    customer.getFirstName(), customer.getLastName(),
    customer.getBirthday(), customer.getAddress() });


  insertPhoneBatch(customer);
 }


 private void insertPhoneBatch(final Customer customer) {
  for (Iterator iter = customer.getPhones().iterator(); iter.hasNext();) {
   String phoneNumber = (String) iter.next();
   PhoneInsertOperation piOperation = new PhoneInsertOperation(
     dataSource);
   piOperation.update(new Object[] { customer.getCustomerId(),
     phoneNumber });
  }
 }


 public void update(final Customer customer) {
  CustomerUpdateOperation cuOperation = new CustomerUpdateOperation(
    dataSource);
  cuOperation.update(new Object[] { customer.getFirstName(),
    customer.getLastName(), customer.getBirthday(),
    customer.getAddress(), customer.getCustomerId() });


  CustomerDeleteOperation customerPhoneDeleteOperation = new CustomerDeleteOperation(
    dataSource, "phone");
  customerPhoneDeleteOperation.delete(customer.getCustomerId());


  insertPhoneBatch(customer);


 }


}
/*********************** CustomerDaoObjectImpl.java ******************/


/*********************** CustomerObjectDemo.java ******************/
package mo.org.cpttm.spring.lab5;


import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Random;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class CustomerObjectDemo {
 public static void main(String[] args) throws Exception {
  ApplicationContext context = new ClassPathXmlApplicationContext(
    "mo/org/cpttm/spring/lab5/beans-config-lab5.xml");
  ICustomerDao dao = (ICustomerDao) context.getBean("customerDaoObject");


  Calendar calendar = new GregorianCalendar();
  calendar.set(1980, 11 - 1, 25);


  Customer customer = new Customer();
  String randomCustomerId = "c" + new Random().nextInt(999999);
  customer.setCustomerId(randomCustomerId);
  customer.setFirstName("joeyta");
  customer.setLastName("chan");
  customer.setAddress("Rua de Areia Preto");
  customer.setBirthday(calendar.getTime());
  customer.setPhones(Arrays.asList(new Object[] { "999", "234567" }));


  dao.insert(customer);


  System.out.println("\n Before modify:");
  System.out.println(dao.findById(randomCustomerId));


  System.out.println(" Print All:");
  System.out.println(dao.findAll());


  calendar.set(1985, 3 - 1, 15);
  customer.setFirstName("Jane");
  customer.setLastName("Law");
  customer.setAddress("Rua de Casa Forte");
  customer.setBirthday(calendar.getTime());
  customer.setPhones(Arrays.asList(new Object[] { "8888", "7890" }));


  dao.update(customer);


  System.out.println();
  System.out.println("\n After modified:");
  System.out.println(dao.findById(randomCustomerId));


  System.out.println("Total " + dao.countAll() + " records");


  dao.delete(customer);
  System.out.println("\n Print All(After deleted):");
  System.out.println(dao.findAll());
  System.out.println("Total " + dao.countAll() + " records");
 }
}
/*********************** CustomerObjectDemo.java ******************/


執行結果如下所示:
 Before modify:
Customer ID: c615566
Name: joeyta chan
Birthday: 1980-11-25
Address: Rua de Areia Preto
Phones: [234567, 999]


 Print All:
[Customer ID: c615566
Name: joeyta chan
Birthday: 1980-11-25
Address: Rua de Areia Preto
Phones: [234567, 999]
]



 After modified:
Customer ID: c615566
Name: Jane Law
Birthday: 1985-03-15
Address: Rua de Casa Forte
Phones: [7890, 8888]


Total 1 records


 Print All(After deleted):
[]
Total 0 records


項目結構如下圖所示:


注意, 必須加下以下 jar 檔:
spring.jar
log4j.jar
commons-logging.jar
junit.jar
hsqldb.jar
commons-pool.jar
commons-dbcp.jar
commons-collections.jar


參考官方文檔
http://static.springframework.org/spring/docs/2.0.x/reference/jdbc.html


 



這次備忘記主要是記錄 Spring course lab6 的過程.


lab6 requirement 的下載位址是:
http://blog.matrix.org.cn/resources/joeyta/spring_lab_06.zip


源程式下載位址是:
http://blog.matrix.org.cn/resources/joeyta/cpttm_spring_lab.zip


這次 lab 要求使用 Programmatic transaction management 及 Declarative transaction management 實作簡單的用戶電話薄系統功能. 這次是延續 lab5 實作, 但新增了 IGroupService 介面, 這個 service 主要是可以同時新增多位用戶, 但在 customer table 裡加入了 unique(first_name, last_name) 的限制, 故當加入相同名字的兩位用戶時, 這個 transaction 就會 rollback.

開始備忘記:
[1] 安裝 HSQLDB 資料庫
[2] 基本資料
[3] 使用 Programmatic transaction management 實作
[4] 使用 Declarative transaction management 實作


[1] 安裝 HSQLDB 資料庫:
下載 hsqldb_1_8_0_1.zip
http://sourceforge.net/project/showfiles.php?group_id=23316
解壓至 D:\cpttm\hsqldb


建立資料庫啟動檔案 D:\cpttm\hsqldb\start_db.bat
內容為:
java -cp ./lib/hsqldb.jar org.hsqldb.Server -database.0 mis -dbname.0 mis


-database.0 mis 定義產生相關的資料庫檔按以 mis 為 prefix
-dbname.0 mis 定義建立資料庫名稱為 mis
上面那個 ".0" 表示建主第一個資料庫的意思


建立資料庫管理啟動檔案 D:\cpttm\hsqldb\start_manager.bat
內容為:
java -cp ./lib/hsqldb.jar org.hsqldb.util.DatabaseManager


執行 D:\cpttm\hsqldb\start_db.bat
如下圖所示:


執行 D:\cpttm\hsqldb\start_manager.bat
如下圖所示

Recent 選擇 url
URL 為 jdbc:hsqldb:hsql://localhost/mis
mis 為資料庫名稱 (即上面 -dbname.0)
HSQLDB 預設 user 為 sa, password 為沒有


按 Ok 後,如下圖所示:


然後執行:
drop table phone if exists;
drop table customer if exists;


create table customer(
 customer_id varchar(10) not null,
 first_name varchar(30) not null,
 last_name varchar(30) not null,
 birthday date,
 address varchar(100),
 primary key(customer_id),
unique(first_name, last_name)
);


create table phone(
 customer_id varchar(10) not null,
 phone_number varchar(10) not null,
 primary key(customer_id, phone_number),
 foreign key(customer_id) references customer,
 unique(phone_number)

);


按 View -> Refresh Tree 後, 如下圖所示:


HSQLDB 參考文檔:
http://www.hsqldb.org/web/hsqlDocsFrame.html



[2] 基本資料:
<!------------------------- beans-config-lab6.xml ---------------------->
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "
http://www.springframework.org/dtd/spring-beans.dtd">


<beans>


 <bean id="dataSource"
  class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName">
   <value>org.hsqldb.jdbcDriver</value>
  </property>
  <property name="url">
   <value>jdbc:hsqldb:hsql://localhost/mis</value>
  </property>
  <property name="username">
   <value>sa</value>
  </property>
  <property name="password">
   <value></value>
  </property>
 </bean>


 <bean id="customerDao"
  class="mo.org.cpttm.spring.lab6.CustomerDaoTemplateImpl">
  <property name="dataSource">
   <ref bean="dataSource" />
  </property>
 </bean>


 <bean id="programmaticTransactionCustomerGroupService"
  class="mo.org.cpttm.spring.lab6.ProgrammaticTransactionCustomerGroupService">
  <property name="customerDao">
   <ref bean="customerDao" />
  </property>
 </bean>


 <bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource">
   <ref bean="dataSource" />
  </property>
 </bean>


 <bean id="customerGroupService"
  class="mo.org.cpttm.spring.lab6.CustomerGroupService">
  <property name="customerDao">
   <ref bean="customerDao" />
  </property>
 </bean>


 <bean id="groupServiceProxy"
  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
  <property name="proxyInterfaces">
   <list>
    <value>mo.org.cpttm.spring.lab6.IGroupService</value>
   </list>
  </property>
  <property name="target">
   <ref bean="customerGroupService" />
  </property>
  <property name="transactionManager">
   <ref bean="transactionManager" />
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="insert*">PROPAGATION_REQUIRED</prop>
   </props>
  </property>
 </bean>
</beans>
<!------------------------- beans-config-lab6.xml ---------------------->


/***************************** Customer.java *********************/
package mo.org.cpttm.spring.lab5;


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


public class Customer {
 private String customerId;
 private String firstName;
 private String lastName;
 private Date birthday;
 private String address;
 private List phones;
 
 @Override
 public String toString() {
  String sOutput = "Customer ID: " + this.customerId + "\n";
  sOutput += "Name: " + this.firstName + " " + this.lastName + "\n";
  sOutput += "Birthday: " + this.birthday + "\n";
  sOutput += "Address: " + this.address+ "\n";
  sOutput += "Phones: " + this.getPhones().toString() + "\n";  
  return sOutput;
 }
 
 public Customer(String customerId, String firstName, String lastName, Date birthday, String address, List phones) {
  super();
  this.customerId = customerId;
  this.firstName = firstName;
  this.lastName = lastName;
  this.birthday = birthday;
  this.address = address;
  this.phones = phones;
 }


 public Customer() {
 }


 public String getAddress() {
  return address;
 }
 public void setAddress(String address) {
  this.address = address;
 }
 public Date getBirthday() {
  return birthday;
 }
 public void setBirthday(Date birthday) {
  this.birthday = birthday;
 }
 public String getCustomerId() {
  return customerId;
 }
 public void setCustomerId(String customerId) {
  this.customerId = customerId;
 }
 public String getFirstName() {
  return firstName;
 }
 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 public String getLastName() {
  return lastName;
 }
 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
 public List getPhones() {
  if(phones == null){
   phones = new ArrayList();
  }
  return phones;
 }
 public void setPhones(List phones) {
  this.phones = phones;
 }
}
/***************************** Customer.java *********************/


/************************** ICustomerDao.java *********************/
package mo.org.cpttm.spring.lab5;


import java.util.List;


public interface ICustomerDao {
 public void insert(Customer customer);
 public void update(Customer customer);
 public void delete(Customer customer); 
 public Customer findById(String customerId);
 public List findAll();
 public int countAll();
}
/************************** ICustomerDao.java *********************/

/*********************** CustomerDaoTemplateImpl.java ******************/
package mo.org.cpttm.spring.lab6;


import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


import javax.sql.DataSource;


import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;


public class CustomerDaoTemplateImpl implements ICustomerDao {
 private JdbcTemplate jdbcTemplate;


 public void setDataSource(DataSource dataSource) {
  jdbcTemplate = new JdbcTemplate(dataSource);
 }
 
 public DataSource getDataSource(){
  return jdbcTemplate.getDataSource();
 }


 public int countAll() {
  return jdbcTemplate.queryForInt("select count(*) from customer");
 }


 public void delete(Customer customer) {
  jdbcTemplate.update("delete from phone where customer_id=?",
    new Object[] { customer.getCustomerId() });
  jdbcTemplate.update("delete from customer where customer_id=?",
    new Object[] { customer.getCustomerId() });
 }


 @SuppressWarnings("unchecked")
 public List findAll() {
  List rows = jdbcTemplate
    .queryForList("select * from customer,phone where customer.customer_id=phone.customer_id order by customer.customer_id");


  Customer customer = new Customer();
  List list = new ArrayList();
  String old_customer_id = "";


  for (Iterator iter = rows.iterator(); iter.hasNext();) {
   Map rowMap = (Map) iter.next();
   if (!old_customer_id.equals(rowMap.get("customer_id").toString())) {
    customer = new Customer();
    customer.setCustomerId(rowMap.get("customer_id").toString());
    customer.setFirstName(rowMap.get("first_name").toString());
    customer.setLastName(rowMap.get("last_name").toString());
    customer.setBirthday((Date) rowMap.get("birthday"));
    customer.setAddress(rowMap.get("address").toString());
   }
   customer.getPhones().add(rowMap.get("phone_number").toString());
   if (!old_customer_id.equals(rowMap.get("customer_id").toString())) {
    list.add(customer);
    old_customer_id = rowMap.get("customer_id").toString();
   }
  }


  return list;
 }


 @SuppressWarnings("unchecked")
 public Customer findById(String customerId) {
  SqlRowSet rs = jdbcTemplate.queryForRowSet(
    "select * from customer where customer_id=?",
    new Object[] { customerId });


  Customer customer = new Customer();


  if (rs.next()) {
   customer.setCustomerId(rs.getString("customer_id"));
   customer.setFirstName(rs.getString("first_name"));
   customer.setLastName(rs.getString("last_name"));
   customer.setBirthday(rs.getDate("birthday"));
   customer.setAddress(rs.getString("address"));
  }


  rs = jdbcTemplate.queryForRowSet(
    "select * from phone where customer_id=?",
    new Object[] { customerId });


  while (rs.next()) {
   customer.getPhones().add(rs.getString("phone_number"));
  }
  return customer;
 }



 public void insert(Customer customer) {
  jdbcTemplate
    .update(
      "insert into customer(customer_id,first_name,last_name,birthday,address) values(?,?,?,?,?)",
      new Object[] { customer.getCustomerId(),
        customer.getFirstName(),
        customer.getLastName(), customer.getBirthday(),
        customer.getAddress() });


  insertPhoneBatch(customer);
 }


 private void insertPhoneBatch(final Customer customer) {
  jdbcTemplate.batchUpdate(
    "insert into phone(customer_id,phone_number) values (?,?)",
    new BatchPreparedStatementSetter() {


     public int getBatchSize() {
      return customer.getPhones().size();
     }


     public void setValues(PreparedStatement pstmt, int rowIndex)
       throws SQLException {
      String phoneNumber = customer.getPhones().get(rowIndex)
        .toString();
      pstmt.setString(1, customer.getCustomerId());
      pstmt.setString(2, phoneNumber);
     }


    });
 }


 public void update(final Customer customer) {
  jdbcTemplate
    .update(
      "update customer set first_name=?,last_name=?,birthday=?,address=? where customer_id=?",
      new Object[] { customer.getFirstName(),
        customer.getLastName(), customer.getBirthday(),
        customer.getAddress(), customer.getCustomerId() });


  jdbcTemplate.update("delete from phone where customer_id=?",
    new Object[] { customer.getCustomerId() });


  insertPhoneBatch(customer);


 }


}
/*********************** CustomerDaoTemplateImpl.java ******************/

/*********************** IGroupService.java ******************/
package mo.org.cpttm.spring.lab6;


import java.util.List;


public interface IGroupService {
 public void setCustomerDao(ICustomerDao customerDao);
 public void insertGroup(List customers);
}
/*********************** IGroupService.java ******************/

[3] 使用 Programmatic transaction management 實作:
/************** ProgrammaticTransactionCustomerGroupService.java ********/
package mo.org.cpttm.spring.lab6;


import java.util.Iterator;
import java.util.List;


import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;


public class ProgrammaticTransactionCustomerGroupService implements IGroupService {
 private DataSourceTransactionManager transactionManager;


 private DefaultTransactionDefinition def;


 private ICustomerDao customerDao;


 public void setCustomerDao(ICustomerDao customerDao) {
  this.customerDao = customerDao;
  transactionManager = new DataSourceTransactionManager(
    ((CustomerDaoTemplateImpl) this.customerDao).getDataSource());
  def = new DefaultTransactionDefinition();
  def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
 }


 public void insertGroup(List customers) {
  TransactionStatus status = transactionManager.getTransaction(def);
  try {
   for (Iterator iter = customers.iterator(); iter.hasNext();) {
    Customer customer = (Customer) iter.next();
    customerDao.insert(customer);
   }
   transactionManager.commit(status);
  } catch (DataAccessException e) {
   transactionManager.rollback(status);
   throw e;
  }
 }
}
/************** ProgrammaticTransactionCustomerGroupService.java ********/

/*************** ProgrammaticTransactionDemo.java *******************/

package mo.org.cpttm.spring.lab6;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Random;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class ProgrammaticTransactionDemo {
 public static void main(String[] args) throws Exception {
  ApplicationContext context = new ClassPathXmlApplicationContext(
    "mo/org/cpttm/spring/lab6/beans-config-lab6.xml");
  IGroupService gs = (IGroupService) context
    .getBean("programmaticTransactionCustomerGroupService");


  Calendar calendar = new GregorianCalendar();
  calendar.set(1980, 11 - 1, 25);


  Customer customer1 = new Customer();
  String randomCustomerId1 = "c" + new Random().nextInt(999999);
  customer1.setCustomerId(randomCustomerId1);
  customer1.setFirstName("joeyta");
  customer1.setLastName("chan");
  customer1.setAddress("Rua de Areia Preto");
  customer1.setBirthday(calendar.getTime());
  customer1.setPhones(Arrays.asList(new Object[] { "999", "234567" }));


  Customer customer2 = new Customer();
  String randomCustomerId2 = "c" + new Random().nextInt(999999);
  customer2.setCustomerId(randomCustomerId2);
  customer2.setFirstName("joeyta");
  customer2.setLastName("chan");
  customer2.setAddress("Rua de Areia Preto");
  customer2.setBirthday(calendar.getTime());
  customer2.setPhones(Arrays.asList(new Object[] { "999", "234567" }));  
  
  List<Customer> customers = new ArrayList<Customer>();
  customers.add(customer1);
  customers.add(customer2);
  
  try {
   gs.insertGroup(customers);
  } catch (RuntimeException e) {
   e.printStackTrace();
  }


  System.out.println(" Print All:");
  ICustomerDao dao = (ICustomerDao) context.getBean("customerDao");  
  System.out.println(dao.findAll());


 }
}
/*************** ProgrammaticTransactionDemo.java *******************/

執行結果如下所示:
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into customer(customer_id,first_name,last_name,birthday,address) values(?,?,?,?,?)]; Violation of unique constraint $$: duplicate value(s) for column(s) $$: SYS_CT_291 in statement [insert into customer(customer_id,first_name,last_name,birthday,address) values(?,?,?,?,?)]; nested exception is java.sql.SQLException: Violation of unique constraint $$: duplicate value(s) for column(s) $$: SYS_CT_291 in statement [insert into customer(customer_id,first_name,last_name,birthday,address) values(?,?,?,?,?)]
java.sql.SQLException: Violation of unique constraint $$: duplicate value(s) for column(s) $$: SYS_CT_291 in statement [insert into customer(customer_id,first_name,last_name,birthday,address) values(?,?,?,?,?)]
 at org.hsqldb.jdbc.Util.throwError(Unknown Source)
 at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
 at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:101)
 at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:697)
 at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:476)
 at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:691)
 at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:753)
 at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:761)
 at mo.org.cpttm.spring.lab6.CustomerDaoTemplateImpl.insert(CustomerDaoTemplateImpl.java:97)
 at mo.org.cpttm.spring.lab6.ProgrammaticTransactionCustomerGroupService.insertGroup(ProgrammaticTransactionCustomerGroupService.java:32)
 at mo.org.cpttm.spring.lab6.ProgrammaticTransactionDemo.main(ProgrammaticTransactionDemo.java:46)
 Print All:
[]

[4] 使用 Declarative transaction management 實作:
/*************** CustomerGroupService.java *******************/
package mo.org.cpttm.spring.lab6;


import java.util.Iterator;
import java.util.List;


public class CustomerGroupService implements IGroupService {
 private ICustomerDao customerDao;


 public void setCustomerDao(ICustomerDao customerDao) {
  this.customerDao = customerDao;
 }


 public void insertGroup(List customers) {
  for (Iterator iter = customers.iterator(); iter.hasNext();) {
   Customer customer = (Customer) iter.next();
   customerDao.insert(customer);
  }
 }
}
/*************** CustomerGroupService.java *******************/

/*************** DeclarativeTransactionDemo.java *******************/

package mo.org.cpttm.spring.lab6;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Random;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class DeclarativeTransactionDemo {
 public static void main(String[] args) throws Exception {
  ApplicationContext context = new ClassPathXmlApplicationContext(
    "mo/org/cpttm/spring/lab6/beans-config-lab6.xml");
  IGroupService gs = (IGroupService) context
    .getBean("groupServiceProxy");


  Calendar calendar = new GregorianCalendar();
  calendar.set(1980, 11 - 1, 25);


  Customer customer1 = new Customer();
  String randomCustomerId1 = "c" + new Random().nextInt(999999);
  customer1.setCustomerId(randomCustomerId1);
  customer1.setFirstName("joeyta");
  customer1.setLastName("chan");
  customer1.setAddress("Rua de Areia Preto");
  customer1.setBirthday(calendar.getTime());
  customer1.setPhones(Arrays.asList(new Object[] { "999", "234567" }));


  Customer customer2 = new Customer();
  String randomCustomerId2 = "c" + new Random().nextInt(999999);
  customer2.setCustomerId(randomCustomerId2);
  customer2.setFirstName("joeyta");
  customer2.setLastName("chan");
  customer2.setAddress("Rua de Areia Preto");
  customer2.setBirthday(calendar.getTime());
  customer2.setPhones(Arrays.asList(new Object[] { "999", "234567" }));  
  
  List<Customer> customers = new ArrayList<Customer>();
  customers.add(customer1);
  customers.add(customer2);
  
  try {
   gs.insertGroup(customers);
  } catch (RuntimeException e) {
   e.printStackTrace();
  }


  System.out.println(" Print All:");
  ICustomerDao dao = (ICustomerDao) context.getBean("customerDao");  
  System.out.println(dao.findAll());


 }
}
/*************** DeclarativeTransactionDemo.java *******************/

執行結果如下所示:
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into customer(customer_id,first_name,last_name,birthday,address) values(?,?,?,?,?)]; Violation of unique constraint $$: duplicate value(s) for column(s) $$: SYS_CT_302 in statement [insert into customer(customer_id,first_name,last_name,birthday,address) values(?,?,?,?,?)]; nested exception is java.sql.SQLException: Violation of unique constraint $$: duplicate value(s) for column(s) $$: SYS_CT_302 in statement [insert into customer(customer_id,first_name,last_name,birthday,address) values(?,?,?,?,?)]
java.sql.SQLException: Violation of unique constraint $$: duplicate value(s) for column(s) $$: SYS_CT_302 in statement [insert into customer(customer_id,first_name,last_name,birthday,address) values(?,?,?,?,?)]
 at org.hsqldb.jdbc.Util.throwError(Unknown Source)
 at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
 at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:101)
 at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:697)
 at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:476)
 at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:691)
 at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:753)
 at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:761)
 at mo.org.cpttm.spring.lab6.CustomerDaoTemplateImpl.insert(CustomerDaoTemplateImpl.java:97)
 at mo.org.cpttm.spring.lab6.CustomerGroupService.insertGroup(CustomerGroupService.java:16)
 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:585)
 at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:287)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:181)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:148)
 at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
 at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176)
 at $Proxy0.insertGroup(Unknown Source)
 at mo.org.cpttm.spring.lab6.DeclarativeTransactionDemo.main(DeclarativeTransactionDemo.java:46)
 Print All:
[]

上面的執行結果只實作了 rollback 的例子, commit 的例子我就不實作了.



這次備忘記主要是記錄 Spring course lab7 的過程.


lab7 requirement 的下載位址是:
http://blog.matrix.org.cn/resources/joeyta/spring_lab_07.zip


源程式下載位址是:
http://blog.matrix.org.cn/resources/joeyta/cpttm_spring_lab.zip


這次 lab 要求使用 Spring + Hibernate 實作用戶電話薄系統簡單功能, 在這裡要求使用 Hibernate 實作 one-to-many 及 many-to-one , 並要求實作 DAO 及 Transaction 功能.

開始備忘記:
[1] 安裝 HSQLDB 資料庫
[2] 下載及配置 Hibernate
[3] 實作

[1] 安裝 HSQLDB 資料庫:
下載 hsqldb_1_8_0_1.zip
http://sourceforge.net/project/showfiles.php?group_id=23316
解壓至 D:\cpttm\hsqldb


建立資料庫啟動檔案 D:\cpttm\hsqldb\start_db.bat
內容為:
java -cp ./lib/hsqldb.jar org.hsqldb.Server -database.0 mis -dbname.0 mis


-database.0 mis 定義產生相關的資料庫檔按以 mis 為 prefix
-dbname.0 mis 定義建立資料庫名稱為 mis
上面那個 ".0" 表示建主第一個資料庫的意思


建立資料庫管理啟動檔案 D:\cpttm\hsqldb\start_manager.bat
內容為:
java -cp ./lib/hsqldb.jar org.hsqldb.util.DatabaseManager


執行 D:\cpttm\hsqldb\start_db.bat
如下圖所示:


執行 D:\cpttm\hsqldb\start_manager.bat
如下圖所示

Recent 選擇 url
URL 為 jdbc:hsqldb:hsql://localhost/mis
mis 為資料庫名稱 (即上面 -dbname.0)
HSQLDB 預設 user 為 sa, password 為沒有


按 Ok 後,如下圖所示:


[2] 下載及配置 Hibernate
下載 hibernate-3.2.0.ga.zip
http://sourceforge.net/project/showfiles.php?group_id=40712&package_id=127784
解壓縮至 D:\cpttm\hibernate-3.2


D:\cpttm\hibernate-3.2\hibernate3.jar
D:\cpttm\hibernate-3.2\lib\antlr-2.7.6.jar
D:\cpttm\hibernate-3.2\lib\asm.jar
D:\cpttm\hibernate-3.2\lib\cglib-2.1.3.jar
D:\cpttm\hibernate-3.2\lib\commons-collections-2.1.1.jar
D:\cpttm\hibernate-3.2\lib\commons-logging-1.0.4.jar
D:\cpttm\hibernate-3.2\lib\dom4j-1.6.1.jar
D:\cpttm\hibernate-3.2\lib\ehcache-1.2.jar
D:\cpttm\hibernate-3.2\lib\jta.jar
D:\cpttm\hibernate-3.2\lib\log4j-1.2.11.jar

加入至 Eclipse 的 CLASSPATH 裡 , 如已經存在則省略.

[3] 實作
建立 hibernate 配置檔案:
############### hibernate.properties ############
hibernate.connection.driver_class=org.hsqldb.jdbcDriver
hibernate.connection.url=jdbc:hsqldb:hsql://localhost/mis
hibernate.connection.username=sa
hibernate.connection.password=
hibernate.dialect=org.hibernate.dialect.HSQLDialect
############### hibernate.properties ############

建立 Ant 設定檔:
<!----------------- build.xml ------------------------------->
<project name="HibernateDemo" default="schemaexport">
 <property name="build.dir" value="bin" />
 <property name="hibernate.home" value="d:/cpttm/hibernate-3.2" />
 <property name="hsqldb.home" value="d:/cpttm/hsqldb" />


 <path id="hibernate-classpath">
  <fileset dir="${hibernate.home}">
   <include name="**/*.jar" />
  </fileset>
  <fileset dir="${hsqldb.home}">
   <include name="lib/*.jar" />
  </fileset>
  <pathelement path="${build.dir}" />
 </path>


 <target name="schemaexport">
  <taskdef name="schemaexport"
   classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
   classpathref="hibernate-classpath" />
  <schemaexport properties="hibernate.properties"
   output="mis.sql" text="yes">
   <fileset dir="${build.dir}">
    <include name="**/*.hbm.xml" />
   </fileset>
  </schemaexport>
 </target>


 <target name="schemaupdate">
  <taskdef name="schemaupdate"
   classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
   classpathref="hibernate-classpath" />
  <schemaupdate properties="hibernate.properties" text="no">
   <fileset dir="${build.dir}">
    <include name="**/*.hbm.xml" />
   </fileset>
  </schemaupdate>
 </target>
</project>
<!----------------- build.xml ------------------------------->

右鍵點選 build.xml -> Run As -> 3. Ant Build...
選擇 schemaupdate , 就會使產料庫自動產生 table.


<!------------------------- beans-config-lab7.xml --------------------->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "
http://www.springframework.org/dtd/spring-beans.dtd">


<beans>


 <bean id="dataSource"
  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName">
   <value>org.hsqldb.jdbcDriver</value>
  </property>
  <property name="url">
   <value>jdbc:hsqldb:hsql://localhost/mis</value>
  </property>
  <property name="username">
   <value>sa</value>
  </property>
  <property name="password">
   <value></value>
  </property>
 </bean>


 <bean id="sessionFactory"
  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource">
   <ref bean="dataSource" />
  </property>
  <property name="mappingResources">
   <list>
    <value>mo/org/cpttm/spring/lab7/Customer.hbm.xml</value>
    <value>mo/org/cpttm/spring/lab7/Occupation.hbm.xml</value>
    <value>mo/org/cpttm/spring/lab7/Phone.hbm.xml</value>        
   </list>
  </property>
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">
     org.hibernate.dialect.HSQLDialect
    </prop>
   </props>
  </property>
 </bean>


 <bean id="customerDao"
  class="mo.org.cpttm.spring.lab7.CustomerDao">
  <property name="sessionFactory">
   <ref bean="sessionFactory" />
  </property>
 </bean>


 <bean id="transactionManager"
  class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory">
   <ref bean="sessionFactory" />
  </property>
 </bean>


 <bean id="customerGroupService"
  class="mo.org.cpttm.spring.lab7.CustomerGroupService">
  <property name="customerDao">
   <ref bean="customerDao" />
  </property>
 </bean>


 <bean id="groupServiceProxy"
  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
  <property name="proxyInterfaces">
   <list>
    <value>mo.org.cpttm.spring.lab7.IGroupService</value>
   </list>
  </property>
  <property name="target">
   <ref bean="customerGroupService" />
  </property>
  <property name="transactionManager">
   <ref bean="transactionManager" />
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="insert*">PROPAGATION_REQUIRED</prop>
   </props>
  </property>
 </bean>


</beans>
<!------------------------- beans-config-lab7.xml --------------------->

<!------------------------- Customer.hbm.xml --------------------->

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
 PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">


<hibernate-mapping package="mo.org.cpttm.spring.lab7">
 <class name="Customer" table="CUSTOMER">
  <id name="id" type="long" column="ID">
   <generator class="assigned" />
  </id> 
  <property name="firstName" type="string">
   <column name="FIRSTNAME" />
  </property>
  <property name="lastName" type="string">
   <column name="LASTNAME" />
  </property>
  <property name="birthday" type="date">
   <column name="BIRTHDAY" />
  </property>  
  <property name="address" type="string">
   <column name="ADDRESS" />
  </property>
  <many-to-one name="occupation" class="Occupation"
   column="OCCUPATION_ID" cascade="save-update" lazy="false"/>
  <bag name="phones" cascade="all" lazy="false">
   <key column="CUSTOMER_ID" />
   <one-to-many class="Phone" />
  </bag>
 </class>
</hibernate-mapping>
<!------------------------- Customer.hbm.xml --------------------->

<!------------------------- Occupation.hbm.xml --------------------->

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
 PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">


<hibernate-mapping package="mo.org.cpttm.spring.lab7">
 <class name="Occupation" table="OCCUPATION">
  <id name="id" type="long" column="ID">
   <generator class="native" />
  </id>
  <property name="type" type="string">
   <column name="TYPE" />
  </property>
  <property name="position" type="string">
   <column name="POSITION_COLUMN" />
  </property>
 </class>
</hibernate-mapping>
<!------------------------- Occupation.hbm.xml --------------------->

<!------------------------- Phone.hbm.xml --------------------->

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
 PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">


<hibernate-mapping package="mo.org.cpttm.spring.lab7">
 <class name="Phone" table="PHONE">
  <id name="id" type="long" column="ID">
   <generator class="native" />
  </id>


  <properties name="phoneKey" unique="true">
   <property name="countryCode" type="string"
    column="COUNTRY_CODE" />
   <property name="areaCode" type="string" column="AREA_CODE" />
   <property name="number" type="string" column="NUMBER" />
  </properties>
 </class>
</hibernate-mapping>
<!------------------------- Phone.hbm.xml --------------------->

/**************** Customer.java *******************/

package mo.org.cpttm.spring.lab7;


import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;


public class Customer {
 private Long id;
 private String firstName;
 private String lastName;
 private Date birthday;
 private Occupation occupation;
 private String address;
 private List phones;
 public Customer() {
 }
 public String getAddress() {
  return address;
 }
 public void setAddress(String address) {
  this.address = address;
 }
 public Date getBirthday() {
  return birthday;
 }
 public void setBirthday(Date birthday) {
  this.birthday = birthday;
 }
 public String getFirstName() {
  return firstName;
 }
 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 public Long getId() {
  return id;
 }
 public void setId(Long id) {
  this.id = id;
 }
 public String getLastName() {
  return lastName;
 }
 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
 public Occupation getOccupation() {
  return occupation;
 }
 public void setOccupation(Occupation occupation) {
  this.occupation = occupation;
 }
 public List getPhones() {
  if(phones == null){
   phones = new ArrayList();
  }
  return phones;
 }
 public void setPhones(List phones) {
  this.phones = phones;
 }
 @Override
 public String toString() {
  String sOutput = "Customer ID: " + this.id + "\n";
  sOutput += "Name: " + this.firstName + " " + this.lastName + "\n";
  sOutput += "Birthday: " + this.birthday + "\n";
  sOutput += "Address: " + this.address+ "\n";
  sOutput += "Occupation: " + this.occupation+ "\n";
  for (Iterator iter = phones.iterator(); iter.hasNext();) {
   Phone phone = (Phone) iter.next();
   sOutput += "Phones: " + phone.toString() + "\n";
  }
  return sOutput;
 } 
}
/**************** Customer.java *******************/

/**************** Occupation.java *******************/

package mo.org.cpttm.spring.lab7;


public class Occupation {
 private Long id;


 private String type;


 private String position;


 public Occupation() {
 }


 public Long getId() {
  return id;
 }


 public Occupation(String type, String position) {
  super();
  this.type = type;
  this.position = position;
 }


 public void setId(Long id) {
  this.id = id;
 }


 public String getPosition() {
  return position;
 }


 public void setPosition(String position) {
  this.position = position;
 }


 public String getType() {
  return type;
 }


 public void setType(String type) {
  this.type = type;
 }


 @Override
 public String toString() {
  return this.type + " - " + this.position;
 }
 
}
/**************** Occupation.java *******************/

/**************** Phone.java *******************/

package mo.org.cpttm.spring.lab7;


public class Phone {
 private Long id;


 private String countryCode;


 private String areaCode;


 private String number;


 public Phone(String countryCode, String areCode, String number) {
  this.countryCode = countryCode;
  this.areaCode = areCode;
  this.number = number;
 }


 public Phone() {
 }


 public String getAreaCode() {
  return areaCode;
 }


 public void setAreaCode(String areCode) {
  this.areaCode = areCode;
 }


 public String getCountryCode() {
  return countryCode;
 }


 public void setCountryCode(String countryCode) {
  this.countryCode = countryCode;
 }


 public Long getId() {
  return id;
 }


 public void setId(Long id) {
  this.id = id;
 }


 public String getNumber() {
  return number;
 }


 public void setNumber(String number) {
  this.number = number;
 }


 @Override
 public String toString() {
  return this.countryCode + "-" + this.areaCode + "-" + this.number;
 }


}
/**************** Phone.java *******************/

/**************** ICustomerDao.java *******************/

package mo.org.cpttm.spring.lab7;


import java.util.List;


public interface ICustomerDao {
 public void insert(Customer customer);
 public void update(Customer customer);
 public void delete(Customer customer);
 public Customer findById(Long customerId);
 public List findAll();
}
/**************** ICustomerDao.java *******************/

/**************** CustomerDao.java *******************/

package mo.org.cpttm.spring.lab7;


import java.sql.SQLException;
import java.util.List;


import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;


public class CustomerDao extends HibernateDaoSupport implements ICustomerDao {


 public void delete(Customer customer) {
  getHibernateTemplate().delete(customer);
 }


 public List findAll() {
  return getHibernateTemplate().find("from Customer");
 }


 public Customer findById(final Long customerId) {
  Customer customer = (Customer)getHibernateTemplate().execute(new HibernateCallback(){
   public Object doInHibernate(Session session) throws HibernateException, SQLException {
    Query query = session.createQuery("from Customer where id = ?");
    query.setLong(0, customerId);
    Customer customer2 = (Customer)query.uniqueResult();
    return customer2;
   }
   
  });


  return customer;
 }


 public void insert(Customer customer) {
  getHibernateTemplate().save(customer);


 }


 public void update(Customer customer) {
  getHibernateTemplate().update(customer);
 }


}
/**************** CustomerDao.java *******************/

/**************** IGroupService.java *******************/

package mo.org.cpttm.spring.lab7;


import java.util.List;


public interface IGroupService {
 public void setCustomerDao(ICustomerDao customerDao);
 public void insertGroup(List customers);
}
/**************** IGroupService.java *******************/

/**************** CustomerGroupService.java *******************/

package mo.org.cpttm.spring.lab7;


import java.util.Iterator;
import java.util.List;


public class CustomerGroupService implements IGroupService {


 private ICustomerDao customerDao;


 public void setCustomerDao(ICustomerDao customerDao) {
  this.customerDao = customerDao;
 }


 public void insertGroup(List customers) {
  for (Iterator iter = customers.iterator(); iter.hasNext();) {
   Customer customer = (Customer) iter.next();
   customerDao.insert(customer);
  }
 }
}
/**************** CustomerGroupService.java *******************/

/**************** CustomerDemo.java *******************/

package mo.org.cpttm.spring.lab7;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Random;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class CustomerDemo {
 ApplicationContext context;


 public CustomerDemo() {
  context = new ClassPathXmlApplicationContext(
    "mo/org/cpttm/spring/lab7/beans-config-lab7.xml");
 }


 public void tryHibernateDao() {
  ICustomerDao dao = (ICustomerDao) context.getBean("customerDao");


  Calendar calendar = new GregorianCalendar();
  calendar.set(1980, 11 - 1, 25);


  Phone phone1 = new Phone("853", "28", "123"
    + new Random().nextInt(999999));
  Phone phone2 = new Phone("852", "11", "654"
    + new Random().nextInt(999999));


  Occupation occupation = new Occupation("management", "Supervisor");


  Customer customer = new Customer();
  Long randomCustomerId = Long.valueOf(new Random().nextInt(999999));
  customer.setOccupation(occupation);
  customer.setId(randomCustomerId);
  customer.setFirstName("joeyta");
  customer.setLastName("chan");
  customer.setAddress("Rua de Areia Preto");
  customer.setBirthday(calendar.getTime());
  customer.getPhones().add(phone1);
  customer.getPhones().add(phone2);


  dao.insert(customer);


  System.out.println("\n Before modify:");
  System.out.println(dao.findById(randomCustomerId));


  System.out.println(" Print All:");
  System.out.println(dao.findAll());


  calendar.set(1985, 3 - 1, 15);
  customer.setFirstName("Jane");
  customer.setLastName("Law");
  customer.setAddress("Rua de Casa Forte");
  customer.setBirthday(calendar.getTime());


  dao.update(customer);


  System.out.println();
  System.out.println("\n After modified:");
  System.out.println(dao.findById(randomCustomerId));


  dao.delete(customer);
  System.out.println("\n Print All(After deleted):");
  System.out.println(dao.findAll());
 }


 public void tryHibernateTransaction() {
  IGroupService gs = (IGroupService) context.getBean("groupServiceProxy");


  Calendar calendar = new GregorianCalendar();
  calendar.set(1980, 11 - 1, 25);


  Occupation occupation1 = new Occupation("management", "Supervisor");
  Occupation occupation2 = new Occupation("management", "Manager");  
  
  Phone phone1 = new Phone("11", "28", "123111");
  Phone phone2 = new Phone("22", "33", "654222");


  Phone phone3 = new Phone("11", "28", "123111");
  Phone phone4 = new Phone("22", "33", "654222");  
  
  Customer customer1 = new Customer();
  Long randomCustomerId1 = Long.valueOf(new Random().nextInt(999999));
  customer1.setId(randomCustomerId1);
  customer1.setOccupation(occupation1);
  customer1.setFirstName("peter");
  customer1.setLastName("Wong");
  customer1.setAddress("Rua de Averida");
  customer1.setBirthday(calendar.getTime());
  customer1.getPhones().add(phone1);
  customer1.getPhones().add(phone2);
  
  Customer customer2 = new Customer();
  Long randomCustomerId2 = Long.valueOf(new Random().nextInt(999999));
  customer2.setId(randomCustomerId2);
  customer2.setOccupation(occupation2);
  customer2.setFirstName("Luis");
  customer2.setLastName("Lee");
  customer2.setAddress("Rua de AA");
  customer2.setBirthday(calendar.getTime());
  customer2.getPhones().add(phone3);
  customer2.getPhones().add(phone4);
  
  List<Customer> customers = new ArrayList<Customer>();
  customers.add(customer1);
  customers.add(customer2);


  try {
   gs.insertGroup(customers);
  } catch (RuntimeException e) {
   e.printStackTrace();
  }


  System.out.println(" Print All:");
  ICustomerDao dao = (ICustomerDao) context.getBean("customerDao");
  System.out.println(dao.findAll());
 }


 public static void main(String[] args) throws Exception {
  CustomerDemo demo = new CustomerDemo();
  demo.tryHibernateDao();
  demo.tryHibernateTransaction();
 }
}
/**************** CustomerDemo.java *******************/



這次備忘記主要是記錄 Spring course lab8 的過程.
這也是本課程的最後一次 Lab 了.


lab8 requirement 的下載位址是:
http://blog.matrix.org.cn/resources/joeyta/spring_lab_08.zip


源程式下載位址是:
http://blog.matrix.org.cn/resources/joeyta/SpringWebProject.zip


這次 lab 主要是把前面所學習的知識結合在一起,
重點加入 Spring Web MVC,
以 web base 方式實作 客戶電話薄 記錄系統.
以 Tomcat 為 container ,
結合 Spring + Hibernate + Hsqldb + Eclipse WTP 實作.

開始備忘記:
[1]
安裝 jdk 5
[2] 安裝 Eclipse WTP
[3] 下載 Spring 及安裝 Spring IDE
[4] 下載 Hibernate 及配置 Eclipse Hibernate Template
[5] 安裝 HSQLDB 資料庫
[6] 配置 hsqldb 與 eclipse
[7] 安裝及配置 tomcat 與 eclipse
[8] 使用 Eclipse WTP 建立 dynamic web project
[9] 建立 Database Schema update 配置檔案
[10] 項目源始檔
[11] Spring Web MVC Layout Demo


[1] 安裝 jdk 5:
下載 jdk-1_5_0_07-nb-5_0-win-ml.exe
http://java.sun.com/j2se/1.5.0/download-netbeans.html
安裝至 D:\jdk1.5.0_07
新增環境變數 JAVA_HOME=D:\jdk1.5.0_07
D:\jdk1.5.0_07\bin 加入至 PATH 中
D:\jdk1.5.0_07\lib\dt.jar 及 D:\jdk1.5.0_07\lib\tools.jar 加入至 CLASSPATH 中
執行 D:\>java -version
輸出 java version "1.5.0_07" 即安裝成功.


[2] 安裝 Eclipse WTP:
下載 wtp-all-in-one-sdk-R-1.5.0-200606281455-win32.zip
http://www.eclipse.org/webtools/
http://www.eclipse.org/downloads/download.php?file=/webtools/downloads/drops/R1.5/R-1.5.0-200606281455/wtp-all-in-one-sdk-R-1.5.0-200606281455-win32.zip&url=ftp://ftp.jaist.ac.jp/pub/eclipse/webtools/downloads/drops/R1.5/R-1.5.0-200606281455/wtp-all-in-one-sdk-R-1.5.0-200606281455-win32.zip&mirror_id=105
解壓縮至 D:\cpttm\eclipse


[3] 下載 Spring 及安裝 Spring IDE:
下載 spring-framework-1.2.8-with-dependencies.zip
http://www.springframework.org/download
http://superb-east.dl.sourceforge.net/sourceforge/springframework/spring-framework-1.2.8-with-dependencies.zip
解壓縮至 D:\CPTTM\spring-framework-1.2.8


安裝 Spring IDE:
Eclipse:Help -> Software Updates -> Find and Install -> Search for new features to install
按 New Remote Site
Name: Spring IDE
URL: http://springide.org/updatesite/
選擇最新的版本然後安裝.


[4] 下載 Hibernate 及配置 Eclipse Hibernate Template:
下載 hibernate-3.2.0.ga.zip
http://www.hibernate.org/6.html
http://jaist.dl.sourceforge.net/sourceforge/hibernate/hibernate-3.2.0.ga.zip
解壓至目錄 D:\cpttm\hibernate-3.2


配置 Eclipse Hibernate Template:
Eclipse:Window -> Preferences -> Web and XML -> XML Files -> XML Templates
按 New
Name: Hibernate 3.0 Templates
Pattern 為將下面的 xml 貼上, 按 OK -> OK 完成.


<?xml version="1.0" encoding="UTF-8" ?>
<templates>
  <template name="Hibernate Configuration" description="" context="xml_new" enabled="true" deleted="false" autoinsert="true"><?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> </hibernate-configuration></template>
  <template name="Hibernate Mapping" description="" context="xml_new" enabled="true" deleted="false" autoinsert="true"><?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> </hibernate-mapping></template>
</templates>


[5] 安裝 HSQLDB 資料庫:
下載 hsqldb_1_8_0_1.zip
http://sourceforge.net/project/showfiles.php?group_id=23316
解壓至 D:\cpttm\hsqldb


建立資料庫啟動檔案 D:\cpttm\hsqldb\start_db.bat
內容為:
java -cp ./lib/hsqldb.jar org.hsqldb.Server -database.0 mis -dbname.0 mis


-database.0 mis 定義產生相關的資料庫檔案以 mis 為 prefix
-dbname.0 mis 定義建立資料庫名稱為 mis
上面那個 ".0" 表示建主第一個資料庫的意思


建立資料庫管理啟動檔案 D:\cpttm\hsqldb\start_manager.bat
內容為:
java -cp ./lib/hsqldb.jar org.hsqldb.util.DatabaseManager


執行 D:\cpttm\hsqldb\start_db.bat
執行 D:\cpttm\hsqldb\start_manager.bat

Recent 選擇 url
URL 為 jdbc:hsqldb:hsql://localhost/mis
mis 為資料庫名稱 (即上面 -dbname.0)
HSQLDB 預設 user 為 sa, password 為沒有


[6] 配置 hsqldb 與 eclipse:
選擇 Windows -> Show View -> Other -> Data -> Database Explorer
打開後 右鍵 New Connection
Select a database manager: 隨便選一種 database
JDBC driver: Other
Databse: mis
JDBC driver class: org.hsqldb.jdbcDriver
Class location: D:\cpttm\hsqldb\lib\hsqldb.jar
Connection URL: jdbc:hsqldb:hsql://localhost/mis
User ID: sa
Password 為不輸入
按 Test Connection 後出現 Connection to [database] is successfull 即安裝成功


[7] 安裝及配置 tomcat 與 eclipse:
下載 apache-tomcat-6.0.0.zip
http://tomcat.apache.org/download-55.cgi
解壓縮至 D:\cpttm\apache-tomcat-5.5.20


啟動 eclipse
D:\cpttm\eclipse\eclipse.exe
選擇 Windows -> Show View -> Other -> Server -> Servers
打開後 右鍵 New -> Server
Server's host name : localhost
Select the server type:
Apache -> Tomcat v5.5 Server -> Next
Tomcat installation directory: D:\cpttm\apache-tomcat-5.5.20
然後按 Finish 成功配置.


[8] 使用 Eclipse WTP 建立 dynamic web project:
Eclipse: File -> New -> Other ->> Web -> Dynamic Web Project
Project name: SpringWebProject ->
Target Runtime: 選擇 Apache Tomcat v5.5
按 Finish 完成


右鍵點擊 SpringWebProject -> Add Spring Project Nature


展開
SpringWebProject -> WebContent -> WEB-INF -> lib
配制以下 library 至 上面目錄 [ 按 ctrl-c 然後 點目錄 ctrl-v 即可 ]
D:\cpttm\apache-tomcat-5.5.20\common\lib\servlet-api.jar
D:\cpttm\apache-tomcat-5.5.20\webapps\jsp-examples\WEB-INF\lib\jstl.jar
D:\cpttm\apache-tomcat-5.5.20\webapps\jsp-examples\WEB-INF\lib\standard.jar
D:\cpttm\hsqldb\lib\hsqldb.jar
D:\cpttm\spring-framework-1.2.8\dist\spring.jar
D:\cpttm\spring-framework-1.2.8\lib\jakarta-commons\commons-logging.jar
D:\cpttm\spring-framework-1.2.8\lib\log4j\log4j-1.2.13.jar
D:\cpttm\hibernate-3.2\hibernate3.jar
D:\cpttm\hibernate-3.2\lib\antlr-2.7.6.jar
D:\cpttm\hibernate-3.2\lib\asm.jar
D:\cpttm\hibernate-3.2\lib\cglib-2.1.3.jar
D:\cpttm\hibernate-3.2\lib\commons-collections-2.1.1.jar
D:\cpttm\hibernate-3.2\lib\dom4j-1.6.1.jar
D:\cpttm\hibernate-3.2\lib\ehcache-1.2.jar
D:\cpttm\hibernate-3.2\lib\jta.jar


建立 log4j.properties
Eclipse:Window -> Show View -> Navigator ->> SpringWebProject -> src -> New File
File name: log4j.properties , 然後按 Finish
內容為:
#################### log4j.properties ####################
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n


log4j.rootLogger=INFO, stdout


log4j.logger.org.hibernate.SQL=debug
log4j.logger.org.hibernate.type=debug
#################### log4j.properties ####################


然後再轉回 Eclipse:Window -> Show View -> Project Explorer


[9] 建立 Database Schema update 配置檔案:
<!----------------- build.xml ------------------------------->
<project name="HibernateDemo" default="schemaexport">
 <property name="build.dir" value="build/classes" />
 <property name="hibernate.home" value="d:/cpttm/hibernate-3.2" />
 <property name="hsqldb.home" value="d:/cpttm/hsqldb" />


 <path id="hibernate-classpath">
  <fileset dir="${hibernate.home}">
   <include name="**/*.jar" />
  </fileset>
  <fileset dir="${hsqldb.home}">
   <include name="lib/*.jar" />
  </fileset>
  <pathelement path="${build.dir}" />
 </path>


 <target name="schemaexport">
  <taskdef name="schemaexport"
   classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
   classpathref="hibernate-classpath" />
  <schemaexport properties="hibernate.properties"
   output="mis.sql" text="yes">
   <fileset dir="${build.dir}">
    <include name="**/*.hbm.xml" />
   </fileset>
  </schemaexport>
 </target>


 <target name="schemaupdate">
  <taskdef name="schemaupdate"
   classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
   classpathref="hibernate-classpath" />
  <schemaupdate properties="hibernate.properties" text="no">
   <fileset dir="${build.dir}">
    <include name="**/*.hbm.xml" />
   </fileset>
  </schemaupdate>
 </target>
</project>
<!----------------- build.xml ------------------------------->


右鍵點選 build.xml -> Run As -> 3. Ant Build...
選擇 schemaupdate , 就會使產料庫自動產生 table.
[ 在執行前, 必須完成所有 hibernate mapping file 的設定 ]


[10] 項目源始檔:
項目結構如下圖所示:


<!------------------- web.xml ------------------------->
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
 xmlns="
http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="
http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 <display-name>SpringWebProject</display-name>


 <servlet>
  <servlet-name>dispatcherServlet</servlet-name>
  <servlet-class>
   org.springframework.web.servlet.DispatcherServlet
  </servlet-class>
  <init-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>
    /WEB-INF/beans-config.xml,/WEB-INF/web-mvc-config.xml
   </param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>dispatcherServlet</servlet-name>
  <url-pattern>*.do</url-pattern>
 </servlet-mapping>


 <welcome-file-list>
  <welcome-file>index.html</welcome-file>
  <welcome-file>index.htm</welcome-file>
  <welcome-file>index.jsp</welcome-file>
  <welcome-file>default.html</welcome-file>
  <welcome-file>default.htm</welcome-file>
 </welcome-file-list>
</web-app>
<!------------------- web.xml ------------------------->


<!------------------- beans-config.xml ------------------------->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "
http://www.springframework.org/dtd/spring-beans.dtd">


<beans>
 <bean id="dataSource"
  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName">
   <value>org.hsqldb.jdbcDriver</value>
  </property>
  <property name="url">
   <value>jdbc:hsqldb:hsql://localhost/mis</value>
  </property>
  <property name="username">
   <value>sa</value>
  </property>
  <property name="password">
   <value></value>
  </property>
 </bean>


 <bean id="sessionFactory"
  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource">
   <ref bean="dataSource" />
  </property>
  <property name="mappingResources">
   <list>
    <value>mo/org/cpttm/spring/lab8/Customer.hbm.xml</value>
    <value>mo/org/cpttm/spring/lab8/Phone.hbm.xml</value>
   </list>
  </property>
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">
     org.hibernate.dialect.HSQLDialect
    </prop>
    <prop key="hibernate.query.factory_class">
     org.hibernate.hql.ast.ASTQueryTranslatorFactory
    </prop>
    <!--
     <prop key="hibernate.query.factory_class">
     org.hibernate.hql.classic.ClassicQueryTranslatorFactory
     </prop>
    -->
   </props>
  </property>
 </bean>


 <bean id="customerDao"
  class="mo.org.cpttm.spring.lab8.CustomerDao">
  <property name="sessionFactory">
   <ref bean="sessionFactory" />
  </property>
 </bean>


 <bean id="transactionManager"
  class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory">
   <ref bean="sessionFactory" />
  </property>
 </bean>


 <bean id="customerDaoProxy"
  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
  <property name="proxyInterfaces">
   <list>
    <value>mo.org.cpttm.spring.lab8.ICustomerDao</value>
   </list>
  </property>
  <property name="target">
   <ref bean="customerDao" />
  </property>
  <property name="transactionManager">
   <ref bean="transactionManager" />
  </property>
  <property name="transactionAttributes">
   <props>
    <prop key="insert">PROPAGATION_REQUIRED</prop>
    <prop key="update">PROPAGATION_REQUIRED</prop>
    <prop key="delete">PROPAGATION_REQUIRED</prop>
   </props>
  </property>
 </bean>


</beans>
<!------------------- beans-config.xml ------------------------->


<!------------------- web-mvc-config.xml ------------------------->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "
http://www.springframework.org/dtd/spring-beans.dtd">


<beans>
 <bean id="viewResolver"
  class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
  <property name="basename">
   <value>mvc-views</value>
  </property>
 </bean>


 <bean
  class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
  <property name="mappings">
   <props>
    <prop key="/customerlist.do">
     customerListController
    </prop>
    <prop key="/customerinsert.do">
     customerInsertController
    </prop>
    <prop key="/customerupdate.do">
     customerUpdateController
    </prop>
    <prop key="/customerdelete.do">
     customerDeleteController
    </prop>    
   </props>
  </property>
 </bean>


 <bean id="customerListController"
  class="mo.org.cpttm.spring.web.CustomerListController">
  <property name="viewPage">
   <value>customerlist</value>
  </property>
  <property name="customerDao">
   <ref bean="customerDaoProxy" />
  </property>
 </bean>


 <bean id="customerInsertController"
  class="mo.org.cpttm.spring.web.CustomerInsertController">
  <property name="formView">
   <value>customerform</value>
  </property>
  <property name="successView">
   <value>customerRedirect</value>
  </property>
  <property name="customerDao">
   <ref bean="customerDaoProxy" />
  </property>
 </bean>


 <bean id="customerUpdateController"
  class="mo.org.cpttm.spring.web.CustomerUpdateController">
  <property name="formView">
   <value>customerform</value>
  </property>
  <property name="successView">
   <value>customerRedirect</value>
  </property>
  <property name="customerDao">
   <ref bean="customerDaoProxy" />
  </property>
 </bean>
 
 <bean id="customerDeleteController"
  class="mo.org.cpttm.spring.web.CustomerDeleteController">
  <property name="viewPage">
   <value>customerRedirect</value>
  </property>
  <property name="customerDao">
   <ref bean="customerDaoProxy" />
  </property>
 </bean> 
</beans>
<!------------------- web-mvc-config.xml ------------------------->


##################### mvc-views.properties #################
customerlist.class=org.springframework.web.servlet.view.JstlView
customerlist.url=/WEB-INF/jsp/customerlist.jsp


customerform.class=org.springframework.web.servlet.view.JstlView
customerform.url=/WEB-INF/jsp/customerform.jsp


customerRedirect.class=org.springframework.web.servlet.view.RedirectView
customerRedirect.url=customerlist.do
##################### mvc-views.properties #################


/********************** Customer.java ***********************/
package mo.org.cpttm.spring.lab8;


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


public class Customer {
 private Long id;


 private String firstName;


 private String lastName;


 private String address;


 private List phones;


 public Customer() {
 }


 public String getAddress() {
  return address;
 }


 public void setAddress(String address) {
  this.address = address;
 }


 public String getFirstName() {
  return firstName;
 }


 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }


 public Long getId() {
  return id;
 }


 public void setId(Long id) {
  this.id = id;
 }


 public String getLastName() {
  return lastName;
 }


 public void setLastName(String lastName) {
  this.lastName = lastName;
 }


 public List getPhones() {
  if (phones == null) {
   phones = new ArrayList();
  }
  return phones;
 }


 public void setPhones(List phones) {
  this.phones = phones;
 }


 public int getPhoneSize(){
  if(phones != null){
   return phones.size();
  }
  return 0;
 }
 
 public String toString() {
  String sOutput = "Customer ID: " + this.id + "\n";
  sOutput += "Name: " + this.firstName + " " + this.lastName + "\n";
  sOutput += "Address: " + this.address + "\n";
  for (Iterator iter = phones.iterator(); iter.hasNext();) {
   Phone phone = (Phone) iter.next();
   sOutput += "Phones: " + phone.toString() + "\n";
  }
  return sOutput;
 }
}
/********************** Customer.java ***********************/


<!-------------------- Customer.hbm.xml ------------------->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
 PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">


<hibernate-mapping package="mo.org.cpttm.spring.lab8">
 <class name="Customer" table="CUSTOMER">
  <id name="id" type="long" column="ID">
   <generator class="native" />
  </id> 
  <property name="firstName" type="string">
   <column name="FIRSTNAME" />
  </property>
  <property name="lastName" type="string">
   <column name="LASTNAME" />
  </property>
  <property name="address" type="string">
   <column name="ADDRESS" />
  </property>
  <bag name="phones" cascade="all" lazy="false">
   <key column="CUSTOMER_ID" />
   <one-to-many class="Phone" />
  </bag>
 </class>
</hibernate-mapping>
<!-------------------- Customer.hbm.xml ------------------->


/********************** Phone.java ***********************/
package mo.org.cpttm.spring.lab8;


public class Phone {
 private Long id;


 private String number;


 public Phone(String number) {
  this.number = number;
 }


 public Phone() {
 }


 public Long getId() {
  return id;
 }


 public void setId(Long id) {
  this.id = id;
 }


 public String getNumber() {
  return number;
 }


 public void setNumber(String number) {
  this.number = number;
 }


 public String toString() {
  return this.number;
 }


}
/********************** Phone.java ***********************/


<!-------------------- Phone.hbm.xml ------------------->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
 PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">


<hibernate-mapping package="mo.org.cpttm.spring.lab8">
 <class name="Phone" table="PHONE">
  <id name="id" type="long" column="ID">
   <generator class="native" />
  </id>
  <property name="number" type="string" column="NUMBER" unique="true" />
 </class>
</hibernate-mapping>
<!-------------------- Phone.hbm.xml ------------------->


/******************* ICustomerDao.java ********************/
package mo.org.cpttm.spring.lab8;


import java.util.List;


public interface ICustomerDao {
 public void insert(Customer customer);
 public void update(Customer customer);
 public void delete(Customer customer);
 public Customer findById(Long customerId);
 public List findByKeyword(String keyword); 
 public List findAll();
}
/******************* ICustomerDao.java ********************/


/******************* CustomerDao.java ********************/
package mo.org.cpttm.spring.lab8;


import java.sql.SQLException;
import java.util.List;


import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;


public class CustomerDao extends HibernateDaoSupport implements ICustomerDao {


 public void delete(Customer customer) {
  getHibernateTemplate().delete(customer);
 }


 public List findAll() {
  return getHibernateTemplate().find("from Customer");
 }


 public Customer findById(final Long customerId) {
  Customer customer = (Customer) getHibernateTemplate().execute(
    new HibernateCallback() {
     public Object doInHibernate(Session session)
       throws HibernateException, SQLException {
      Query query = session
        .createQuery("from Customer where id = ?");
      query.setLong(0, customerId);
      Customer customer2 = (Customer) query.uniqueResult();
      return customer2;
     }


    });


  return customer;
 }


 public void insert(Customer customer) {
  getHibernateTemplate().save(customer);


 }


 public void update(Customer customer) {
  getHibernateTemplate().update(customer);
 }


 public List findByKeyword(String keyword) {
  return getHibernateTemplate().find(
    "from Customer where firstName=? or lastName=?",
    new Object[] { keyword, keyword });
 }
}
/******************* CustomerDao.java ********************/


/**************** CustomerListController.java *****************/
package mo.org.cpttm.spring.web;


import java.util.List;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import mo.org.cpttm.spring.lab8.ICustomerDao;


import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;


public class CustomerListController implements Controller {
 private String viewPage;


 private ICustomerDao customerDao;


 public ModelAndView handleRequest(HttpServletRequest request,
   HttpServletResponse response) throws Exception {
  String keyword = request.getParameter("keyword");
  List customers;
  if (keyword == null || keyword.equals("")) {
   customers = getCustomerDao().findAll();
  } else {
   customers = getCustomerDao().findByKeyword(keyword);
  }
  System.out.println(customers);
  return new ModelAndView(getViewPage(), "customers", customers)
    .addObject("keyword", keyword);
 }


 public ICustomerDao getCustomerDao() {
  return customerDao;
 }


 public void setCustomerDao(ICustomerDao customerDao) {
  this.customerDao = customerDao;
 }


 public String getViewPage() {
  return viewPage;
 }


 public void setViewPage(String viewPage) {
  this.viewPage = viewPage;
 }


}
/**************** CustomerListController.java *****************/


/**************** CustomerInsertController.java *****************/
package mo.org.cpttm.spring.web;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import mo.org.cpttm.spring.lab8.Customer;
import mo.org.cpttm.spring.lab8.ICustomerDao;
import mo.org.cpttm.spring.lab8.Phone;


import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;


public class CustomerInsertController extends SimpleFormController {
 private ICustomerDao customerDao;


 public CustomerInsertController() {
  setCommandClass(Customer.class);
 }


 protected ModelAndView onSubmit(HttpServletRequest request,
   HttpServletResponse response, Object command, BindException be)
   throws Exception {
  Phone phone0 = new Phone(request.getParameter("tel0"));
  Phone phone1 = new Phone(request.getParameter("tel1"));
  Phone phone2 = new Phone(request.getParameter("tel2"));
  
  Customer customer = (Customer) command;
  customer.getPhones().add(phone0);
  customer.getPhones().add(phone1);
  customer.getPhones().add(phone2);  
  customerDao.insert(customer);  
  return new ModelAndView(getSuccessView());
 }


 public ICustomerDao getCustomerDao() {
  return customerDao;
 }


 public void setCustomerDao(ICustomerDao customerDao) {
  this.customerDao = customerDao;
 }
}
/**************** CustomerInsertController.java *****************/


/**************** CustomerUpdateControllerjava *****************/
package mo.org.cpttm.spring.web;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import mo.org.cpttm.spring.lab8.Customer;
import mo.org.cpttm.spring.lab8.ICustomerDao;
import mo.org.cpttm.spring.lab8.Phone;


import org.springframework.validation.BindException;
import org.springframework.web.bind.RequestUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;


public class CustomerUpdateController extends SimpleFormController {
 private ICustomerDao customerDao;


 protected Object formBackingObject(HttpServletRequest request)
   throws Exception {
  Long id = RequestUtils.getRequiredLongParameter(request, "id");
  Customer customer = customerDao.findById(id);
  return customer;
 }


 protected ModelAndView onSubmit(HttpServletRequest request,
   HttpServletResponse response, Object command, BindException be)
   throws Exception {
  Customer customer = (Customer) command;
  if(customer.getPhones().get(0) != null){
   ((Phone)customer.getPhones().get(0)).setNumber(request.getParameter("tel0"));
  }
  if(customer.getPhones().get(1) != null){
   ((Phone)customer.getPhones().get(1)).setNumber(request.getParameter("tel1"));
  }
  if(customer.getPhones().get(2) != null){
   ((Phone)customer.getPhones().get(2)).setNumber(request.getParameter("tel2"));
  }  
  customerDao.update(customer);
  return new ModelAndView(getSuccessView());
 }


 public ICustomerDao getCustomerDao() {
  return customerDao;
 }


 public void setCustomerDao(ICustomerDao customerDao) {
  this.customerDao = customerDao;
 }
}
/**************** CustomerUpdateControllerjava *****************/


/**************** CustomerDeleteController.java *****************/
package mo.org.cpttm.spring.web;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import mo.org.cpttm.spring.lab8.ICustomerDao;


import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;


public class CustomerDeleteController implements Controller {
 private String viewPage;


 private ICustomerDao customerDao;


 public ModelAndView handleRequest(HttpServletRequest request,
   HttpServletResponse response) throws Exception {
  Long id = Long.parseLong(request.getParameter("id"));
  getCustomerDao().delete(getCustomerDao().findById(id));
  return new ModelAndView(getViewPage());
 }


 public ICustomerDao getCustomerDao() {
  return customerDao;
 }


 public void setCustomerDao(ICustomerDao customerDao) {
  this.customerDao = customerDao;
 }


 public String getViewPage() {
  return viewPage;
 }


 public void setViewPage(String viewPage) {
  this.viewPage = viewPage;
 }


}
/**************** CustomerDeleteController.java *****************/


<%--------------- customerlist.jsp -------------------------%>
<%@ taglib prefix="c" uri="
http://java.sun.com/jstl/core_rt"%>
<%@ page language="java" contentType="text/html; charset=BIG5"
 pageEncoding="BIG5"%>
<!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=BIG5">
<title>Customer List</title>
</head>
<body>
<h1>Customer List</h1>
<div>
 <form method="post">
  Search by name:<br/>
  <input type="text" name="keyword" value="${keyword}" />
  <input type="submit" value="Search" />
 </form>
 <form>
  <input type="submit" value="Show All" />
 </form>
</div>


<table border="1">
 <thead>
  <th>First Name</th>
  <th>Last Name</th>
  <th>Address</th>
  <th>Delete</th>
  <th>Phones</th>
 </thead>
 <c:forEach items="${customers}" var="customer">
  <tr>
   <td><a href="customerupdate.do?id=${customer.id}">${customer.firstName}</a></td>
   <td>${customer.lastName}</td>
   <td>${customer.address}</td>
   <td><a href="customerdelete.do?id=${customer.id}">delete</a></td>
   <td><c:forEach items="${customer.phones}" var="phone" varStatus="status">
    ${status.index!=0?',':''}&nbsp;${phone}
   </c:forEach></td>
  </tr>
 </c:forEach>
</table>
<br/>
<a href="customerinsert.do">Insert Customer</a>
</body>
</html>
<%--------------- customerlist.jsp -------------------------%>


<%--------------- customerform.jsp -------------------------%>
<%@ taglib prefix="c" uri="
http://java.sun.com/jstl/core_rt"%>
<%@ page language="java" contentType="text/html; charset=BIG5"
 pageEncoding="BIG5"%>
<!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=BIG5">
<title>Customer Form</title>
</head>
<body>
<h1>Customer Form</h1>


<form method="POST">
<table>
 <tr>
  <td>First Name</td>
  <td><input type="text" name="firstName"
   value="${command.firstName}" /></td>
 </tr>
 <tr>
  <td>Last Name</td>
  <td><input type="text" name="lastName"
   value="${command.lastName}" /></td>
 </tr>
 <tr>
  <td>Address</td>
  <td><input type="text" name="address" value="${command.address}" /></td>
 </tr>
 <c:forEach items="${command.phones}" var="phone" varStatus="status">
  <tr>
   <td>phone ${status.index}</td>
   <td><input type="text" name="tel${status.index}"
    value="${phone.number}" /></td>
  </tr>
 </c:forEach>
 <c:choose>
  <c:when test="${command.phoneSize == 0}">
   <c:forEach begin="0" end="2" step="1" varStatus="status">
    <tr>
     <td>phone ${status.index}</td>
     <td><input type="text" name="tel${status.index}"
      value="${phone.number}" /></td>
    </tr>
   </c:forEach>
  </c:when>
  <c:when test="${command.phoneSize == 1}"><
   <c:forEach begin="1" end="2" step="1" varStatus="status">
    <tr>
     <td>phone ${status.index}</td>
     <td><input type="text" name="tel${status.index}"
      value="${phone.number}" /></td>
    </tr>
   </c:forEach>
  </c:when>  
  <c:when test="${command.phoneSize == 2}"><
   <c:forEach begin="2" end="2" step="1" varStatus="status">
    <tr>
     <td>phone ${status.index}</td>
     <td><input type="text" name="tel${status.index}"
      value="${phone.number}" /></td>
    </tr>
   </c:forEach>
  </c:when>    
 </c:choose>
 <tr>
  <td colspan="2"><input type="submit" value="Submit" /></td>
 </tr>
</table>
</form>


</body>
</html>
<%--------------- customerform.jsp -------------------------%>


[11] Spring Web MVC Layout Demo:
右鍵點選 SpringWebProject -> Run As -> Run on Server -> Finish
如下圖所示:


就會自動啟動 Tomcat 及 發佈 SpringWebProject 項目.
並會自動開啟 Browser.
輸入網址 http://localhost:8080/SpringWebProject/customerlist.do
如下圖所示:


點擊 Insert Customer 並輸入資料:
如下圖所示:



參考文檔:
http://www.springframework.org/documentation
http://www.hibernate.org/5.html
http://www.hsqldb.org/web/hsqlDocsFrame.html
http://www.eclipse.org/webtools/