經過前面的兩篇文章,我相信大家對Hibernate與Structs都有了初步的了解。 在Hibernate開發指南之環境準備中我們說明了開發Hibernate和Struts的工具環境的使用及相關原理,并寫了一個簡單的Hibernate的小例子。 在Hibernate開發指南之Plugin to Struts中我們對Hibernate的SessionFactory的初始化與Struts結合的方法做了一些探討,讓大家對Struts的Plugin有了一定的理解,并且也仔細的了解了Hibernate的SesssionFactory的使用模式。 本文將進入一個具有很強操作性的實例中來,我們使用Struts的Action來通過Hibernate對數據庫進行增、刪、改、查四項操作。 希望大家通過本文,來了解以下知識:
Struts Action的使用及開發方法/模式
Hibernate的數據庫增、刪、改、查的編寫方法
Struts ActionForm Bean與Hibernate Maping Bean之間的轉換
Struts內容的國際化
本文將不會涉及深入的Struts中的tag lib中的應用,這方面的內容大家可以參考技術天空的Chinese Struts項目中的相關文檔。
首先,請按照Hibernate開發指南之環境準備來準備一個包含Hibernate和Struts的Web Project。 這個Web Project的名稱為:DBWebDemo。你可從這里下載這個空的Web Project,從這個項目來與我一起動手開始下面的工作。 你可以使用Eclipse的導入功能從zip文件中導入這個項目。
國際化資源文件
首先我們為站點準備一個國際化的資源文件,這個資源文件將是站點用戶所使用的語言來決定的,如果Struts不能找到相對應的語言資源文件,就會使用默認的資源文件。 這里我們先會建立一個txt文件,將所有的中文信息寫入,再通過一個批處理或是shell腳本將該txt轉化成為Struts所使用的資源文件。這個方法并不是很好,但是我還不知在Eclipse中是否有更好的國際化插件。 如果你知道,請告訴我,我試用后會將相關的使用加入本節中。
建立原始的信息文件
在DBWebDemo項目的WEB-INF文件夾中建立一個新的文件夾,名為res,并在其中建立一個新文件,名為res_zh.txt,同時新建一個批處理文件名為genres.bat和一個shell腳本文件名為genres.sh:
這里我們給出genres.bat的內容:
native2ascii -encoding GBK C:\...\web\WEB-INF\res\res_zh.txt > C:\...\DBWebDemo\src\...\ApplicationResources_zh_CN.properties
請使用你的文件的路徑來代替workspace之前的路徑。如果你有多種語言資源文件,加入多行,為每個文件準備一個encode參數,并使用該語言的后綴為ApplicationResources起名。
設置站點所使用的語言
我們使用一個Servlet的Filter來設置站點所使用的語言,我們可以將站點所使用的語言存在一個客戶端的cooke中,或是存于session中,還可以固定用戶必須使用中文。這里我們先固定用戶所使用的語言是中文。
建立SetEncodeFilter類
首先我們建立一個新的類名為SetEncodeFilter:
Filter接口會要求實現類實現以下方法:
init:初始化Filter時所做的工作
destroy:銷毀Filter時所做的工作
doFilter:當有Servlet初調用時過濾的方法
我們先不寫這三個方法,先將這個Filter配置到Web應用中去。
配置Web應用使用Filter
首先我們打開web.xml,在web-app的內容中加入filter的聲明:
<filter>
<filter-name>Set Web Application Character Encoding</filter-name>
<filter-class> com.huangdong.dbwebdemo.SetEncodeFilter</filter-class>
<init-param>
<param-name>defaultencoding</param-name>
<param-value>gb2312</param-value>
</init-param>
</filter>
這里我們設置了過濾器的名稱和它的具體實現的類,還有就是為這個類配置了一個參數,參數名為defaultencoding,它的值為gb2312。
為了使用這個過濾器,我們還要設置過濾器的作用范圍,在filter的聲明后加入以下內容:
<filter-mapping>
<filter-name>Set Web Application Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
這里我們使用了/*這個通配符,意思是所有的servlet請求都會使用這個過濾器。接下來,我們來書寫filter的實現代碼。
實現設置語言內容過濾器
以下是過濾器的代碼內容:
package com.huangdong.dbwebdemo;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* @author HD
*/
public class SetEncodeFilter implements Filter {
/**
* 本過濾器的配置信息
*/
protected FilterConfig filterConfig = null;
/**
* 系統缺省的語言編碼
*/
protected String defaultencoding = null;
/**
* 初始化過濾器
*/
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
// 獲取系統缺省語言編碼
this.defaultencoding = filterConfig.getInitParameter("defaultencoding");
}
/**
* 過濾系統請求,設置請求的語言編碼
*/
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding(selectEncoding(request));
chain.doFilter(request, response);
}
/**
* 過濾器銷毀
*/
public void destroy() {
this.defaultencoding = null;
this.filterConfig = null;
}
/**
* 選擇這次過濾所使用的語言編碼
* @param request 本次servlet請求的用戶request實例
* @return 選擇出的語言編碼
*/
protected String selectEncoding(ServletRequest request) {
// 可以在這里加入從request中取得session并從session中獲取用戶所選擇的encode
// 也可以在這里加入從request取得用戶客戶端的encode來設置當前的語言編碼
return (this.defaultencoding);
}
}
代碼就不再一一解釋了,里面的注釋也說的很清楚了。
加入Hibernate的相關配置
SessionFactory的初始化Struts Plugin
具體的加入方法這里就不再說明,請大家參考Hibernate開發指南之Plugin to Struts中的說明,這里只是列出代碼:
package com.huangdong.dbwebdemo;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.cfg.Configuration;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ModuleConfig;
/**
* @author HD
*/
public class InitHibernatePlugin implements PlugIn {
/**
* 插件所獲取的容器上下文實例
*/
private Context ctx;
/**
* Hibernate初始化好的SessionFactory
*/
private SessionFactory sessionFactory;
/*
* 插件銷毀方法
*/
public void destroy() {
if (ctx != null) {
try {
// unbind JNDI 節點
ctx.unbind("HibernateSessionFactory");
} catch (NamingException e) {
e.printStackTrace();
}
}
if (this.sessionFactory != null) {
try {
// 關閉sessionFactory
sessionFactory.close();
} catch (HibernateException e) {
e.printStackTrace();
}
this.sessionFactory = null;
}
}
/*
* 插件初始化方法
*/
public void init(ActionServlet servlet, ModuleConfig config)
throws ServletException {
try {
// 獲取SessionFactory的實例
this.sessionFactory =
new Configuration().configure().buildSessionFactory();
} catch (HibernateException ex) {
throw new RuntimeException(
"Exception building SessionFactory: " + ex.getMessage(),
ex);
}
try {
// 取得容器上下文
ctx = new InitialContext();
// 將sessionFactory bind到JND樹中
ctx.bind("HibernateSessionFactory", this.sessionFactory);
} catch (NamingException ex) {
throw new RuntimeException(
"Exception binding SessionFactory to JNDI: " + ex.getMessage(),
ex);
}
}
}
session獲取工具類
另外為了配置Plugin的使用,我們還是使用一個與HibernateUtilPlus相同功能的類名為DBUtil類,以下為它的代碼:
package com.huangdong.dbwebdemo;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
/**
* @author HD
*/
public class DBUtil {
private static SessionFactory sessionFactory = null;
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession() throws HibernateException {
if (sessionFactory == null) {
// 如果sessionFactory實例為null則從JNDI中獲取
if (getSystemSessionFactory() == false) {
throw new HibernateException("Exception geting SessionFactory from JNDI ");
}
}
Session s = (Session) session.get();
if (s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
session.set(null);
if (s != null)
s.close();
}
private static boolean getSystemSessionFactory() {
try {
//從JNDI中取得SessionFactory的實例,如果出錯返回false
Context inttex = new InitialContext();
sessionFactory =
(SessionFactory) inttex.lookup("HibernateSessionFactory");
} catch (NamingException e) {
return false;
}
return true;
}
}
Hibernate的配置文件
不要忘記我們還要將Hibernate的配置文件寫好(以下部分可以參見Hibernate開發指南之環境準備中相關的內容):
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="dialect">net.sf.hibernate.dialect.Oracle9Dialect</property>
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.username">hd</property>
<property name="connection.password">abc</property>
<property name="connection.url">jdbc:oracle:thin:@hdibm:1521:hdorc</property>
<property name="connection.pool.size">1</property>
<property name="statement_cache.size">25</property>
<property name="jdbc.fetch_size">50</property>
<property name="jdbc.batch_size">30</property>
<property name="show_sql">true</property>
<!-- Mapping files -->
<mapping resource="com/huangdong/demo/dao/SysUser.hbm.xml"/>
</session-factory>
</hibernate-configuration>
設置數據表實體化映射
這里我們還是使用之前幾篇文章的Oracle數據庫。所以連接配置都是差不多的。我們這次的增、刪、改、查還是使用SYSUSER表,所以還是使用顯示原來的map文件。以下是SysUser類的源代碼:
package com.huangdong.dbwebdemo.dao;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
/** @author Hibernate CodeGenerator */
public class SysUser implements Serializable {
/** identifier field */
private String userid;
/** persistent field */
private String username;
/** persistent field */
private String userpasword;
/** nullable persistent field */
private java.util.Calendar lastlogin;
/** full constructor */
public SysUser(
java.lang.String username,
java.lang.String userpasword,
java.util.Calendar lastlogin) {
this.username = username;
this.userpasword = userpasword;
this.lastlogin = lastlogin;
}
/** default constructor */
public SysUser() {
}
/** minimal constructor */
public SysUser(java.lang.String username, java.lang.String userpasword) {
this.username = username;
this.userpasword = userpasword;
}
public java.lang.String getUserid() {
return this.userid;
}
public void setUserid(java.lang.String userid) {
this.userid = userid;
}
public java.lang.String getUsername() {
return this.username;
}
public void setUsername(java.lang.String username) {
this.username = username;
}
public java.lang.String getUserpasword() {
return this.userpasword;
}
public void setUserpasword(java.lang.String userpasword) {
this.userpasword = userpasword;
}
public java.util.Calendar getLastlogin() {
return this.lastlogin;
}
public String getLastloginstr() {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return df.format(this.lastlogin);
}
public void setLastlogin(java.util.Calendar lastlogin) {
this.lastlogin = lastlogin;
}
public String toString() {
return new ToStringBuilder(this)
.append("userid", getUserid())
.toString();
}
public boolean equals(Object other) {
if (!(other instanceof SysUser))
return false;
SysUser castOther = (SysUser) other;
return new EqualsBuilder()
.append(this.getUserid(), castOther.getUserid())
.isEquals();
}
public int hashCode() {
return new HashCodeBuilder().append(getUserid()).toHashCode();
}
}
還有映射的xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="com.huangdong.dbwebdemo.dao.SysUser" table="SYSUSER">
<id column="userid" length="32" name="userid" type="string">
<generator class="uuid.hex"/>
</id>
<property column="username" length="20" name="username" not-null="true" type="string" unique="true"/>
<property column="userpasword" length="20" name="userpasword" not-null="true" type="string"/>
<property column="lastlogin" length="7" name="lastlogin" type="calendar"/>
</class>
</hibernate-mapping>
建立Struts的Action和Action FormBean
建立FormBean
在MyEclipse和Easy Struts中建立一個FormBean都是很簡單的,首先打開struts-config.xml文件,在大綱中選擇form-beans,新建:
然后在表單中按下圖輸入:
這樣我們就在com.huangdong.dbwebdemo.form包中新建了SysuserForm類。我們來為它加入相關的屬性,并設置geter和seter,以下是代碼:
package com.huangdong.dbwebdemo.form;
import org.apache.struts.action.ValidatorForm;
public class SysuserForm extends ValidatorForm {
// --------------------------------------------------------- Instance Variables
/** 用戶ID,只讀 */
private String userid;
/** 用戶名 */
private String username;
/** 用戶密碼 */
private String userpasword;
/** 最后登錄時間 */
private String lastlogin;
public SysuserForm() {
super();
}
public SysuserForm(SysUser sysuser) {
this.newSysuserForm(
sysuser.getUserid(),
sysuser.getUsername(),
sysuser.getUserpasword(),
sysuser.getLastloginstr());
}
public SysuserForm(
String userid,
String username,
String userpasword,
String lastlogin) {
this.newSysuserForm(userid, username, userpasword, lastlogin);
}
// --------------------------------------------------------- Methods
/**
* 為一個新的SysuserForm填充數據
*/
private void newSysuserForm(
String userid,
String username,
String userpasword,
String lastlogin) {
this.userid = userid;
this.username = username;
this.userpasword = userpasword;
this.lastlogin = lastlogin;
}
/**
* @return 最后登錄時間
*/
public String getLastlogin() {
return lastlogin;
}
/**
* @return 用戶ID
*/
public String getUserid() {
return userid;
}
/**
* @return 用戶名
*/
public String getUsername() {
return username;
}
/**
* @return 用戶密碼
*/
public String getUserpasword() {
return userpasword;
}
/**
* @param string 最后登錄時間,格式為YYYY-MM-DD hh:mm:ss
*/
public void setLastlogin(String string) {
lastlogin = string;
}
/**
* @param string 用戶名
*/
public void setUsername(String string) {
username = string;
}
/**
* @param string 用戶密碼
*/
public void setUserpasword(String string) {
userpasword = string;
}
/**
* 對提交的表單進行校驗
*/
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
ActionErrors errors = super.validate(mapping, request);
return errors;
}
}
這里我們看到我們該FormBean加入了數據校驗的功能,在下面會仔細說明。同時為了與Hibernate的數據庫進行轉換加入了相關的構造器和私有方法。
建立第一個Action
在struts-config.xml中加入action的定義,如下:
<action name="sysuserForm"
path="/sysuser"
type="com.huangdong.dbwebdemo.action.SysuserAction"
scope="request"
input="/sysuser/editsysuser.jsp">
<forward name="create" path="/sysuser/editsysuser.jsp"/>
</action>
這里我們來說明一下,name和attribute說明了本action所使用的FormBean的名稱, path指定了調用action的URL頭,type則指明了該action實現的具體類, scope說明了FormBean所存活的區域,而input則說明了輸入數據的View的地址(實質上是校驗有問題里指到的View)。 在最后的forward定義中則定義了一系列的action可能會用到的View指向,這里我們只指定了一個,后面會一個個的加入進來。
接著在com.huangdong.dbwebdemo.action包中新建SysuserAction類:
package com.huangdong.dbwebdemo.action;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.util.MessageResources;
import com.huangdong.dbwebdemo.form.SysuserForm;
public class SysuserAction extends Action {
// --------------------------------------------------------- Instance Variables
// --------------------------------------------------------- Methods
/**
* Method execute
* @param ActionMapping mapping
* @param ActionForm form
* @param HttpServletRequest request
* @param HttpServletResponse response
* @return ActionForward
* @throws Exception
*/
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
// 初始化系統語言資源
Locale locale = getLocale(request);
MessageResources messages = getResources(request);
// 本action默認接收的處理請求為創建一個新的SYSUSER
String action = "create";
return (mapping.findForward(action));
}
}
這里頭兩句代碼是用于設置View所使用的本地消息資源文件語言代碼的。
將VO與PO關聯
VO與PO的關系
VO即業務層的數據表示,而PO即持久層的數據表示。VO會在View和業務處理時大量使用,也就是說,所有沒有入庫前的數據都會存儲于一個個的VO中。而PO則是數據庫在Java中的持久數據結構。
有許多人喜歡將Struts的VO與Hibernate的PO合并起來,我不同意,原因很多,最重要的有以下幾點:
VO有自己的數據屬性,同時因框架的不同可能會有自己的結構和方法,在Struts中我喜歡用FormBean來做VO,它就是擴展ActionForm的一個類
VO中還會有大量的業務操作方法,如校驗、自動生成等方法
PO中會包含數據集之間的關系,如數據庫中的關系也會體現在PO中的一對一、多對多、一對多等,而在VO中不一定關注這樣的細節
總之,我更喜歡使用一個或多個關聯的類將業務邏輯中的VO與PO對映起來,實現VO到PO的轉換,以及PO中VO的取出。
VO與PO操作的抽像類
所有VO到PO的操作基本上都會是持久層數據的存入或更改(刪除)。這樣的操作一定會涉及到數據庫的事務操作。另一方面,PO到VO的數據取出涉及到的則是數據集合的緩沖、分頁、過涉等技巧。所以我們為這兩種情況聲明兩個抽像類:
AbsBaseMap類主要完成VO到PO的數據操作:
package com.huangdong.dbwebdemo.db;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import com.huangdong.dbwebdemo.DBUtil;
/**
* 系統VO與PO的操作映射器抽像類,完成數據庫事務和連接的初始化以及數據庫事務的提交及數據庫連接的關閉
* @author HD
*/
public abstract class AbsBaseMap {
// 數據庫連接session
private Session session;
// 數據庫事務處理器
private Transaction transaction;
/**
* 初始化數據庫連接事務
* @return 初始化完成的數據庫連接
* @throws HibernateException
*/
public Session beginTransaction() throws HibernateException {
session = DBUtil.currentSession();
transaction = session.beginTransaction();
return session;
}
/**
* 完成一個數據庫事務
* @param commit 是否提交事務,true時提交,false時向數據庫發起回滾(rollback)
* @throws HibernateException
*/
public void endTransaction(boolean commit) throws HibernateException {
if (commit) {
transaction.commit();
} else {
transaction.rollback();
}
DBUtil.closeSession();
}
}
AbsQueryMap類則主要提供了有關持久層數據的查詢的抽像方法:
package com.huangdong.dbwebdemo.db;
/**
* 系統VO與PO的查詢映射抽像類,加入查詢的分頁相關設置
* @author HD
*/
public class AbsQueryMap {
/**
* 數據庫連接session
**/
Session session;
// 分頁為20
int pagesize = 20;
// 當前頁數
int pageno = 1;
/**
* @return 分頁行數大小(默認為20)
*/
public int getPagesize() {
return pagesize;
}
/**
* @param i 設置分頁行數大小
*/
public void setPagesize(int i) {
pagesize = i;
}
/**
* @return 返回當前頁號,初始值為1
*/
public int getPageno() {
return pageno;
}
/**
* @param i 設置當前頁號
*/
public void setPageno(int i) {
pageno = i;
}
/**
* 設置查詢分頁
*/
public void setQueryPage(Query query) {
// 設置分頁起始記錄號
query.setFirstResult((this.pageno - 1) * this.pagesize);
// 設置頁內數據量
query.setMaxResults(this.pagesize);
}
/**
* 打開當前的數據庫連接
* @return
* @throws HibernateException
*/
public void initSession() throws HibernateException {
this.session = DBUtil.currentSession();
}
/**
* 關閉當前的數據庫連接
* @throws HibernateException
*/
public void closeSession() throws HibernateException {
DBUtil.closeSession();
}
}
以后,我們所有的數據庫更新操作都會繼承AbsBaseMap類,而數據庫查詢操作會繼承AbsQueryMap類。
數據庫增、刪、改操作映射
一旦有了上節所提供的AbsBaseMap抽像類和Hibernate所提供的功能,我們只需要了解一點點Java的知識就可以完成復雜的數據庫更新功能了。
首先在com.huangdong.dbwebdemo.db包中新建一個類名為SysUserMap并擴展AbsBaseMap類:
package com.huangdong.dbwebdemo.db;
import com.huangdong.dbwebdemo.form.SysuserForm;
public class SysUserMap extends AbsBaseMap {
}
先來實現一個增加的方法,在VO層,我們使用的數據類為SysuserForm,所以增加方法的參數一定是SysuserForm:
public void createSysUser(SysuserForm sysuerForm)
throws HibernateException {
// 使用sysuerForm的相關屬性新建sysuser實例
SysUser sysuser =
new SysUser(
sysuerForm.getUsername(),
sysuerForm.getUserpasword(),
Calendar.getInstance());
// 啟動事務
Session session = this.beginTransaction();
try {
// 新增這個實例到數據庫中
session.save(sysuser);
// commit
this.endTransaction(true);
} catch (HibernateException e) {
// rollback
this.endTransaction(false);
throw e;
}
}
這個方法已經非常的簡單了,書寫者完全可以不用理會數據庫相關的內容了。
接著我們來寫更新的代碼,相比之下一樣的簡單:
/**
* 更新一個sysuser
* @param sysuerForm 包含更新內容的sysuser form bean
* @throws HibernateException
*/
public void updateSysUser(SysuserForm sysuerForm)
throws HibernateException {
// 使用sysuerForm的相關屬性新建sysuser實例
SysUser sysuser =
new SysUser(
sysuerForm.getUsername(),
sysuerForm.getUserpasword(),
Calendar.getInstance());
// 啟動事務
Session session = this.beginTransaction();
try {
// 新增這個實例到數據庫中
session.update(sysuser);
// commit
this.endTransaction(true);
} catch (HibernateException e) {
// rollback
this.endTransaction(false);
}
}
下面我們來寫一個內容更多一些的代碼--刪除。刪除一般都會是使用用戶ID來刪除,所以參數可以是帶有用戶ID的formbean或是用戶ID,我們使用兩個方法來實現刪除:
/**
* 依據一個sysuser formbean來刪除該用戶
* @param sysuerForm 包含該用戶信息的Form Bean
* @throws HibernateException
*/
public void deleteSysUser(SysuserForm sysuerForm)
throws HibernateException {
String userid = sysuerForm.getUserid();
this.deleteSysUser(userid);
}
/**
* 依據用戶ID來刪除該用戶
* @param userid 用戶ID
* @throws HibernateException
*/
public void deleteSysUser(String userid) throws HibernateException {
Session session = this.beginTransaction();
try {
// 先向數據庫中查詢是否有這個用戶
SysUser sysuser = (SysUser) session.load(SysUser.class, userid);
// 干掉它!
session.delete(sysuser);
this.endTransaction(true);
} catch (HibernateException e) {
this.endTransaction(false);
}
}
數據庫簡單查詢
數據庫的查詢相對復雜一些了,我們從簡單做起,先不使用任何備件,查詢數據庫中所有的記錄。在com.huangdong.dbwebdemo.db中新建SysUserQueryMap類,它擴展AbsQueryMap抽像類:
package com.huangdong.dbwebdemo.db;
import java.util.Iterator;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Query;
/**
* @author HD
*/
public class SysUserQueryMap extends AbsQueryMap {
public SysUserQueryMap() throws HibernateException {
this.initSession();
}
public Iterator findAllSysUser() throws HibernateException {
// 查詢語句
String querystr = "from SysUser";
// 創建查詢
Query query = this.session.createQuery(querystr);
// 設置分頁
this.setQueryPage(query);
// 返回查詢出的結果集
return query.iterate();
}
}
這里我們已經寫好一個查詢所有的用戶的方法,它的第一句:
String querystr = "from SysUser";
這里的查詢語句使用了Hibernate的HQL語法,別的我們先不用管,這里SysUser是區分大小寫的,我們之前定義了SysUser數據庫映射類,這里必須完全一樣,這樣Hibernate就會從數據庫中取出所有SysUser類的實例。
接下來我們還需要一個方法能夠按照用戶的id返回其所對應的用戶。
/**
* 查詢出一個UserID的用戶實例
* @param UserID 用戶的UserID
* @return 用戶實例,如果數據庫無相應記錄返回null
* @throws HibernateException
*/
public SysuserForm getSysuserByID(String UserID)
throws HibernateException {
SysuserForm sysuerform = null;
try {
sysuerform =
new SysuserForm(
(SysUser) this.session.load(SysUser.class, UserID));
} catch (HibernateException e) {
throw e;
}
return sysuerform;
}
有了這個方法,我們才能對指定用戶進行查詢,或者對他已有的信息進行修改。
創建第一個View
新建JSP頁面
我們先為增加記錄建立一個JSP頁面,它提供了增加記錄的View。
<head>
<html:base />
<title><bean:message key="title.register"/></title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<logic:notEmpty name="sysuserForm" property="username" scope="request">
<html:errors />
</logic:notEmpty>
<html:form action="/sysuser.do" method="post" focus="login" onsubmit="return validateSysuserForm(this);">
<table border="0">
<tr>
<td>
<bean:message key="prompt.login"/>
</td>
<td>
<html:text property="username" size="16" maxlength="16"/>
</td>
</tr>
<tr>
<td>
<bean:message key="prompt.password"/>
</td>
<td>
<html:password property="userpasword" />
</td>
</tr>
<logic:notEmpty name="sysuserForm" property="lastlogin" scope="request">
<td>
<html:hidden property="lastlogin" write="true"/>
</td>
</logic:notEmpty>
<tr>
<td colspan="2" align="center">
<logic:empty name="sysuserForm" property="userid" scope="request">
<input type="hidden" name="act" value="add"/>
</logic:empty>
<logic:notEmpty name="sysuserForm" property="userid" scope="request">
<input type="hidden" name="act" value="update"/>
</logic:notEmpty>
<html:submit>
<bean:message key="button.submit"/>
</html:submit>
</td>
</tr>
</table>
</html:form>
<html:javascript formName="sysuserForm"
dynamicjavascript="true"
staticjavascript="false"/>
<script language="javascript1.1" src="../staticjavascript.jsp"/></script>
</body>
</html:html>
有關這個JSP頁面中的各種taglib的使用在這里我們就不深入講解, 你可以到技術天空的Chinese Struts項目網站了解這些tag的使用。 我們只對一些我們關心的內容做一些說明。
首先我們使用了html:errors這個tag來顯示可能出現的錯誤信息, 在后面我們的Action加工中我們會向errors中加入我們的數據校驗、后臺操作產生的錯誤,在這里通過這個tag來顯示出來。 這個表單的action="/sysuser.do"說明提交還是提交到了sysuser這個action,因為具體的增加方法也會是在這個action中完成。 表單的屬性最后有一個onsubmit="return validateSysuserForm(this);",是用于驗證每個輸入條目的正確的,具體這里的定義我們會在稍后一些說明。 在表單的最后我們使用logic這個tag來判斷sysuserForm中的userid是否存在,在確定表單中act這個參數的默認值是add還是edit。 這么做的原因是我們還將使用這個jsp用于用戶信息的編輯。 在整個頁面的最后,我們使用了html:javascript這個tag將sysuserForm所使用到的校驗javascript生成出來。 為了優化資源的使用,我們將所有的校驗javascript放在一個jsp中,也在最后使用javascript的引用標簽引用進來了。
View公用的校驗javascript代碼頁
下面我們列出staticjavascript.jsp的內容:
<%@ page language="java" %>
<%-- set document type to javascript (addresses a bug in Netscape according to a web resource --%>
<%@ page contentType="application/x-javascript" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html:javascript dynamicjavascript="false" staticjavascript="true"/>
這個JSP則是使用了struts所帶的校驗組件來生成了校驗所使用的所有javascript。
將業務邏輯關聯起來
為Action加入處理
這個Action會處理的是與SYSUSER相關的所有操作,所以action會接收到的請求和顯示的表單會有多個,我們在execute方法中加入以下的代碼:
if (action.equals("create")) {
// 如果處理請求類型為create
request.setAttribute("sysuserForm", new SysuserForm());
action = "create";
} else if (action.equals("add")) {
// 如果處理請求類型為add
SysUserMap sysusermap = new SysUserMap();
try {
sysusermap.createSysUser(sysuser);
// 加入成功,我們先再接著加下一個用戶
request.setAttribute("sysuserForm", new SysuserForm());
action = "create";
} catch (HibernateException e) {
// 數據庫存入出錯
ActionErrors errors = new ActionErrors();
errors.add(
ActionErrors.GLOBAL_ERROR,
new ActionError("error.database.save", e.toString()));
this.saveErrors(request, errors);
//將當前數據回顯,并顯示讓用戶重新輸入
request.setAttribute("sysuserForm", sysuser);
action = "create";
}
這個業務操作很簡單,主要是調用Map來將VO的數據存入PO中,完成一個向數據庫中增加一條記錄的功能。 我們的具體實現是先創建了一個新的FormBean來存放用戶的輸入,之后將FormBean中的數據存入數據庫中。 在存入時如果出現錯誤,我們將這個exception的內容打入error中,在jsp中去顯示(可以回頭看看jsp中的內容)。
接下來,我們完成刪、改、查的功能,這些操作也是很簡單的。
if (action == null || action.equals("list")) {
//如果處理請求類型為list
SysUserQueryMap sqm = new SysUserQueryMap();
Iterator sysuserlist = sqm.findAllSysUser();
request.setAttribute("sysuserlist", sysuserlist);
action = "list";
}
這段代碼定義了在默認的情況下或者請求list操作的時候,完成列出所有用戶的操作。 實際就是調用了我們在前面完成的SysUserQueryMap中的findAllSysUser的方法從數據庫中取出了所有的用戶。 之后將用戶列表壓入了bean中。
if (action.equals("edit")) {
// 請求類型為edit
SysUserQueryMap sqm = new SysUserQueryMap();
String uid = request.getParameter("userid");
request.setAttribute("sysuserForm",sqm.getSysuserByID(uid));
} else if (action.equals("update")){
// 請求類型為update
SysUserMap sysusermap = new SysUserMap();
sysusermap.updateSysUser(sysuser);
action = "list";
}
這段代碼完成了編輯用戶數據的工作。我們可以看出首先需要完成一個edit工作, 這個操作的作用是從數據庫中取得這個用戶的信息,然后將這個用戶信息壓入FormBean中,供頁面進行顯示。 在用戶對信息進行更改之后則完成update操作,對數據庫條目進行更新。
if (action.equals("delete")) {
// 如果處理請求類型為delete
SysUserMap sysusermap = new SysUserMap();
String uid = request.getParameter("userid");
try {
sysusermap.deleteSysUser(uid);
} catch (HibernateException e) {
ActionErrors errors = new ActionErrors();
errors.add(
ActionErrors.GLOBAL_ERROR,
new ActionError("error.database.delete", e.toString()));
this.saveErrors(request, errors);
}
action = "list";
}
這段代碼完成刪除的功能。刪除指定的條目。如果出錯就壓一個錯誤到全局的錯誤中。
對表單輸入的數據做校驗
在struts中帶有了Jakarta Commons中的一個叫做Validator的數據校驗框架。 使用起來也是非常的簡單。
首先將struts提供的validator-rules.xml的配置文件放入WEB-INF中。
然后在struts-config.xml中加入validation的plugin聲明:
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
</plug-in>
我們會看到還有一個validator.xml文件不存在,在WEB-INF中新建這個xml文件,我們需要在其中加入自己的校驗信息:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE form-validation PUBLIC
"-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN"
"http://jakarta.apache.org/commons/dtds/validator_1_0.dtd">
<form-validation>
<formset>
<form name="sysuserForm">
<field property="username"
depends="required, minlength,maxlength">
<arg0 key="prompt.login"/>
<arg1 key="${var:minlength}" name="minlength"
resource="false"/>
<arg2 key="${var:maxlength}" name="maxlength"
resource="false"/>
<var>
<var-name>maxlength</var-name>
<var-value>16</var-value>
</var>
<var>
<var-name>minlength</var-name>
<var-value>3</var-value>
</var>
</field>
<field property="userpasword"
depends="required, minlength,maxlength">
<arg0 key="prompt.password"/>
<arg1 key="${var:minlength}" name="minlength"
resource="false"/>
<arg2 key="${var:maxlength}" name="maxlength"
resource="false"/>
<var>
<var-name>maxlength</var-name>
<var-value>16</var-value>
</var>
<var>
<var-name>minlength</var-name>
<var-value>3</var-value>
</var>
</field>
</form>
</formset>
</form-validation>
仔細的看看這個xml文件的內容就會發現它說明要校驗的表單名為sysuserForm, 并對這個表單中的幾個需要做校驗的字段一一做了校驗方法說明。 具體Validator的使用請參考它帶的相關文檔和struts的相關說明。
注意:如果使用默認的檢查規則對表單進行檢查的話需要注意幾個關鍵的地方,否則你可能就會花很長的時間檢查js腳本無法應用的原因。 首先您所使用的FormBean就不能是原來的ActionForm了,需要更改為ValidatorForm。 這個Form中還必須有一個類似這樣的validate方法:
public ActionErrors validate(
ActionMapping mapping,
HttpServletRequest request) {
ActionErrors errors = super.validate(mapping, request);
return errors;
}
其次需要在頁面的結尾處聲明使用javascript:
<html:javascript formName="sysuserForm"
dynamicjavascript="true"
staticjavascript="false"/>
dynamicjavascript屬性是代表是否在頁面內生成動態的js腳本。 如果您要對表單進行合法性檢查的話,此屬性必須為true。 staticjavascript屬性代表是否在頁面內生成靜態js腳本。 如果您設為true,則存放在validator-rules.xml文件中的規則檢查js 都將填充到本頁面內。這樣做的效果不是很好,因為會使頁面變得很大, 并且消耗大量的額外資源。通常的做法是將此選項設置成false, 將validator-rules.xml中的js填充到一個指定的jsp頁面中去。 這樣多個表單都可以同時使用一個靜態頁面,從而節省大量的資源。 如果這樣做我們就需要在后面在聲明對這個靜態頁面進行引用。
<script language="javascript1.1" src="../staticjavascript.jsp"/></script>
書寫這句話的時候也需要注意一點,盡量使用相對路徑較好。 使用絕對路徑的時候很難準確的定位這個靜態頁面的位置。
最后需要注意的一點就是在引用這個js檢查的時候。
<html:form action="/sysuser.do" method="post" focus="login" onsubmit="return validateSysuserForm(this);">
return validateSysuserForm(this);這個函數的名字是根據你所檢查的表單的名字而改變的。 例如我們檢查的這個表單叫做sysuserForm,則生成的檢查函數的名字為validateSysuserForm。 您在使用的時候需要將其修改成跟您所需要檢查的表單相對應的函數名,否則js的檢查就形同虛設了。
附錄
最后我們提供我們這篇文章所使用的例子的完整項目給大家下載。 您可以方便的將這個項目導入到您的eclipse中去。
轉自http://bsd.huangdong.com/dev/hibernate/strutsaction/index.html