@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
首先:我们需要搭建SSH环境,在这里,我们选择使用Myeclipse自带的导航为我们添加框架支持。接下来,我会一步步教大家做。
1.新建一个工程项目
2.添加Hibernate框架支持
A.myeclipse--project capabilities--add Hibernate capabilities
B.在这里,使用Hibernate3.3版本,默认情况下添加核心包和注解包,注意,勾选最下面选项,将jar自动添加到lib目录,方便换了电脑后修改。
C.这一步,按照默认点击next,生成的配置文件,我们可能不会使用到,因为我们整合了Spring。
D.忽略数据库连接的创建
E.不使用Hibernate帮我们创建SessionFactory,Spring会帮助我们创建
到此为止,Hibernate的配置就完成了,下面我们来进行Spring的配置。
3.添加Spring框架支持
A.在这里我们使用Spring2.5的版本,所添加的jar包括以下5个(截图为4个,还有一个为Spring2.5 web libray)别忘记把jar添加到lib目录。
B.创建的Spring配置文件applicationContext.xml一般不用改名字,但目录建议改为webroot/WEB-INF下,因为Spring加载配置文件默认从那里开始
C.接下来一步,一般不需要选择,我们自己编写代码来创建SessionFactory
到目前为止,我们基本上把Spring也配置完成了,观察我们的包结构,会发现多了一些标记,如‘S’等,意味着我们已经成功添加了Spring框架。接下来我们添加Struts框架支持,myeclipse没有为我们提供导航,你可以下载相关插件,但也不必要因为配置Struts的框架比较简单,手动添加就可以了。
4.添加Struts框架支持。
A.导入Struts2.2相关架包,注意不要忘记Struts2=Spring-plugin.jar这个包,它是框架整合需要用到的。
B.创建一个Struts的配置文件Struts.xml,可以直接拷贝之前项目的。(截图略)
C.配置Struts的启动参数,在web.xml配置相关信息,如下:
D.同时,我们注册Spring的监听器,一样是在web.xml文件下
<!-- 配置Spring监听器 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
有同学要问为什么不配置Spring配置文件的加载路径,因为之前我们已经修改过路径为Spring默认的加载路径,所以在这里我们不需要指定,但是如果你自定义了配置文件在src目录下,你需要配置如下信息:
<!-- 指定spring的配置文件,默认从web根目录寻找配置文件,我们可以通过spring提供的classpath:前缀指定从类路径下寻找 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
到目前为止,我们已经成功的为我们的项目添加了SSH的框架支持,接下来我们可以加载一遍服务器,启动服务器,运行一下我们的这个小系统,看是否能跑起来。(截图略)如果没有异常,表示大功告成。
5.配置Spring的applicationContext.xml文件
在配置之前,必要说明一点:在这里我们使用的是sql server 2005作为后台数据库,所以要加入相关的驱动,我们在这里选用jtds驱动,当然你可以使用其它,比如jdbc等,此时我们还需要导入jtds的架包:jtds-1.2.jar.
A.在Spring的配置文件中,我们首先配置数据源:
<!--
配置数据源
驱动:jtds
数据库:sql server 2005
username : sa
password : 123
-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver"></property>
<property name="url" value="jdbc:jtds:sqlserver://localhost:1433/oa;characterEncoding=UTF-8"></property>
<property name="username" value="sa"></property>
<property name="password" value="123"></property>
<property name="maxActive" value="100"></property>
<property name="maxIdle" value="30"></property>
<property name="maxWait" value="500"></property>
<property name="defaultAutoCommit" value="true"></property>
</bean>
B.然后我们配置会话工厂SessionFactory:相关属性不做介绍,在这里主要mappingResource这个属性,我们填写的就是我们要操作的实体对象,在这里我们要对一个User对象进行操作,所以我们需要创建一个User类,把这个对象映射到数据库当中。
<!-- 会话工厂 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<!-- 数据库方言 -->
<prop key="hibernate.dialect">org.hibernate.dialect.SybaseDialect</prop>
<!-- 显示sql语言 -->
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<!-- 映射文件 -->
<property name="mappingResources">
<list>
<value>cn/zbvc/bean/User.hbm.xml</value>
</list>
</property>
</bean>
6..现在,我们的Spring配置文件先配置到这里,接下来,我们来创建我们的实体映射类User和他的实体映射文件User.hbm.xml:
在User类中,增加相关属性,id,name,.age即可以,然后增加其set和get方法,这里就省略了。
然后我们创建其实体映射文件User.hbm.xml,要求和User类是在同一个包下:在这里注意一点,就是id的生成策略,在这里我们使用native,如果改为increment自增的话会出现一些问题,具体网上有解释,可参考http://blog.sina.com.cn/s/blog_57554ed50100e3cu.html
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.zbvc.bean.User" table="users">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"/>
<property name="age"/>
</class>
</hibernate-mapping>
7.下面我们来完成第一个功能,增加一个用户的操作(注:保证数据库已经有了我们需要使用数据库oa),在写增加操作之前我们需要明白一个流程,即SSH是如何运行的,他们的流程是什么?
首先客户端提交需要保存的信息到filter,filter根据不同的需求转发给不同的Action,Action调用业务逻辑层Service方法,Service层再调用持久层方法,持久层直接对数据库进行操作,最后再一步步的返回相应到Action,到客户端。所以在写我们的CRUD之前,我们还需要做一步就是建立我们的业务逻辑层和持久层还有Action表现层。
通过这个截图图,我们可以看到我们的相关类和接口已经创建完成了,在这里我要说明的是,我们的Action可能会有好几个,因为在这里我们只是写一个demo,所以方便大家看,我们为各自的功能分别增加一个Action,另外,对于业务逻辑层,在这里它的功能和持久层一致,因为这是一个demo,没有太多复杂的业务,但为了养成良好的习惯我们还是强制的加上这个业务逻辑层。
(1)下面我们首先编写DAO层的相关具体操作:
package cn.zbvc.dao;
import java.util.List;
import cn.zbvc.bean.User;
public interface UserDAO {
/**
* 保存用户
* @param user
*/
public void saveUser(User user);
/**
* 删除用户
* @param id
*/
public void deleteUser(int id);
/**
* 根据id找到某个用户
* @param id
*/
public User findUserById(int id);
/**
* 查询所有的用户
* @param user
* return List<User>
*/
public List<User> findAllUsers();
/**
* 更新用户
* @param user
*/
public void updateUser(User user);
}
(2)具体实现类:
在具体实现类里我们需要注意,我们继承了一个HibernateDaoSupport,这个类是Spring给我们提供的用于操作数据库的类,使用非常简单。具体代码如下:
package cn.zbvc.dao.impl;
import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import cn.zbvc.bean.User;
import cn.zbvc.dao.UserDAO;
public class UserDAOImpl extends HibernateDaoSupport implements UserDAO {
public void deleteUser(int id) {
User user = findUserById(id);
if(user != null){
this.getHibernateTemplate().delete(user);
}
}
@SuppressWarnings("unchecked")
public List<User> findAllUsers() {
String hql = "from User";
return (List<User>)this.getHibernateTemplate().find(hql);
}
public User findUserById(int id) {
return (User)this.getHibernateTemplate().get(User.class, id);
}
public void saveUser(User user) {
this.getHibernateTemplate().save(user);
}
public void updateUser(User user) {
this.getHibernateTemplate().update(user);
}
}
接下来拷贝相关代码到业务逻辑层,注意:在业务逻辑层需要创建一个持久层的对象来调用持久层的方法,这个持久层对象我们使用Spring为我们创建,具体代码如下:首先在业务层定义这样一个持久层对象userDao.,同时我们Action还需要定义个业务逻辑层的对象,所以其创建也在配置文件中写到。
<!-- 创建持久层Dao对象 依赖注入sessionFactory-->
<bean id="userDao" class="cn.zbvc.dao.impl.UserDAOImpl" scope="singleton">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 创建业务逻辑层Service对象 依赖注入其持久层对象属性-->
<bean id="userService" class="cn.zbvc.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao" />
</bean>
再往下继续,我们需要编写我们的Action了,第一个Action:SaveUserAction.java:保存用户信息,以往的做法是添加这个用户的属性字段,实例化User对象,调用其set方法封装信息到User对象中,然后调用相关方法。但是假设属性字段比较多的时候这个方法不可取,现在我们使用模型驱动的方式获取我们的表单信息,首先,我们编写一个网页,叫做save.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>Save User</title>
</head>
<body>
<h1><font color="red">保存用户</font></h1>
<s:form action="saveUser">
<!-- 注意name = 对象名.属性名 -->
<s:textfield name="user.name" label="%{getText('name')}"></s:textfield>
<s:textfield name="user.age" label="%{getText('age')}"></s:textfield>
<s:submit value="提交"></s:submit>
</s:form>
</body>
</html>
然后我们需要配置Struts.xml相关信息:在这里我们看到我们的class是一个别名,也就是说我们对于Action的实例,也让Spring托管了。另外当返回结果为success的时候我们使其重定向为listUser.action,这个action的目的是为了显示所有用户信息的列表。具体代码如下:
<!-- 保存用户的Action -->
<action name="saveUser" class="saveUserAction">
<result name="success" type="redirect">listUser.action</result>
<result name="input">/save.jsp</result>
</action>
<!-- 显示所有用户的Action -->
<action name="listUser" class="listUserAction">
<result name="success">/listUser.jsp</result>
</action>
<!-- 创建控制层SaveUserAction的对象 依赖注入其业务层对象属性 -->
<bean id="saveUserAction" class="cn.zbvc.action.user.SaveUserAction" scope="prototype">
<property name="service" ref="userService" />
</bean>
<!-- 创建ListUserAction的对象 依赖注入其业务逻辑层对象属性-->
<bean id="listUserAction" class="cn.zbvc.action.user.ListUserAction" scope="prototype">
<property name="service" ref="userService" />
</bean>
SaveUserAction.java的具体代码如下:
package cn.zbvc.action.user;
import cn.zbvc.bean.User;
import cn.zbvc.service.UserService;
import com.opensymphony.xwork2.ActionSupport;
/**
* 控制层Action
* @author 吕鹏
*/
public class SaveUserAction extends ActionSupport {
private User user;
private UserService service;//通过Spring创建业务层对象 使用set方法依赖注入
public void setService(UserService service) {
this.service = service;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String execute() throws Exception {
this.service.save(this.user);
return SUCCESS;
}
}
下面我们来进行一下测试,看我们能否保存数据到数据库当中,在这之前,请再次检查一下相关配置,Struts.xml文件的配置,applicationContext.xml的配置等是否已经都正确,然后加载服务器,启动服务器,进入save.jsp页面;
许多同学可能不知道listUser.jsp的页面是如何显示的。下面拷贝一下代码:
<body>
<center>
<h1><font color="red">用户列表</font></h1>
<table border="1" width="450">
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>是否删除</td>
<td>是否更新</td>
</tr>
<s:iterator value="listUser">
<tr>
<td><s:property value="id"/></td>
<td><s:property value="name"/></td>
<td><s:property value="age"/></td>
<td><a href='<s:url action="deleteUser.action"><s:param name="user.id" value="id"/></s:url>' onclick="return del()">删除</a></td>
<td><a href='<s:url action="findUser.action"><s:param name="user.id" value="id"/></s:url>'>更新</a></td>
</tr>
</s:iterator>
</table>
</center>
</body>
从代码中可以看到我们使用了Struts2的标签库,其中迭代器的使用是比较方便的。刚才,我们完成了增加和查询所有信息的操作,接着往下看,我们要对数据进行删除和修改操作,先做删除,删除的话,我们要传递给DeleteAction一个id值,根据id值删除我们在数据库中的记录,我们可以在Action里面写这样一个字段id,添加其set和get方法,在网页上写这样的传递语句:(参考语句)
<td align="center"><a href="deleteUser.action?id=<s:property value="id"/>">删除</a></td>
但是这样的写法有一个问题,没有使用到我们之前的模型驱动,我们既然已经定义了User的对象,我们使用Struts2的标签就可以简化代码为:
<td><a href='<s:url action="deleteUser.action"><s:param name="user.id" value="id"/></s:url>' onclick="return del()">删除</a></td>
这样写的好处就是在Action中就没必要再增加id这个字段和其方法了。
然后我们还需要写相关的删除和修改的Action,在struts.xml文件中进行配置,在applicationContext文件中进行Action的配置,代码如下:
<!-- 删除用户的Action -->
<action name="deleteUser" class="deleteUserAction">
<result name="success" type="redirect">listUser.action</result>
</action>
<!-- 查询用户的Action -->
<action name="findUser" class="findUserAction">
<result name="success">/update.jsp</result>
</action>
<!-- 更新用户的Action -->
<action name="updateUser" class="updateUserAction">
<result name="success" type="redirect">listUser.action</result>
<result name="input">update.jsp</result>
</action>
在这里,可能你有一个问题不明白,为什么还有一个findUser的Action,因为是这样的,当我们进行修改操作的时候,必要要传递一个id值,根据id值查到相关的信息,进入一个修改页面,在修改页面中需要显示这个User的信息,所以这个过程需要用到findUser这个Action。
接下来是applicationContext.xml
<!-- 创建deleteUserAction的对象 依赖注入其业务逻辑层对象属性-->
<bean id="deleteUserAction" class="cn.zbvc.action.user.DeleteUserAction" scope="prototype">
<property name="service" ref="userService" />
</bean>
<!-- 创建findUserAction的对象 依赖注入其业务逻辑层对象属性-->
<bean id="findUserAction" class="cn.zbvc.action.user.FindUserAction" scope="prototype">
<property name="service" ref="userService" />
</bean>
<!-- 创建UpdateUserAction的对象 依赖注入其业务逻辑层对象属性-->
<bean id="updateUserAction" class="cn.zbvc.action.user.UpdateUserAction" scope="prototype">
<property name="service" ref="userService" />
</bean>
在这些Action的配置中,你会发现多了一个参数,即scope="prototype"。为什么要加这样一个参数,具体原因请浏览:http://blog.sina.com.cn/s/blog_5f12739d0100cre0.html
这样,我们基本上就完成了CRUD的相关配置和代码编写,下面我们再次进行测试,看是否可以完成这些操作:
Index.jsp:
Save.jsp:
listUser.jsp:(非功能截图)
Update.jsp:
显示要修改的用户
修改
修改成功
Delete.action:
这个对话框是使用js技术实现的,具体代码如下:
<SCRIPT type="text/javascript">
function del()
{
var result = confirm("你确定删除吗?");
if(result)
{
return true;
}
else
{
return false;
}
}
</SCRIPT>
删除成功(截图略)
以上就是一个简单的SSH整合的CRUD操作。
我们简单的回顾一下:
(1)首先,我们分别使用导航配置了Hibernate和Spring的框架
(2)创建了实体映射类和实体映射文件User.hbm.xml
(3)然后我们导入了Struts2.2的相关包,配置了Struts.xml文件
(4)然后我们又配置了applicationContext.xml文件和web.xml文件
(5)最后编写我们的Action,DAO,Service等
(6)最后编写相关操作网页等
讲解当中,我重点讲解了一些重点,还希望大家回顾的时候好好看,我推荐的一些网站,也都是很重要的。
下面我们针对这个CRUD系统,做一下扩展:
(1)国际化问题
在我们的表单里,默认语言为中文,所以显示的label都是汉字,当我们的默认语言设置为英文的情况下,显示的依旧是中文,这样是不健全的,所以我们需要使用Struts2国际化修改我们的jsp页面,这里只做一个简单的例子,将Save页面的label国际化:
大家都知道,国际化要在struts.xml文件中加一句
<constant name="struts.custom.i18n.resources" value="globalMessages" />
这里我们使用另外一种方式,和这个道理是一样的,我们建议资源文件:struts.properties,在文件中写:
struts.custom.i18n.resources=globalMessages
然后我们再建立关于中文或者英文的相关资源文件:
以中文为例,要注意不可以保存中文字符,所以要利用一些工具进行操作:
id=\u5E8F\u53F7
name=\u59D3\u540D
age=\u5E74\u9F84
做完这些操作以后,我们需要在我们的页面进行一下修改:
<s:form action="saveUser">
<!-- 注意label处调用的getText方法 -->
<s:textfield name="user.name" label="%{getText('name')}"></s:textfield>
<s:textfield name="user.age" label="%{getText('age')}"></s:textfield>
<s:submit value="提交"></s:submit>
</s:form>
然后我们把浏览器默认字符改成en 英文:
效果:
还有一个文件globalMessages.properties.这个文件的作用也是处理国际化问题,它还有一个作用是处理age的校验,当age字段接受的值为非数字的时候,将会出现文件的提示信息:
xwork.default.invalid.fieldvalue={0} \u683C\u5F0F\u4E0D\u6B63\u786E
国际化问题就讲到这里。
(2)校验问题
我们在保存用户和修改修护的时候并没有对其字段进行校验,在这里我们扩展一下校验:
大家所知道的校验方式有这么两三种,客户端校验和服务器的校验,服务器的校验又分重写valdate方法,和校验框架,对于客户端校验在这里就不写了,对于validate方法这里也省略,我们来写一下校验框架,校验框架就是字段校验,代码很简单,拷贝一下:
<!-- 模型字段校验方式 -->
<validators>
<field name="user.name">
<field-validator type="requiredstring">
<message>姓名不能为空</message>
</field-validator>
</field>
<field name="user.age">
<field-validator type="required">
<message>required age</message>
</field-validator>
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>年龄应该保持在 ${min} 到 ${max}</message>
</field-validator>
</field>
</validators>
我们讲这种校验方式叫做 模型字段校验方式,使用起来也比较容易理解,下面我们介绍另外一种校验方式,visitor方式:
<!--visitor校验方式-->
<validators>
<!-- 要校验的属性 -->
<field name="user">
<!-- 校验类型 -->
<field-validator type="visitor">
<!-- 上下文:具体的检验文件名字在这里指定 -->
<param name="context">user</param>
<!-- 附加前缀 比如:'用户的' -->
<param name="appendPrefix">true</param>
<!-- 附加前缀内容 -->
<message>用户的 </message>
</field-validator>
</field>
</validators>
我们发现我们的代码明显少了很多,看具体代码,都有注释,也容易理解。既然我们说使用这种校验方式,可是我们并没有看到具体的校验代码,原因是因为我们把校验的代码放到了别的地方,我们应该放在什么地方呢?Action是不断变化的,我们放在User对象所在的包下就可以了:
首先我们在User类所在包下建立这样一个文件User-user-validation.xml这个文件的命名是有一定讲究的,第一个User是要校验的类,第二个user是这个文件的真实名字,这个名字在前面的配置文件中出现过。。。里面的具体代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="name">
<field-validator type="requiredstring">
<message>姓名不能为空</message>
</field-validator>
</field>
<field name="age">
<field-validator type="required">
<message>required age</message>
</field-validator>
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>年龄应该保持在 ${min} 到 ${max}</message>
</field-validator>
</field>
</validators>
我们看一下效果:当什么都不输入的时候:
好,以上就是对这样一个SSH整合的CRUD的扩展,这节就到这里,接下来,我还会陆续的整理一些关于框架整合的案例,以此巩固框架的整合,慢慢的提升能力。下节课,我们在本节课的基础上再次的扩展,内容如下:
当我们查看数据库的信息的时候,数据库过大的情况下,我们需要采用分页,分页机制在开发中是一件繁琐而又复杂的事。下节课,我们来整理这样一个分页机制,帮助大家理解分页功能的实现。