学习笔记:Hibernate开发指南之Struts Action实践
在网上看到一篇文章《Hibernate开发指南之Struts Action实践 》这是一个入门实例,使用Struts的Action通过Hibernate对数据库进行增、删、改、查四项操作,还有表单的服务器端和客户端验证。 可惜没有下到全部的源文件,便自已动手加、改,不当之处难免,权当参考。
一、准备工作
主要是搭建Hibernate+Struts的环境,我是mysql+Tomcat 5.0+Hibernate2.1+Struts1.2.4,包括建目录和复制jar文件到相关目录,还有在mysql中创建数据库HibernateTest和表sysuser,请下载我的目录结构参考。
下面文件createTable.txt用来创建表sysuser
create table sysuser(
userid varchar(32) not null,
username varchar(20) not null unique,
userpasword varchar(20) not null,
lastlogin DATETIME,
primary key(userid)
)
二、Hibernate的配置文件hibernate.cfg.xml,主要是数据库的设定,这里用mysql。
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"">
true
net.sf.hibernate.dialect.MySQLDialect
com.mysql.jdbc.Driver
jdbc:mysql://localhost/HibernateTest
root
二、Struts的配置文件struts-config.xml,有表单定义,动作映射和资源文件,插件定义。
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"">
path="/sysuser"
type="com.huangdong.dbwebdemo.action.AddAction"
scope="request"
validate="true"
input="/sysuser/editsysuser.jsp">
type="com.huangdong.dbwebdemo.action.ListAction"
scope="request"
input="/sysuser/index.jsp">
type="com.huangdong.dbwebdemo.action.EditAction"
scope="request"
input="/sysuser/editsysuser1.jsp">
type="com.huangdong.dbwebdemo.action.DelAction"
scope="request"
input="/sysuser/del.jsp">
path="/update"
type="com.huangdong.dbwebdemo.action.UpdateAction"
scope="request"
input="/sysuser/editsysuser1.jsp">
这里加了两个插件,其一利用Plugin的方式将Hibernate与Struts结合起来,其二进行服务器端验证。插件将在Web应用启动时初始化。
在InitHibernatePlugin.java的init()方法中将SessionFactory的实例bind到JNDI目录树的一个节点上,以便在所有要使用SessionFactory的地方通过JNDI lookup出sessionFactory的实例得到具体的session进行数据库操作。
InitHibernatePlugin.java(部分)
/*
* 插件初始化方法
*/
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);
}
}
二、国际化资源文件
我们为站点准备一个国际化的资源文件,这个资源文件将是站点用户所使用的语言来决定的,如果Struts不能找到相对应的语言资源文件,就会使用默认的资源文件。 这里我们先会建立一个txt文件,将所有的中文信息写入,再通过一个批处理或是shell脚本将该txt转化成为Struts所使用的资源文件。
这里我们给出genres.bat的内容:
native2ascii -encoding GBK d:\java\res_zh.txt >d:\java\application_zh_CN.properties
请使用你的文件的路径来代替这里的路径。下面是res_zh.txt:
# Resources for parameter 'ApplicationResources'
# Project P/WebDlog
title.register=用户注册
prompt.login=用户名
prompt.password=口令
# Errors
errors.footer=
errors.header=
验证错误
你必须更正下列错误:
errors.ioException=I/O exception rendering error messages: {0}
error.database.missing=User database is missing, cannot validate logon credentials
errors.required={0} 是必填项.
errors.minlength={0} 不能少于 {1} 个字符.
errors.maxlength={0} 不能大于 {2} 个字符.
errors.invalid={0} 是无效的.
errors.byte={0} must be an byte.
errors.short={0} must be an short.
errors.integer={0} must be an integer.
errors.long={0} must be an long.
errors.float={0} must be an float.
errors.double={0} must be an double.
errors.date={0} is not a date.
errors.range={0} is not in the range {1} through {2}.
errors.creditcard={0} is not a valid credit card number.
errors.email={0} is an invalid e-mail address.
三、设置站点所使用的语言
我们使用一个Servlet的Filter来设置站点所使用的语言。(略,请参看原文)
四、session获取工具类
另外为了配置Plugin的使用,我们使用工具类DBUtil类来获取Session,以下为它的代码:
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;
}
}
五、持久层的数据表示及设置数据表实体化映射
我们的增、删、改、查使用sysuser表,以下是其对应的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文件:
六、建立Struts的Action和Action FormBean
有AddAction.java,UpdateAction.java,SysuserForm.java等,请下载源码查看。
七、将VO与PO关联
VO与PO的关系
VO即业务层的数据表示,而PO即持久层的数据表示,在这个例子里就是SysUser.java类。VO会在View和业务处理时大量使用,也就是说,所有没有入库前的数据都会存储于一个个的VO中。而PO则是数据库在Java中的持久数据结构。
有许多人喜欢将Struts的VO与Hibernate的PO合并起来,我不同意,原因很多,最重要的有以下几点:
1、VO有自己的数据属性,同时因框架的不同可能会有自己的结构和方法,在Struts中我喜欢用FormBean来做VO,它就是扩展ActionForm的一个类
2、VO中还会有大量的业务操作方法,如校验、自动生成等方法
3、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;
}
}
这个方法已经非常的简单了,书写者完全可以不用理会数据库相关的内容了。
其它代码请下载。
十二、数据库简单查询
数据库的查询相对复杂一些了,我们从简单做起,先不使用任何条件,查询数据库中所有的记录。在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页面editsysuser.jsp,它提供了增加记录的View。
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
OK!!!
十二、客户端验证和服务器端验证
editsysuser.jsp中的表单属性中有一个onsubmit="return validateSysuserForm(this);"与页面最后的
一起构成表单客户端验证。所有javaScript验证代码Struts自动生成。
表单服务器端验证由struts-config.xml中的插件
及资源文件构成。
十三、将业务逻辑关联起来
所有的Action类请下载。