首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

Struts+Spring+Hibernate开发范例

2012-09-22 
Struts+Spring+Hibernate开发实例Struts+Spring+Hibernate开发实例 一 介绍本文并不想介绍Struts,Spring,H

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中避免代码重复使用了自定义标志来处理分页

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下载
 

热点排行