设为首页收藏本站

数码鹭岛论坛

 找回密码
 注-册

QQ登录

只需一步,快速开始

搜索
查看: 4334|回复: 0
打印 上一主题 下一主题

【资料分享】Hibernate + Struts 學習筆記

[复制链接]
跳转到指定楼层
1#
发表于 2003-11-12 15:56:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Hibernate + Struts 學習筆記
作者: Annhy
原文: http://www.jsptw.com/jute/post/view?bid=11&id=16968
  
寫在前面
  
最近開始學 Hibernate + Struts,大概是因為我資質駑鈍,
總覺得看的東西不算少(這要感謝各位前輩),但是寫起程式來就是不太順。
  
所以我想把一些心得與疑問貼在這裡,一則可以向各位請教,
再則也可以提供後來學習的人參考。
  
各位如果覺得我寫的有問題,還請一定要提出批評與指教,
有批評我才會進步,有指教我會進步得更快。
  
我會先從後端的 Hibernate 開始進行,然後再加上前端 Struts 的部分。
既然是沿路將相關的工作內容與疑問記下,可能會看起來有點混亂,
還請忍耐。希望我有恆心與毅力把它完成...
  
為了避免侵犯我們公司的智慧財產權,以及營業秘密等頭痛的問題,
我決定不使用目前公司所上線使用的系統當例子
(而且它的商業邏輯已經變得有點龐大,大到我不知道怎麼拿來當作教學文件了),
改用比較像是小玩具等級的程式當作範例程式,
之前的那個例子就讓它隨風而逝吧...
  
我參考的資料:
1. hibernate 快速入門
http://www.jsptw.com/jute/post/view?bid=11&id=3291&sty=1&tpg=1&age=-1
2. Introduction to Hibernate from theserverside
http://www.theserverside.com/resources/articles/Hibernate/IntroductionToHibernate.pdf
3. Hibernate2 Reference Documentation
http://www.hibernate.org/hib_docs/reference/pdf/hibernate_reference.pdf  
  
================================================================================
  
功能需求
  
我們要開發一個(非常陽春的)相片管理系統,它的功能非常的簡單。大致如下:
  
1. 提供對相片的 新增/修改/刪除/查詢 的功能。
2. 為了能夠比較方便的管理相片,我們要建立相片的分類 (也就是相簿),
  此分類為階層式的樹狀結構,也就是說 leaf node 為相簿,
  non-leaf node 為相簿之分類。
  (分類也要有 新增/修改/刪除/查詢 的功能)
3. 一個相簿中可以有多張相片,一張相片也可以同時歸類於不同的相簿中。
  
類別設計
  
根據剛剛所定義的需求,可以很快的定出兩個 domain class,那就是 Photo 與 Album。
  
1. Photo: 紀錄相片的相關資訊
  
Photo {
 id : Long;               // persistent 時所用的唯一識別碼
 fileName : String;       // 檔名
 title : String;          // 照片標題
 description : String;    // 照片說明
 albums : Set;            // 此照片所屬的相簿
}
  
Thinking 1:
 id 的資料型態用 Long,而不是 long,我看到的 Hibernate 範例幾乎都是這樣用的。
 這是因為 Long 可以有 null 值,用來判斷尚未被 Hibernate 存入資料庫的物件很方便。
 若用 long,0 或 -1 就可能比較容易會不小心與真正的值相衝突。
  
 另外,為什麼不用 Integer 呢?這我不知道,大概是怕 integer 的範圍不夠大,
 如果設計時沒考慮到,等到上線時爆掉,就很麻煩吧...
  
Thinking 2:
 因為 albums 中不應該出現重複的分類,所以用 Set 而不用其它的 Collection,
 可以避免發生不小心的情況。
 另外 albums 的排序,我想應該用分類的 id 來當作排序依據。
  
2. Album: 紀錄 Photo 的分類階層架構
  
Album {
 id : Long;             // persistent 時所用的唯一識別碼。
 title : String;        // 相簿標題
 description : String;  // 相簿說明
 photos : Set;          // 屬於此相簿之所有相片物件
 parent : Album;        // 上層相簿分類
 children : Set;        // 屬於此分類之下層相簿物件
}
  
Thinking 1:
 與 Photo.albums 相同的理由,所以用 Set。
 這是個雙向的 association,想不起來哪裡看過一個條款,
 它說如果雙向連結不好維護或效率很差,就把它改成單向的。
 不過既然 Hibernate 的範例是這樣寫,那我就先這樣用了,
 應該沒問題吧...
  
Thinking 2:
 因為整個相簿分類是一個樹狀結構,所以需要有 parent 與 children
 來記錄相互間的架構關係。
  
 這裡又扯出一個問題,那就是樹狀結構是否應該有一個共同的根?
 也就是說如果我有三大分類 [家人], [學生時代], [其它],
 我們是否應該為它們建立一個共同的父節點呢?
  
 我個人是比較傾向建立這樣的 (虛擬) 共同父節點 (就取為 "我的相簿" 好了),
 這樣寫程式時,可以減少許多判斷上的工作量。  
  
我的相簿
  |
  +--家人
  |  |
  |  +--親愛的老婆大人
  |  |
  |  +--寶貝女兒
  |  
  +--學生時代
  |  |
  |  +--大學
  |  |
  |  +--研究所
  |  
  +--其它
  
