风行天下

JAVA太极
posts - 4, comments - 10, trackbacks - 0, articles - 55
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
经过前面的两篇文章,我相信大家对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


评论

# re: 使用Struts的Action来通过Hibernate对数据库进行增、删、改、查四项操作  回复  更多评论   

2011-07-29 23:24 by Codenfans
谢谢 可以。。。了

只有注册用户登录后才能发表评论。


网站导航: