读书人

Struts+Spring+Hibernate开发范例

发布时间: 2012-09-22 21:54:54 作者: rapoo

Struts+Spring+Hibernate开发实例

Struts+Spring+Hibernate开发实例
一 介绍
本文并不想介绍Struts,Spring,Hibernate的原理系统架构等,本文地目的是通过一个较复杂地实例介绍如何整合Struts,Spring,Hibernate,网上现有的例子虽然也能达到目的,但功能都比较单一,复杂的例子时会有意想不到的麻烦。本文对读者假设已经具备了以上框架的基础知识。以及那些已经了解Struts,Spring,Hibernate的基本概念,但是还没有亲身在较复杂的项目中体验Struts+Spring+Hibernate的开发人员。
1 Struts
虽然不打算过多介绍Struts的原理,但是大概介绍一下还是有必要的。Struts本身就是 MVC 在这里负责将用户数据传人业务层,以及 将业务层处理的结果返回给用户,此系统属于较简单WEB应用,采用了OpenSessionInView模式处理LazyLoad问题,这样我们可以在用户视图中使用 get,set方法来方便地获取关联对象。为了处理庞大的Action和ActionForm问题,在此我门准备使用DynaActionForm (DynaValidatorForm)和DispatchAction以及 动态验证框架 来解决。及使用Tile来解决框架问题 。使用自定义标签处理分页和身份验证问题。
2 Spring
Spring Framework最得以出名的是与Hibernate的无缝链接,虽然Spring 对Hibernate提供了90%以上的封装,使我们不必去关心Session 的建立,关闭,以及事务使我们能够专心的关注业务逻辑。但是一些特殊情况如 有时需要Query以及Criteria 对象,分页等,Spring不能给我们提供支持,总不能每次都在你的DAO上写个HibernateCallBackup()吧?Spring的作用不是把Hibernate再封装一层,而是让你接触不到Hibernate的API,而是帮助你管理好Session和Transaction。
在这里解决方法是:首先 写一个IBase 的接口,和一个BaseDao的实现。在实现中仿照HibernateTemplate,将其功能一一实现,同时考虑到Spring 未能支持的地方,我们不得已只好自己来管理Session,因此加入public Session openSession(),public Query getQuery(String sql),public Criteria getCriteria(Class clazz),以及分页的方法。 然后为每一个Entity 都建立继承于以上类的IEntity,与EntityDao。这里可以根据需求对Entity加入特殊的方法实现,如 在 StudentsDao.java 中加入类似用户身份验证等。以上就是数据访问层。接下来在Service层中通过对dao的引用完成业务逻辑方法。在下面的例子中我们分别为学生模块,教师模块,管理员模块构建Service层,StudentsServiceImpl,TeachersServiceImpl,AdminServiceImpl。

3 Hibernate
有了Spring的封装,我们要对Hibernate做的就是正确实现对象关系的映射。由于此处处于系统的最底层,准确无误的实现对象之间的关联关系映射将起着至关重要的作用。
总之,理解了Struts,Spring,Hibernate地原理以及之间的关系之后,剩下的工作就如同在以Spring为核心的Struts为表现的框架中堆积木。
下图可以更好的帮助我们理解Struts,Spring,Hibernate之间的关系。

二 案例简述:
设计思路主要源于 大学选修课,该系统可以方便处理学生在课程选报,学分查询,成绩查询,以及 成绩发布等。
系统以班级为核心,一门课程可以对应多个班级,一名教师也可以带不同的班级,学生可以选报不同课程所对应的班级,班级自身有目前人数,和最大人数,以及上课时间,上课地点的属性。
学生在选报班级之后,班级的人数会自动加一,直到等于最大人数时,其他学生将会有人数已满的错误提示。同理如果学生选择了同一课程的不同班级,也将收到错误提示。学生有密码,系别,学分,地址,电话等属性。
教师在系统中主要负责成绩发布,教师可以对其所带的班级的学生的成绩修改,系统会以成绩是否大于等于60来判断学生是否通过考试,如果通过会将该课程的学分累加到学生学分,同样如果教师二次修改了成绩,而且小于60,系统会在学生学分上扣掉该课程的分数。
课程在系统中具体体现为班级,自身带有学分属性。
</S< body>
系有编号,名称的属性,同时可以作为联系教师,课程,学生的桥梁。