================================================================================
  
類別實作
  
因為 Album 與 Photo 是 domain objects,所以放在 annhy.photo.domains 這個 package 之下。
根據之前的類別設計,我們可以很容易地把它們實作出來,但它們目前只有 private data member 與
getter/setter methods,是不折不扣的 Java Bean。
  
這樣還不夠,為了要使它們更好用,我們還要加上一些東西:
  
a. 提供額外的建構式。除了預設建構式以外,再加上傳入一個字串參數 (fileName) 的建構式。
  
b. 覆寫 Object 類別的 toString(), equals(), hashcode() 等函式。
  
c. 實作 Comparable 介面,以及 compareTo() 函式。
  
d. 套用 Encapsulate Collection (封裝群集) 的重構手法。
  (註: 在 Refactoring 一書的 p.208 有一個 Encapsulate Collection (封裝群集) 的手法。
  按照它的說法,我不應該為整個群集 (例如: Photo.albums) 提供 getter/setter,
  而應該提供用以為群集 add/remove 元素的函式。所以我為這兩個 class 加上相對應的函式,
  但暫不將原有 getter/setter 移除,以免 Hibernate 發生危險。)
  
e. 提供 finder methods,以便搜尋物件。因為 finder methods 與 Hibernate
  的藕合度較高,所以把它們抽離出來,移至 Albums.java 與 Photos.java。(晚點再寫)
  
Photo.java:
  
package annhy.photo.domains;
import java.util.*;
  /**  
  * 用來表示 Photo 資料的物件。  
  *
  * @author Annhy  
  * @version 1.0, 2003/10/01  
  */
  
  public class Photo implements Comparable {
  
[$nbsp][$nbsp]//================
[$nbsp][$nbsp]//== Constructors
[$nbsp][$nbsp]//================
  
[$nbsp][$nbsp]public Photo() {
[$nbsp][$nbsp]}  
  
[$nbsp][$nbsp]// 為了方便起見,多加一個建構式
[$nbsp][$nbsp]public Photo(String fileName) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.fileName = fileName;
[$nbsp][$nbsp]}   
  
[$nbsp][$nbsp]//================   
[$nbsp][$nbsp]//== data members   
[$nbsp][$nbsp]//================   
[$nbsp][$nbsp]private Long id;
[$nbsp][$nbsp]private String fileName;
[$nbsp][$nbsp]private String title;
[$nbsp][$nbsp]private String description;
[$nbsp][$nbsp]private Set albums = new TreeSet();  // 有順序,所以用 TreeSet
  
[$nbsp][$nbsp]//================================
[$nbsp][$nbsp]//== getter & setter methods
[$nbsp][$nbsp]//================================
[$nbsp][$nbsp]public Long getId() {
[$nbsp][$nbsp][$nbsp][$nbsp]return id;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void setId(Long id) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.id = id;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public String getFileName() {
[$nbsp][$nbsp][$nbsp][$nbsp]return fileName;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void setFileName(String fileName) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.fileName = fileName;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public String getTitle() {
[$nbsp][$nbsp][$nbsp][$nbsp]return title;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void setTitle(String title) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.title = title;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public String getDescription() {
[$nbsp][$nbsp][$nbsp][$nbsp]return description;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void setDescription(String description) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.description = description;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public Set getAlbums() {
[$nbsp][$nbsp][$nbsp][$nbsp]return albums;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void setAlbums(Set albums) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.albums = albums;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]//====================
[$nbsp][$nbsp]//== override methods
[$nbsp][$nbsp]//====================
[$nbsp][$nbsp]/**
[$nbsp][$nbsp][$nbsp]* 覆寫 Object.toString()
[$nbsp][$nbsp][$nbsp]* @return debug 用的字串
[$nbsp][$nbsp][$nbsp]*/
  
[$nbsp][$nbsp]public String toString() {
[$nbsp][$nbsp][$nbsp][$nbsp]return "[Photo(" + id + "): " + fileName+ "]";
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]/**
[$nbsp][$nbsp][$nbsp]* 覆寫 Object.equals()
[$nbsp][$nbsp][$nbsp]* @param other 欲比較的另一個物件
[$nbsp][$nbsp][$nbsp]* @return 兩物件是否相等
[$nbsp][$nbsp][$nbsp]*/
[$nbsp][$nbsp]public boolean equals(Object other) {
[$nbsp][$nbsp][$nbsp][$nbsp]if (other == this) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]return true;
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp][$nbsp][$nbsp]if (!(other instanceof Photo)) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]return false;
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp][$nbsp][$nbsp]Photo that = (Photo) other;
[$nbsp][$nbsp][$nbsp][$nbsp]return this.id.equals(that.id);
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]/**
[$nbsp][$nbsp][$nbsp]* 覆寫 Object.hashCode(),這樣才能用於 hash 物件
[$nbsp][$nbsp][$nbsp]* @return 物件的 hash code
[$nbsp][$nbsp][$nbsp]*/
[$nbsp][$nbsp]public int hashCode() {
[$nbsp][$nbsp][$nbsp][$nbsp]return this.id.hashCode();
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]/**
[$nbsp][$nbsp][$nbsp]* 實作 Comparable.compareTo()
[$nbsp][$nbsp][$nbsp]* @param other 欲比較的另一個物件
[$nbsp][$nbsp][$nbsp]* @return 比較大小的結果
[$nbsp][$nbsp][$nbsp]*/
[$nbsp][$nbsp]public int compareTo(Object other) {
[$nbsp][$nbsp][$nbsp][$nbsp]Photo that = (Photo) other;
[$nbsp][$nbsp][$nbsp][$nbsp]return this.id.compareTo(that.id);
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]//====================================
[$nbsp][$nbsp]//== Encapsulate Collection (封裝群集)
[$nbsp][$nbsp]//====================================
[$nbsp][$nbsp]public void addAlbum(Album album) {
[$nbsp][$nbsp][$nbsp][$nbsp]albums.add(album);
[$nbsp][$nbsp][$nbsp][$nbsp]// 加入反向連結 (要先判斷!!)
[$nbsp][$nbsp][$nbsp][$nbsp]if (!album.getPhotos().contains(this)) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]album.addPhoto(this);
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void removeAlbum(Album album) {
[$nbsp][$nbsp][$nbsp][$nbsp]albums.remove(album);
[$nbsp][$nbsp][$nbsp][$nbsp]// 移除反向連結 (要先判斷!!)
[$nbsp][$nbsp][$nbsp][$nbsp]if (album.getPhotos().contains(this)) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]album.removePhoto(this);
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void clearAlbums() {
[$nbsp][$nbsp][$nbsp][$nbsp]// 記錄原來的 snapshot
[$nbsp][$nbsp][$nbsp][$nbsp]Album[] arrSnapshot = (Album[]) albums.toArray(new Album[0]);
[$nbsp][$nbsp][$nbsp][$nbsp]for (int i = 0; i < arrSnapshot.length; i++) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]removeAlbum(arrSnapshot[i]);
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp]}
}
  