功能模块
? 身份验证模块: 根据用户名,密码,用户类别 转发用户到不同的模块。
? 学生模块: 查看课程,查看班级,选报课程,查看己选课程,成绩查询。
? 教师模块: 录入成绩
? 管理员模块:对学生,教师,课程,班级,系 增,删,查,改。

三 具体实践
代码下载
http://www.blogjava.net/Files/limq/StudentManger.rar
1 对象关系映射:
首先,将库表映射为数据模型(SQL在源码中查看),转换后的数据模型如下图:
[Gjava人才] www.gjrencai.com [java学习资料,学习笔记实例源码等]
地址:www.gjrencai.com 只做java人的网站


由此我们可以看出一下关联关系:
1 Students 和 Contact(联系方式)一对一关系。
2 Students 和 History(选课历史) 一对多关系
3 Students 和 Classes 多对多关系。
4 Classes 和 Classes_info 一对多关系。
5 Classes 和 Teachers 多对一关系。
6 Classes 和 Courses 多对一关系。
7 Course 和 Department(系) 多对一关系。
8 Teachers 和 Department 多对一关系。
9 Students 和 Department 多对一关系。

在Hibernate中将以上关系一一映射,如Students 和 History 一对多关系
Students.cfg.xm.:
1 <set name="history"
2 table="history"
3 cascade="all"
4 inverse="true"
5 lazy="true" >
6 <key column="student_id"/>
7 <one-to-many + entity.getClass().getName() + " 实例到数据库失败", e);
7
8 }
9 }
10 /**
11 * 获得session
12 */
13 public Session openSession() {
14 return SessionFactoryUtils.getSession(getSessionFactory(), false);
15 }
16
17 /**
18 * 获得Query对象
19 */
20 public Query getQuery(String sql) throws Exception{
21 Session session = this.openSession();
22 Query query = session.createQuery(sql);
23 return query;
24 }
25 /**
26 * 获得Criteria对象
27 */
28 public Criteria getCriteria(Class clazz) throws Exception{
29
30 Session session=this.openSession();
31 Criteria criteria = session.createCriteria(clazz);
32 return criteria;
33 }
34

可以看到,这里即充分利用了Spring对Hibernate的支持,还弥补了Spring的不足。最后分别为每个持久对象建立Interface,以及DAO,使其分别继承IBaseDao与BaseDao。
如IDepartment,DepartmentDao
1 public interface IDepartment extends IBaseDao {}
2
3 public class DepartmentDao extends BaseDao implements IBaseDao {}
4

3 Service 层
在这里需要认真思考每个业务逻辑所能用到的持久层对象和DAO,还要完成配置Spring框架, 首先我一起看看applications-service.xml