Album.java:
  
package annhy.photo.domains;
import java.util.*;
/**
  * 用來表示相片分類 (相簿) 的物件。
  *
  * @author Annhy
  * @version 1.0, 2003/10/01
  */
  
public class Album implements Comparable {
[$nbsp][$nbsp]//================
[$nbsp][$nbsp]//== Constructors
[$nbsp][$nbsp]//================
  
[$nbsp][$nbsp]public Album() {
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]// 為了方便起見,多加一個建構式
[$nbsp][$nbsp]public Album(String title) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.title = title;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]//================
[$nbsp][$nbsp]//== data members
[$nbsp][$nbsp]//================
[$nbsp][$nbsp]private Long id;
[$nbsp][$nbsp]private String title;
[$nbsp][$nbsp]private String description;
[$nbsp][$nbsp]private Set photos = new TreeSet(); // 有順序,所以用 TreeSet
[$nbsp][$nbsp]private Album parent;
[$nbsp][$nbsp]private Set children = new TreeSet(); // 有順序,所以用 TreeSet
  
[$nbsp][$nbsp]//================================
[$nbsp][$nbsp]//== getter & setter methods
[$nbsp][$nbsp]//================================
[$nbsp][$nbsp]public Long getId() {
[$nbsp][$nbsp][$nbsp][$nbsp]return id;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void setId(Long id) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.id = id;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public String getTitle() {
[$nbsp][$nbsp][$nbsp][$nbsp]return title;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void setTitle(String title) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.title = title;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public String getDescription() {
[$nbsp][$nbsp][$nbsp][$nbsp]return description;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void setDescription(String description) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.description = description;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public Set getPhotos() {
[$nbsp][$nbsp][$nbsp][$nbsp]return photos;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void setPhotos(Set photos) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.photos = photos;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public Album getParent() {
[$nbsp][$nbsp][$nbsp][$nbsp]return parent;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void setParent(Album parent) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.parent = parent;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public Set getChildren() {
[$nbsp][$nbsp][$nbsp][$nbsp]return children;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void setChildren(Set children) {
[$nbsp][$nbsp][$nbsp][$nbsp]this.children = children;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]//====================
[$nbsp][$nbsp]//== override methods
[$nbsp][$nbsp]//====================
[$nbsp][$nbsp]/**
[$nbsp][$nbsp][$nbsp]* 覆寫 Object.toString()
[$nbsp][$nbsp][$nbsp]* @return debug 用的字串
[$nbsp][$nbsp][$nbsp]*/
  
[$nbsp][$nbsp]public String toString() {
[$nbsp][$nbsp][$nbsp][$nbsp]return "[Album(" + id + "): " + title + "]";
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]/**
[$nbsp][$nbsp][$nbsp]* 覆寫 Object.equals()
[$nbsp][$nbsp][$nbsp]* @param other 欲比較的另一個物件
[$nbsp][$nbsp][$nbsp]* @return 兩物件是否相等
[$nbsp][$nbsp][$nbsp]*/
[$nbsp][$nbsp]public boolean equals(Object other) {
[$nbsp][$nbsp][$nbsp][$nbsp]if (other == this) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]return true;
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp][$nbsp][$nbsp]if (!(other instanceof Album)) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]return false;
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp][$nbsp][$nbsp]Album that = (Album) other;
[$nbsp][$nbsp][$nbsp][$nbsp]return this.id.equals(that.id);
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]/**
[$nbsp][$nbsp][$nbsp]* 覆寫 Object.hashCode(),這樣才能用於 hash 物件
[$nbsp][$nbsp][$nbsp]* @return 物件的 hash code
[$nbsp][$nbsp][$nbsp]*/
[$nbsp][$nbsp]public int hashCode() {
[$nbsp][$nbsp][$nbsp][$nbsp]return this.id.hashCode();
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]/**
[$nbsp][$nbsp][$nbsp]* 實作 Comparable.compareTo()
[$nbsp][$nbsp][$nbsp]* @param other 欲比較的另一個物件
[$nbsp][$nbsp][$nbsp]* @return 比較大小的結果
[$nbsp][$nbsp][$nbsp]*/
  
[$nbsp][$nbsp]public int compareTo(Object other) {
[$nbsp][$nbsp][$nbsp][$nbsp]Album that = (Album) other;
[$nbsp][$nbsp][$nbsp][$nbsp]return this.id.compareTo(that.id);
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]//====================================
[$nbsp][$nbsp]//== Encapsulate Collection (封裝群集)
[$nbsp][$nbsp]//====================================
[$nbsp][$nbsp]public void addPhoto(Photo photo) {
[$nbsp][$nbsp][$nbsp][$nbsp]photos.add(photo);
[$nbsp][$nbsp][$nbsp][$nbsp]// 加入反向連結 (要先判斷!!)
[$nbsp][$nbsp][$nbsp][$nbsp]if (!photo.getAlbums().contains(this)) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]photo.addAlbum(this);
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void removePhoto(Photo photo) {
[$nbsp][$nbsp][$nbsp][$nbsp]photos.remove(photo);
[$nbsp][$nbsp][$nbsp][$nbsp]// 移除反向連結 (要先判斷!!)
[$nbsp][$nbsp][$nbsp][$nbsp]if (photo.getAlbums().contains(this)) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]photo.removeAlbum(this);
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void clearPhotos() {
[$nbsp][$nbsp][$nbsp][$nbsp]// 記錄原來的 snapshot
[$nbsp][$nbsp][$nbsp][$nbsp]Photo[] arrSnapshot = (Photo[])photos.toArray(new Photo[0]);
[$nbsp][$nbsp][$nbsp][$nbsp]for (int i = 0; i < arrSnapshot.length; i++) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]removePhoto(arrSnapshot[i]);
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void addChild(Album child) {
[$nbsp][$nbsp][$nbsp][$nbsp]children.add(child);    // 加入反向連結 (要先判斷!!)
[$nbsp][$nbsp][$nbsp][$nbsp]if (!this.equals(child.getParent())) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]child.setParent(this);
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void removeChild(Album child) {
[$nbsp][$nbsp][$nbsp][$nbsp]children.remove(child);
[$nbsp][$nbsp][$nbsp][$nbsp]// 移除反向連結 (要先判斷!!)
[$nbsp][$nbsp][$nbsp][$nbsp]if (this.equals(child.getParent())) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]child.setParent(null);
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public void clearChildren() {
[$nbsp][$nbsp][$nbsp][$nbsp]// 記錄原來的 snapshot
[$nbsp][$nbsp][$nbsp][$nbsp]Album[] arrSnapshot = (Album[]) children.toArray(new Album[0]);
[$nbsp][$nbsp][$nbsp][$nbsp]for (int i = 0; i < arrSnapshot.length; i++) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]removeChild(arrSnapshot[i]);
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp]}
}
  