1 <?xml version="1.0" encoding="UTF-8"?>
2 DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
3 "http://www.springframework.org/dtd/spring-beans.dtd">
4 <beans>
5 <bean id="dataSource" destroy-method="close">
6 <property name="driverClassName">
7 <value>com.mysql.jdbc.Driver</< SPAN>value>
8 </< SPAN>property>
9 <property name="url">
10 <value>jdbc:mysql://localhost:3306/Student</< SPAN>value>
11 </< SPAN>property>
12 <property name="username">
13 <value>root</< SPAN>value>
14 </< SPAN>property>
15 <property name="password">
16 <value></< SPAN>value>
17 </< SPAN>property>
18 </< SPAN>bean>
19 <bean id="sessionFactory" encoding="UTF-8"?>
2 <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
3 <context-param>
4 <param-name>contextConfigLocation</< SPAN>param-name>
5 <param-value>/WEB-INF/classes/applications-service.xml</< SPAN>param-value>
6 </< SPAN>context-param>
7 <context-param>
8 <param-name>log4jConfigLocation</< SPAN>param-name>
9 <param-value>/WEB-INF/log4j.properties</< SPAN>param-value>
10 </< SPAN>context-param>
11 <filter>
12 <filter-name>hibernateFilter</< SPAN>filter-name>
13 <filter-class>org.springframework.orm.hibernate.support.OpenSessionInViewFilter</< SPAN>filter-class>
14 </< SPAN>filter>
15 <filter-mapping>
16 <filter-name>hibernateFilter</< SPAN>filter-name>
17 <url-pattern>/*</< SPAN>url-pattern>
18 </< SPAN>filter-mapping>
19 <filter>
20 <filter-name>Set Character Encoding</< SPAN>filter-name>
21 <filter-class>limq.struts.SetCharacterEncodingFilter</< SPAN>filter-class>
22 </< SPAN>filter>
23 <filter-mapping>
24 <filter-name>Set Character Encoding</< SPAN>filter-name>
25 <url-pattern>/*</< SPAN>url-pattern>
26 </< SPAN>filter-mapping>
27 <servlet>
28 <servlet-name>SpringContextServlet</< SPAN>servlet-name>
29 <servlet-class>org.springframework.web.context.ContextLoaderServlet</< SPAN>servlet-class>
30 <load-on-startup>1</< SPAN>load-on-startup>
31 </< SPAN>servlet>
32 <servlet>
33 <servlet-name>action</< SPAN>servlet-name>
34 <servlet-class>org.apache.struts.action.ActionServlet</< SPAN>servlet-class>
35 <init-param>
36 <param-name>config</< SPAN>param-name>
37 <param-value>/WEB-INF/struts-config.xml</< SPAN>param-value>
38 </< SPAN>init-param>
39 <init-param>
40 <param-name>debug</< SPAN>param-name>
41 <param-value>3</< SPAN>param-value>
42 </< SPAN>init-param>
43 <init-param>
44 <param-name>detail</< SPAN>param-name>
45 <param-value>3</< SPAN>param-value>
46 </< SPAN>init-param>
47 <load-on-startup>0</< SPAN>load-on-startup>
48 </< SPAN>servlet>
49 <servlet-mapping>
50 <servlet-name>action</< SPAN>servlet-name>
51 <url-pattern>*.do</< SPAN>url-pattern>
52 </< SPAN>servlet-mapping>
53 </< SPAN>web-app>
54
55
其中注意这几句,
1 <filter>
2 <filter-name>hibernateFilter</< SPAN>filter-name>
3 <filter-class>org.springframework.orm.hibernate.support.OpenSessionInViewFilter</< SPAN>filter-class>
4 </< SPAN>filter>
5 <filter-mapping>
6 <filter-name>hibernateFilter</< SPAN>filter-name>
7 <url-pattern>/*</< SPAN>url-pattern>
8 </< SPAN>filter-mapping>
9

由于我们使用了lazy = "true",如果想在UI层使用实体对象关联来获得其他对象时就会有这样的提示:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection
Spring 中引入了 OpenSessionInView模式可以处理以上问题,即在web.xml中加入以上代码。


接下来建立抽象BaseAction,和BaseDispatchAction,其中后者与前者相似目的为减少Action的数量
1 abstract class BaseAction extends Action {
2
3 private IStudentsService studentsService;
4 private ITeachersService teachersSerivce;
5 private IAdminService adminService;
6 public void setServlet(ActionServlet actionServlet) {
7 super.setServlet(actionServlet);
8 ServletContext servletContext = actionServlet.getServletContext();
9 WebApplicationContext wac = WebApplicationContextUtils
10 .getRequiredWebApplicationContext(servletContext);
11
12 this.studentsService = (IStudentsService) wac.getBean("studentManager");
13 this.adminService = (IAdminService) wac.getBean("adminManager");
14 this.teachersSerivce = (ITeachersService) wac.getBean("teacherManager");
15 }
部分源码被删除了 完整详细源码 请到http://www.gjrencai.com/soft_show.asp?id=3 查看
16 5 HttpServletRequest request,
6 HttpServletResponse response)
7 throws Exception {
8 String pageNo=request.getParameter("pageNo");
9 String totalcount=request.getParameter("totalcount");
10 String totalpage=request.getParameter("totalpage");
11 int pagesize=2;//每页的大小
12 PageInfo page =new PageInfo();
13 page.setPageSize(pagesize);
14 HashMap hp=null;
15 History[] historys = null;
16 String stu_name=null;
17 HttpSession session = request.getSession();
18 stu_name = (String) session.getAttribute("userName");
19 if(pageNo == null || totalcount == null || totalpage==null){
20 //第一次发送请求
21 page.setPageNo(1);
22 hp=super.getStudentsService().getStudentHistory(page,stu_name);
23 page.setTatalCount(((Integer)hp.get("totalCount")).intValue());
24 page.setTotalpage(((Integer)hp.get("totalPage")).intValue());
25 }else{
26 page.setPageNo(Integer.parseInt(pageNo));
27 page.setTatalCount(Integer.parseInt(totalcount));
28 page.setTotalpage(Integer.parseInt(totalpage));
29 hp=super.getStudentsService().getStudentHistory(page,stu_name);
30
31 }
32 historys =(History[]) hp.get("history");
33 request.setAttribute("history",historys);
34 request.setAttribute("pageinfo",page);
35 return mapping.findForward("success");
36 }
37 }
38

在stu_his_Content.jsp中避免代码重复使用了自定义标志来处理分页
1
2
3
4
5
6
7
8 <html:html locale="true">
9 <body>
10
14 <table width="550" border="1" cellspacing="0" align="center" cellpadding="0">
15 <tr>
16 <td><bean:message key="class.id"/></< SPAN>td>
17 <td><bean:message key="course.name"/></< SPAN>td>
18 <td><bean:message key="enrol.time"/></< SPAN>td>
19 <td><bean:message key="score"/></< SPAN>td>
20 <td><bean:message key="marking"/></< SPAN>td>
21 </< SPAN>tr>
22
26 <tr>
27 <td></< SPAN>td>
28 <td></< SPAN>td>
29 <td></< SPAN>td>
30 <td></< SPAN>td>
31 <td></< SPAN>td>
32 </< SPAN>tr>
33
36 </< SPAN>table>
37 <mytag:page pageinfo="" action="getHistory.do"/>
38 </< SPAN>body>
39 </< SPAN>html:html>
40
[Gjava人才] www.gjrencai.com [java学习资料,学习笔记实例源码等]
地址:www.gjrencai.com 只做java人的网站

标志处理类如下:
1 PageTag.java
2
3 public class PageTag extends SimpleTagSupport {
4
5 private PageInfo pageinfo = null;
6 private String action = null;
7
8 public String getAction() {
9 return action;}
10 public void setAction(String action) {
11 this.action = action;
12 }
13 public PageInfo getPageinfo() {
14 return pageinfo;
15 }
16 public void setPageinfo(PageInfo pageinfo) {
17 this.pageinfo = pageinfo;
18 }
19
20 public void doTag() throws JspException, IOException {
21 JspWriter out = getJspContext().getOut();
22
23 int totalpage = pageinfo.getTotalpage();
24 int totalcount = pageinfo.getTatalCount();
25 int pageNo = pageinfo.getPageNo();
26 int addPageNo = pageNo + 1;
27 int minPageNo = pageNo - 1;
28
29 out.println("< SPAN>"400\" align=\"center\" cellPadding=\"0\" cellSpacing=\"0\"> ");
30 out.print("");
31 out.println("共" + totalcount + "条," + totalpage + "页,当前"
32 + pageNo + "页");
33 out.println(" ");
34 if (pageNo > 1) {
35 out
36 .println("&< SPAN>"/StudentManger/" + action
37 + "?pageNo=1"+ "&totalpage=" + totalpage + "&totalcount="
38 + totalcount + "\">" );
39 }
40 out.print("首页");
41 out.println(" ");
42 if (pageNo > 1) {
43 out.println("&< SPAN>"/StudentManger/" + action + "?pageNo="
44 + minPageNo + "&totalpage=" + totalpage + "&totalcount="
45 + totalcount + "\">");
46 }
47 out.print("上页");
48 out.println(" ");
49 if (pageNo < totalpage) {
50 out.println("&< SPAN>"/StudentManger/" + action + "?pageNo="
51 + addPageNo + "&totalpage=" + totalpage + "&totalcount="
52 + totalcount + "\">");
53 }
54 out.print("下页");
55 out.println(" ");
56 if (pageNo < totalpage) {
57
58 out.println("< SPAN>"/StudentManger/" + action + "?pageNo="
59 + totalpage + "&totalpage=" + totalpage + "&totalcount="
60 + totalcount + "\">");
61
62 }
63 out.print("末页");
64 out.println(" ");
65 out.println("
");
66
67 }
68
69 }
70


5 中文乱码问题:
1 数据库:MYSQL 4.1 (或以上版本)4.1直接支持Unicode,以下版本支持的不好。
2 驱动: MySQL JDBC Driver的3.0.16(或以上版本)
3 在数据库中做如下设定

4 在建立表时同样加上ENGINE=MyISAM DEFAULT CHARSET=gbk
1 CREATE TABLE `students` (
2 `id` int(20) NOT NULL default '0',
3 `name` varchar(20) NOT NULL default '',
4 `department_id` int(11) default NULL,
5 `password` varchar(20) default NULL,
6 `score` double(15,3) default NULL,
7 PRIMARY KEY (`id`)
8 ) ENGINE=MyISAM DEFAULT CHARSET=gbk
9

5 配置hibernate.cfg.xml
1<property name="connection.url">jdbc:mysql://localhost:3306/Student</< SPAN>property>
2 <property name="dialect">net.sf.hibernate.dialect.MySQLDialect</< SPAN>property>
3 <property name="connection.password"></< SPAN>property>
4 <property name="connection.driver_class">com.mysql.jdbc.Driver</< SPAN>property>
5

robbin: MySQL JDBC Driver的3.0.16也是一个分水岭,3.0.16版本会取数据库本身的编码,然后按照该编码转换,这种方式和Oracle的JDBC Driver是一样的。例如你的数据库是GBK编码的话,JDBC Driver就会把数据库里面的取出来的字符串按照GBK往unicode转换,送给JVM。因此正确的设置数据库本身的编码就尤为重要。
MySQL JDBC Driver3.0.16以下的版本则不然,它不会那么智能的根据数据库编码来确定如何转换,它总是默认使用ISO8859-1,因此你必须使用 characterEncoding=GBK来强制他把数据库中取出来的字符串按照GBK来往unicode转换。
因此,使用什么数据库版本,不管是3.x,还是4.0.x还是4.1.x,其实对我们来说不重要,重要的有二:

1) 正确的设定数据库编码,MySQL4.0以下版本的字符集总是默认ISO8859-1,MySQL4.1在安装的时候会让你选择。如果你准备使用UTF- 8,那么在创建数据库的时候就要指定好UTF-8(创建好以后也可以改,4.1以上版本还可以单独指定表的字符集)
2) 使用3.0.16以上版本的JDBC Driver,那么你就不需要再写什么characterEncoding=UTF-8

6 开发工具介绍
MyEclipse 3.8
首先添加用户库,如下图将Struts,Spring,Hibernate 的库添加到用户库中

如果出现环境问题可能你的Struts包有问题,请到http://struts.apache.org/download.cgi下载

读书人网 >编程

热点推荐