目前檔案列表:
/annhy/photo/domains/Photo.java
/annhy/photo/domains/Album.java  
  
================================================================================
  
設定 Hibernate 相關檔案
  
class 初步實作好之後,就可以設定 Hibernate 相關的檔案了。
這裡有三個檔案要做: hibernate.cfg.xml, Photo.hbm.xml, Album.hbm.xml
(本來是使用 hibernate.properties,改用 hibernate.cfg.xml 可以有額外的功能,
感謝 browser 的指導)
  
hibernate.cfg.xml:
  
<?xml version="1.0" encoding="Big5"?>
<!DOCTYPE hibernate-configuration PUBLIC
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]"-//Hibernate/Hibernate Configuration DTD 2.0//EN"
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
<hibernate-configuration>
[$nbsp][$nbsp]<session-factory>
[$nbsp][$nbsp][$nbsp][$nbsp]<!-- session factory properties -->
[$nbsp][$nbsp][$nbsp][$nbsp]<property name="dialect">net.sf.hibernate.dialect.MySQLDialect</property>
[$nbsp][$nbsp][$nbsp][$nbsp]<property name="connection.driver_class">org.gjt.mm.mysql.Driver</property>
[$nbsp][$nbsp][$nbsp][$nbsp]<property name="connection.url">jdbc:mysql://localhost/photo_db?useUnicode=true&characterEncoding=Big5</property>
[$nbsp][$nbsp][$nbsp][$nbsp]<property name="connection.username">photo_db</property>
[$nbsp][$nbsp][$nbsp][$nbsp]<property name="connection.password">photo_db</property>
[$nbsp][$nbsp][$nbsp][$nbsp]<!-- domain object 的對應檔案 -->
[$nbsp][$nbsp][$nbsp][$nbsp]<mapping resource="annhy/photo/domains/Photo.hbm.xml"/>
[$nbsp][$nbsp][$nbsp][$nbsp]<mapping resource="annhy/photo/domains/Album.hbm.xml"/>
[$nbsp][$nbsp]</session-factory>
</hibernate-configuration>
  
這裡要注意的是這個特殊的字串: ?useUnicode=true&characterEncoding=Big5 ,
聽說這是因為 MySQL 不支援 UniCode,如果不加上這些,存入中文資料就會有問題。
還有,因為這是 XML 格式的檔案,所以 & 要替換為 &
(請自己自己換成半形!! 為了這個問題,浪費了我幾個小時...)
  
Photo.hbm.xml:
  
<?xml version="1.0" encoding="Big5"?>
<!DOCTYPE hibernate-mapping PUBLIC
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
<hibernate-mapping>
[$nbsp][$nbsp]<class name="annhy.photo.domains.Photo" table="photo">
[$nbsp][$nbsp][$nbsp][$nbsp]<id name="id" column="photo_id">
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<generator class="native" />
[$nbsp][$nbsp][$nbsp][$nbsp]</id>
[$nbsp][$nbsp][$nbsp][$nbsp]<property name="fileName">
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<column name="fileName" sql-type="text" />
[$nbsp][$nbsp][$nbsp][$nbsp]</property>
[$nbsp][$nbsp][$nbsp][$nbsp]<property name="title">
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<column name="title" sql-type="text" />
[$nbsp][$nbsp][$nbsp][$nbsp]</property>
[$nbsp][$nbsp][$nbsp][$nbsp]<property name="description">
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<column name="description" sql-type="text" />
[$nbsp][$nbsp][$nbsp][$nbsp]</property>
[$nbsp][$nbsp][$nbsp][$nbsp]<!-- Photo 與 Album 的 n:n 對應關係 -->
[$nbsp][$nbsp][$nbsp][$nbsp]<set name="albums" table="rel_album_photo" lazy="false" sort="natural">
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<key column="photo_id" />
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<many-to-many class="annhy.photo.domains.Album" column="album_id" />
[$nbsp][$nbsp][$nbsp][$nbsp]</set>
[$nbsp][$nbsp]</class>
</hibernate-mapping>
  
Album.hbm.xml:
  
<?xml version="1.0" encoding="Big5"?>
<!DOCTYPE hibernate-mapping PUBLIC
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
<hibernate-mapping>
[$nbsp][$nbsp]<class name="annhy.photo.domains.Album" table="album">
[$nbsp][$nbsp][$nbsp][$nbsp]<id name="id" column="album_id">
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<generator class="native" />
[$nbsp][$nbsp][$nbsp][$nbsp]</id>
[$nbsp][$nbsp][$nbsp][$nbsp]<property name="title">
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<column name="title" sql-type="text" />
[$nbsp][$nbsp][$nbsp][$nbsp]</property>
[$nbsp][$nbsp][$nbsp][$nbsp]<property name="description">
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<column name="description" sql-type="text" />
[$nbsp][$nbsp][$nbsp][$nbsp]</property>
[$nbsp][$nbsp][$nbsp][$nbsp]<!-- Photo 與 Album 的 n:n 對應關係 -->
[$nbsp][$nbsp][$nbsp][$nbsp]<set name="photos" table="rel_album_photo" inverse="true" lazy="false"
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]sort="natural">
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<key column="album_id" />
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<many-to-many class="annhy.photo.domains.Photo" column="photo_id" />
[$nbsp][$nbsp][$nbsp][$nbsp]</set>
[$nbsp][$nbsp][$nbsp][$nbsp]<!-- Album 自己的樹狀階層架構 -->
[$nbsp][$nbsp][$nbsp][$nbsp]<!-- 指向 parent -->
[$nbsp][$nbsp][$nbsp][$nbsp]<many-to-one name="parent" column="parent_id"
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]class="annhy.photo.domains.Album" />
[$nbsp][$nbsp][$nbsp][$nbsp]<!-- 指向 children -->
[$nbsp][$nbsp][$nbsp][$nbsp]<set name="children" inverse="true" lazy="false" sort="natural">
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<key column="parent_id" />
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<one-to-many class="annhy.photo.domains.Album" />
[$nbsp][$nbsp][$nbsp][$nbsp]</set>
[$nbsp][$nbsp]</class>
</hibernate-mapping>
  
這裡要注意的是:
1. 當使用了雙向連結,其中一個 class 必須要設定 inverse="true"
才行。至於是設定哪一邊的 class,似乎沒啥影響。
  
2. 因為在 MySQL 中 java.lang.String 預設對應為 varchar(255),
這個 size 通常不夠用,所以要用下面這種寫法,強制它對應到 text。
(感謝 chrischang 的指導)
  
[$nbsp][$nbsp][$nbsp][$nbsp]<property name="title">
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]<column name="title" sql-type="text" />
[$nbsp][$nbsp][$nbsp][$nbsp]</property>
  
這個 Album.hbm.xml 展示了 many-to-many 的關係,以及樹狀階層架構。
這兩種特殊用法,之前我都找不到範例,只好自己 try。
這就是我 try 出來的結果,還不錯,很直覺。
  
Thinking 1:
 在設定對應關係的地方,我都有設定 lazy="false"
 (不使用 Lazy Initialization),而且不能設定 class 的 proxy 屬性。
 這是因為之前我設定過這兩個選項時,如果在關閉 session 之後,才去讀取 photo.albums
 或 albums.children 這些 collection 就會產生 error。
  
 我知道 Hibernate 就是這樣設計的,但我不懂,難道 session 用完可以不關閉嗎?
 (手冊上面這麼描述 Session: A single-threaded, short-lived object ....)
 如果是 Web AP,每一個 HTTP request 都算是獨立的動作,這樣難道不會有問題?
 在我還搞不懂之前,我還是先不要亂用好了,反正在資料量不多的情況下,頂多只是效率比較差罷了...
  
 有沒有善心人士可以指點我一下...
  
目前檔案列表:
/annhy/photo/domains/Photo.java
/annhy/photo/domains/Album.java
/hibernate.cfg.xml
/annhy/photo/domains/Photo.hbm.xml
/annhy/photo/domains/Album.hbm.xml  
  
================================================================================
  
ThreadLocalSession.java, Photos.java, Albums.java
  
到這裡,已經寫好 domain class 與 Hibernate 相關檔案,我們已經有足夠的東西
來產生測試資料庫。
  
我要先提供一個用以取得 session 的公用類別,因為我有用到 web 的程式,
所以此公用類別必須是 thread-safe 的,於是我參考了
http://hibernate.bluemars.net/42.html
http://hibernate.bluemars.net/114.html
之後,使用 ThreadLocal 變數來解決。
  
ThreadLocalSession.java:
  
package annhy.photo.hibernate;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
  
public class ThreadLocalSession {
[$nbsp][$nbsp]// Hibernate 的設定環境物件,由 xml mapping 文件產生
  
[$nbsp][$nbsp]private static Configuration config;  // SessionFactory
[$nbsp][$nbsp]private static SessionFactory factory;  // ThreadLocal 變數,用來存放 ThreadLocalSession 物件
[$nbsp][$nbsp]private static final ThreadLocal sessionContext = new ThreadLocal();
[$nbsp][$nbsp]static {
[$nbsp][$nbsp][$nbsp][$nbsp]init();
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]private static final void init() {
[$nbsp][$nbsp][$nbsp][$nbsp]try {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]config = new Configuration().configure();
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]factory = config.buildSessionFactory();
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp][$nbsp][$nbsp]catch (HibernateException ex) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]ex.printStackTrace(System.err);
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]config = null;
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]factory = null;
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public static Session openSession() throws HibernateException {
[$nbsp][$nbsp][$nbsp][$nbsp]Session session = (Session) sessionContext.get();
[$nbsp][$nbsp][$nbsp][$nbsp]if (session == null) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]session = factory.openSession();
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]sessionContext.set(session);
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp][$nbsp][$nbsp]return session;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public static void closeSession() throws HibernateException {
[$nbsp][$nbsp][$nbsp][$nbsp]Session session = (Session) sessionContext.get();
[$nbsp][$nbsp][$nbsp][$nbsp]sessionContext.set(null);
[$nbsp][$nbsp][$nbsp][$nbsp]if (session != null) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]session.close();
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public static Configuration getConfig() {
[$nbsp][$nbsp][$nbsp][$nbsp]return config;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public static SessionFactory getFactory() {
[$nbsp][$nbsp][$nbsp][$nbsp]return factory;
[$nbsp][$nbsp]}
}
  
有了 ThreadLocalSession 之後,我就可以著手進行 Photos.java, Albums.java 了。
  
Photos.java:
  
package annhy.photo.domains;
import java.util.*;
import annhy.photo.hibernate.*;
import net.sf.hibernate.*;
  
/**
  * 用來處理 Photo 的相關 static 函式。
  *
  * @author Annhy
  * @version 1.0, 2003/10/01
  */
public class Photos {
[$nbsp][$nbsp]//==================
[$nbsp][$nbsp]//== finder methods
[$nbsp][$nbsp]//==================
[$nbsp][$nbsp]public static Photo findByPK(long id) throws HibernateException {
[$nbsp][$nbsp][$nbsp][$nbsp]return findByPK(new Long(id));
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public static Photo findByPK(Long id) throws HibernateException {
[$nbsp][$nbsp][$nbsp][$nbsp]Session s = ThreadLocalSession.openSession();
[$nbsp][$nbsp][$nbsp][$nbsp]Photo result = (Photo) s.load(Photo.class, id);
[$nbsp][$nbsp][$nbsp][$nbsp]ThreadLocalSession.closeSession();
[$nbsp][$nbsp][$nbsp][$nbsp]return result;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public static Collection findAll() throws HibernateException {
[$nbsp][$nbsp][$nbsp][$nbsp]Session s = ThreadLocalSession.openSession();
[$nbsp][$nbsp][$nbsp][$nbsp]Collection result = s.find("from " + Photo.class.getName() + " photo order by photo.id");
[$nbsp][$nbsp][$nbsp][$nbsp]ThreadLocalSession.closeSession();
[$nbsp][$nbsp][$nbsp][$nbsp]return result;
[$nbsp][$nbsp]}
}
  
Albums.java:
  
package annhy.photo.domains;
import java.util.*;
import annhy.photo.hibernate.*;
import net.sf.hibernate.*;
  
/**
  * 用來處理 Album 的相關 static 函式。
  *
  * @author Annhy
  * @version 1.0, 2003/10/01
  */
public class Albums {
[$nbsp][$nbsp]//==================
[$nbsp][$nbsp]//== finder methods
[$nbsp][$nbsp]//==================
[$nbsp][$nbsp]public static Album findByPK(long id) throws HibernateException {
[$nbsp][$nbsp][$nbsp][$nbsp]return findByPK(new Long(id));
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public static Album findByPK(Long id) throws HibernateException {
[$nbsp][$nbsp][$nbsp][$nbsp]Session s = ThreadLocalSession.openSession();
[$nbsp][$nbsp][$nbsp][$nbsp]Album result = (Album) s.load(Album.class, id);
[$nbsp][$nbsp][$nbsp][$nbsp]ThreadLocalSession.closeSession();
[$nbsp][$nbsp][$nbsp][$nbsp]return result;
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public static Collection findAll() throws HibernateException {
[$nbsp][$nbsp][$nbsp][$nbsp]Session s = ThreadLocalSession.openSession();
[$nbsp][$nbsp][$nbsp][$nbsp]Collection result = s.find("from " + Album.class.getName());
[$nbsp][$nbsp][$nbsp][$nbsp]ThreadLocalSession.closeSession();
[$nbsp][$nbsp][$nbsp][$nbsp]return result;
[$nbsp][$nbsp]}
}
  
目前檔案列表:
/annhy/photo/domains/Album.java
/annhy/photo/domains/Albums.java
/annhy/photo/domains/Photo.java
/annhy/photo/domains/Photos.java
/annhy/photo/hibernate/ThreadLocalSession.java
/hibernate.properties
/annhy/photo/domains/Photo.hbm.xml
/annhy/photo/domains/Album.hbm.xml  
  
================================================================================
  
利用 Hibernate 產生對應的資料庫
  
現在終於可以建立資料庫,並且新增測試資料了。
  
PhotoTester.java:
  
package annhy.photo;
import annhy.photo.domains.*;
import annhy.photo.hibernate.*;
import net.sf.hibernate.*;
import net.sf.hibernate.tool.hbm2ddl.*;
  
public class PhotoTester {
[$nbsp][$nbsp]public static void main(String[] args)
  throws HibernateException {
[$nbsp][$nbsp][$nbsp][$nbsp]// 產生 Database Schema Script 文件並建立資料庫
[$nbsp][$nbsp][$nbsp][$nbsp]generateDbSchemaScript(true);
[$nbsp][$nbsp][$nbsp][$nbsp]// 建立測試資料
[$nbsp][$nbsp][$nbsp][$nbsp]insertTestData();
[$nbsp][$nbsp][$nbsp][$nbsp]// 查詢測試資料
[$nbsp][$nbsp][$nbsp][$nbsp]assert Albums.findAll().size() == 8 : "應該有 8 個 Album";
[$nbsp][$nbsp][$nbsp][$nbsp]assert Photos.findAll().size() == 3 : "應該有 3 個 Photo";
[$nbsp][$nbsp][$nbsp][$nbsp]assert Photos.findByPK(1).getAlbums().size() == 1 : "Photo 1 歸屬於 1 個相簿";
[$nbsp][$nbsp][$nbsp][$nbsp]assert Photos.findByPK(3).getAlbums().size() == 2 : "Photo 3 歸屬於 2 個相簿";
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public static void generateDbSchemaScript(boolean affectToDb) throws
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]HibernateException {
[$nbsp][$nbsp][$nbsp][$nbsp]SchemaExport dbExport = new SchemaExport(ThreadLocalSession.getConfig());
[$nbsp][$nbsp][$nbsp][$nbsp]dbExport.setOutputFile("Photo_DB_Schema.sql");
[$nbsp][$nbsp][$nbsp][$nbsp]dbExport.create(false, affectToDb);
[$nbsp][$nbsp]}
  
[$nbsp][$nbsp]public static void insertTestData() throws HibernateException {
[$nbsp][$nbsp][$nbsp][$nbsp]Session s = ThreadLocalSession.openSession();
[$nbsp][$nbsp][$nbsp][$nbsp]Transaction t = s.beginTransaction();
[$nbsp][$nbsp][$nbsp][$nbsp]// create all catalog objects
[$nbsp][$nbsp][$nbsp][$nbsp]Album[] c = new Album[8];
[$nbsp][$nbsp][$nbsp][$nbsp]c[0] = new Album("我的相簿");
[$nbsp][$nbsp][$nbsp][$nbsp]c[1] = new Album("家人");
[$nbsp][$nbsp][$nbsp][$nbsp]c[2] = new Album("學生時代");
[$nbsp][$nbsp][$nbsp][$nbsp]c[3] = new Album("其它");
[$nbsp][$nbsp][$nbsp][$nbsp]c[4] = new Album("親愛的老婆大人");
[$nbsp][$nbsp][$nbsp][$nbsp]c[5] = new Album("寶貝女兒");
[$nbsp][$nbsp][$nbsp][$nbsp]c[6] = new Album("大學");
[$nbsp][$nbsp][$nbsp][$nbsp]c[7] = new Album("研究所");
[$nbsp][$nbsp][$nbsp][$nbsp]for (int i = 0; i < c.length; i++) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]s.save(c[i]);
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp][$nbsp][$nbsp]// create the catalog hierarchy
[$nbsp][$nbsp][$nbsp][$nbsp]c[0].addChild(c[1]);
[$nbsp][$nbsp][$nbsp][$nbsp]c[0].addChild(c[2]);
[$nbsp][$nbsp][$nbsp][$nbsp]c[0].addChild(c[3]);
[$nbsp][$nbsp][$nbsp][$nbsp]c[1].addChild(c[4]);
[$nbsp][$nbsp][$nbsp][$nbsp]c[1].addChild(c[5]);
[$nbsp][$nbsp][$nbsp][$nbsp]c[2].addChild(c[6]);
[$nbsp][$nbsp][$nbsp][$nbsp]c[2].addChild(c[7]);
[$nbsp][$nbsp][$nbsp][$nbsp]// 對整個物件網的 root 儲存動作,所有物件的更動都會存入資料庫
[$nbsp][$nbsp][$nbsp][$nbsp]s.save(c[0]);
[$nbsp][$nbsp][$nbsp][$nbsp]// create all photo objects
[$nbsp][$nbsp][$nbsp][$nbsp]Photo[] q = new Photo[3];
[$nbsp][$nbsp][$nbsp][$nbsp]q[0] = new Photo("c:\\images\\1.jpg");
[$nbsp][$nbsp][$nbsp][$nbsp]q[0].setTitle("大學畢業照!!");
[$nbsp][$nbsp][$nbsp][$nbsp]q[1] = new Photo("c:\\images\\2.jpg");
[$nbsp][$nbsp][$nbsp][$nbsp]q[1].setTitle("研究所畢業照!!");
[$nbsp][$nbsp][$nbsp][$nbsp]q[2] = new Photo("c:\\images\\3.jpg");
[$nbsp][$nbsp][$nbsp][$nbsp]q[2].setTitle("寶貝女兒滿月母女合照");
[$nbsp][$nbsp][$nbsp][$nbsp]for (int i = 0; i < q.length; i++) {
[$nbsp][$nbsp][$nbsp][$nbsp][$nbsp][$nbsp]s.save(q[i]);
[$nbsp][$nbsp][$nbsp][$nbsp]}
[$nbsp][$nbsp][$nbsp][$nbsp]// 建立 Photo 與 Album 的關聯
[$nbsp][$nbsp][$nbsp][$nbsp]c[6].addPhoto(q[0]);
[$nbsp][$nbsp][$nbsp][$nbsp]c[7].addPhoto(q[1]);
[$nbsp][$nbsp][$nbsp][$nbsp]q[2].addAlbum(c[4]);
[$nbsp][$nbsp][$nbsp][$nbsp]q[2].addAlbum(c[5]);
[$nbsp][$nbsp][$nbsp][$nbsp]// 再一次儲存全部物件
[$nbsp][$nbsp][$nbsp][$nbsp]s.save(c[0]);
[$nbsp][$nbsp][$nbsp][$nbsp]// 交易完成
[$nbsp][$nbsp][$nbsp][$nbsp]t.commit();
[$nbsp][$nbsp][$nbsp][$nbsp]ThreadLocalSession.closeSession();
[$nbsp][$nbsp]}
}
  
這裡建立測試資料的方式實在很醜,不知道有沒有人有比較好的建議。
不過要注意一點,若有兩個 domain object 要設定關聯性 (ex: obj1.addChild(obj2); ),
則至少其中之一要先用 Hibernate 存入 DB 中才行,不然會有 error。  
  
================================================================================
  
作者外出取材,敬請不必耐心等候..   
  
現在終於懂少年快報那些連載漫畫作者的心情了...
因為實在是江郎才盡,我真想掛個牌子 作者外出取材,敬請不必耐心等候 ,
然後就此消失,避不見面... 這樣會不會很惡劣...  
  
目前我還在努力搞懂 Struts 中  ,而且手上的工作快要做不完了 ,
先跟大家介紹我在 SourceForge 上找到的一個 project,
Java Struts-Polls http://sourceforge.net/projects/jpolls
我正在 trace 它的 code,它用到的技巧實在有夠多 (虐待我這個新手...),
有興趣可以一起討論。
要不是因為它沒什麼教學文件可看,不然我這個討論的 thread 就可以關閉了,
把它的教學文件連結過來就好了....
  
未完待續... but,有空再說了...
  
相关讨论:
http://www.jsptw.com/jute/post/view?bid=11&id=11048
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享
您需要登录后才可以回帖 登录 | 注-册

本版积分规则

小黑屋|手机版|Archiver|数码鹭岛 ( 闽ICP备20006246号 )  

counter

GMT+8, 2025-12-3 23:22 , Processed in 0.075358 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表