MyEclipse10-SSH简单开发应用
1 开发环境
开发环境MyEclipse10.6及其Struts 2.1,Spring3.1和Hibernate4.1[MyEclipse内置的最高版本]
2 建立Web工程并添加 SSH支持
2.1 新建一个WebProject
指定工程名字SSH_SQLServerB,javaEE默认6.0版本。
2.2 添加Spring支持
选择Spring3.1版本及其库Spring 3.1 Core、Spring 3.1 Persistence Core、Sp1 ring 3. 1 Web,将所选库“指定”“Copy”到lib目录以方便工程发布,指定在/WebRoot/WEB-INF下形成配置文件applicationContext.xml。
2.3 添加Hibernate支持
选择Hibernate4.1版本及其库Hibernate 4.1 Core、Hibernate 4.1 Advanced Support,将所选库“指定”“Copy”到lib目录以方便工程发布,指定数据库SQL Server2010及其连接,不形成特定的HibernateSessionFactory,并选定将配置文件合并到Spring的applicationContext.xml。指定“/*”的URL过滤方式,在/src下生成配置文件struts.xml。
2.4 添加Struts支持
选择Struts2.1版本及其库Struts 2 Core、Struts 2 Spring,下面是过滤器的名字和URL,如图。
在产生的web.xml文件中添加一个Spring监听:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener </listener-class>
</listener>
2.5 系统初步测试
添加发布的Tomcat服务器支持,启动Tomcat服务器,在Web Browser下执行:
http://localhost:8080/SSH_SQLServerA
应该正常显示“This is my JSP page”。
<?xml version="1.0"encoding="UTF-8"?>
<web-appversion="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name></display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping></web-app>
3 最简Struts2系统设计
一个最简单的Struts系统,起码应该包括这样几个文件:
l 带有表单的页面文件(jsp文件)
l 表单提交后要执行的action(java类)
l Action执行完毕要转向的页面(jsp文件)
所谓的“action”(动作),就是在表单提交后系统会自动执行的一个Java类,该类必须继承 ActionSupport(在com.opensymphony.xwork2中)并重写其中的execute()方法。表单提交后,系统会将表单里包含的字段数据传递给该action类,并执行其中的execute()方法。
execute()方法必须返回一个字符串,而该字符其将决定系统要转向那个页面,这就是所谓的“导航”。
首先要构思系统的功能结构,以及需要哪些文件来实现这些功能。设计最简系统如下:
(1) 页面 index.jsp,其中包含一个简单的 form,其action名为“aCheck”,它包含两个文本字段:name和 password;
(2) 表单提交后要执行的 action类为AccountCheck;
(3) AccountCheck类执行时,打印出传递过来的name和password参数的值
(4) AccountCheck执行后转向页面Show.jsp。
3.1 action的执行与导航
为了将这一个构思传递给系统,需要使用struts.xml来配置这几个文件之间的逻辑关系。
修改struts.xml代码如下:
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//ApacheSoftware Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<package name="default"namespace="/" extends="struts-default">
<action name="aCheck"class="dbAction.AccountCheck">
<result name="toShow">/Show.jsp</result>
</action>
</package>
</struts>
<?xmlversion="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC"-//Apache Software Foundation//DTD Struts Configuration 2.1//EN""http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<package name="default" namespace="/"extends="struts-default">
<action name="aCheck"class="com.neuq.check.AccountCheck">
<result name="toIndex2">/index2.jsp</result>
</action>
</package>
</struts>
说明:
(1) “action”标签定义了一个动作,该动作由其name属性确定,并和页面表单中的“action=”属性相对应;class属性则指定了该动作由哪个Java类来实现。上面的代码相对应的页面表单应该有如下形式:
<formaction="aCheck">...</form>
则上述 form提交时,系统将寻找 AccountCheck类并执行之。
(2) “result”标签定义了在动作执行之后的跳转(导航),其中name属性和动作中execute()函数的返回值相对应,也就是说,如果动作中execute()返回值为字符串“toShow”,则系统跳转到Show.jsp页面。
(3) package是对 action分类的标签,其核心属性是“name”,name是与其他 package区分的依据;而 namespace(命名空间)属性则定义了到哪个地址寻找其下属的action;对于修改后的例子,只是简单的定义了一个名为“default”的package并指定根命名空间。需要注意的是,命名空间只有“一层”,而不是像文件目录那样可以有多个层次。另注意:URL中“xxx.action”和简化形式的“xxx”是等同的。
添加代码后,struts.xml文件的“<package..>”行在计算机没有联网时会有警告:Package default extends undefined package struts-default。这是隐含通过网络的规划检查,只要计算机连接internet,重启Myeclipse开发环境,就不会产生警告了。
3.2 页面表单编码
Web工程默认的“入口”页面是 index.jsp。最简单的支持Struts2框架的JSP文件结构为:
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<body>
</body>
</html>
最上面两行最关紧要!使用上面的模板替换系统中index.jsp原来的内容,并在<body>标签内加入struts表单:
<s:form action="aCheck" namespace="/" >
<s:textfield name="name" value="wang"></s:textfield>
<s:textfield name="password" value="123"></s:textfield>
<s:submit></s:submit>
</s:form>
其中“s:”是由上面所示第二行代码
<%@taglib prefix="s" uri="/struts-tags" %>
规定的struts标签的前缀,也就是说,所有struts标签都要以“s:”打头。“form”和“submit”标签和HTML表单中类似的标签的意义相同,而“textfield”标签相当于HTML表单的input标签。下划线的部分:action属性定义的名称"aCheck"和struts.xml中的action name是一致的;两个textfield定义了两个表单字段,一个名为“name”,另一个名为“password”。用户在这两个字段中输入的数据将被Struts传递到后台action 程序中。
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="s"uri="/struts-tags" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>MyJSP 'index.jsp' starting page</title>
<meta http-equiv="pragma"content="no-cache">
<meta http-equiv="cache-control"content="no-cache">
<meta http-equiv="expires"content="0">
<meta http-equiv="keywords"content="keyword1,keyword2,keyword3">
<meta http-equiv="description"content="This is my page">
<!-- <link rel="stylesheet"type="text/css" href="styles.css"> -->
</head>
<body>
<s:form action="aCheck" namespace="/" >
<s:textfield name="name"value="wang"></s:textfield>
<s:textfield name="password"value="123"></s:textfield>
<s:submit></s:submit>
</s:form>
</body>
</html>
3.3 动作编程
根据struts.xml要求,要编写一个名为“AccountCheck”的类来实现action“aCheck”,放在包dbShow下。这个类有4个要求:
a) 继承 ActionSupport;
b) 包含表单字段对应的属性变量;
c) 对上面的属性变量编写 getter和setter方法;
d) 重写execute()方法,并返回与struts.xml对应的字符串以进行导航。
AccountCheck.java的代码如下:
packagedbShow;
importcom.opensymphony.xwork2.ActionSupport;
publicclass AccountCheckextendsActionSupport
{ privatestaticfinallongserialVersionUID=1L;
privateString name;
privateString password;
publicString getName() { returnname; }
publicvoidsetName(String name) { this.name= name; }
publicString getPassword() { returnpassword; }
publicvoidsetPassword(String password) { this.password= password; }
publicString execute() { System.out.println(name+ password); return"toShow"; }
}
上文中的核心代码只有4行,前两行只是声明了与表单字段相对应的变量(Struts会自动在表单和 action类之间进行数据传递);第三行将传递过来的字段值打印出来(一个简单的数据处理);最后一行返回字符串“toShow”以指示系统导航到/Show.jsp页面。注意其中的setter和getter函数分别用于设置和获取字段变量的值;但是在上面的代码中并没有看到对getter和setter的引用,而其实这正是Struts的奇妙之处:当execute()被执行时,所有的字段变量已经在Struts的控制调度下被设置好了!
3.4 结果页面编程
根据struts.xml的要求,结果页面是Show.jsp;目前暂不需要在该页面中工作,因此可以随便写个页面并命名为Show.jsp即可。
3.5 部署运行测试
现在可以运行系统了。将工程部署在tomcat下,运行。在浏览器中输入
http://localhost:8080/SSH_SQLServerB/
可以输入一些内容,点击“Submit”按钮,系统将自动转到Show.jsp,并在控制台打印出来输入的内容(由execute()函数中的println()实现)。
3.6 错误处理
在这一阶段常见的错误是HTTP Status 404 - There is no Action mapped for namespace/ and action name xxx。错误意味着在指定的命名空间没有找到名为xxx 的动作(同时在“根”命名空间也没有找到)。需要查看并修改struts.xml 配置文件。
还可能有这样一个莫名其妙的错误:MyEclipse首次启动的时候,会要求建一个“工作区(workspace)”,后继的工程文件均存放到这个工作区文件夹中。如果编写了多个工程,有时候会出现莫名其妙的错误,那么除了认真阅读错误提示并进行更正意外,有些错误可能是前期的工程遗留下来的,即使早已删除了那些工程,其发布到tomcat中的应用并没有移除干净!
解决的方法很简单:退出MyEclipse,到workspace目录,在.metadata\.me_tcat\webapps中有已经发布所有工程的文件夹,找到原来的工程文件夹,删除那些有问题的工程(或者干脆全部删除,因为这并不影响你的源文件),然后重新启动 MyEclispse 就可以了。如果怕麻烦,最好一个工程使用一个独立的工作区。
3.7 结果页面编程
上面的实验,Java的打印语句只能在服务器的控制台上输出结果,而不能将结果显示到用户的浏览器中。Struts将action处理的数据(包括表单提交给action的数据)和应用(Application)范围内的数据,都存储在一个叫做“值栈”(valueStack)的地方;有很多种方法可以从这里取出值来。现在重新对Show.jsp编程,将这些值取出到页面中:
首先,使用通用的struts页面模板替换Show.jsp的内容,并在<body>标签内加上:
<s:text name="password"/><hr/>
<s:property value="password" /><hr/>
${password}<hr/>
运行一下试试。可以看到,三种方法都可以将输入的password显示出来。其中前两种为Struts标签形式,${password}的写法是OGNL表达式语言,其用法暂略。
4 简单数据库操作系统设计
在SQL Server2010中建立应用数据库study及其数据表Recorder。然后,设计最简单数据库操作系统:从“Recorder”表中读出所有数据,并显示在页面中,流程如下:
(1) 在index.jsp中再建立一个表单,对应的动作是“showList”;
(2) “showList”对应的 Java类命名为“RecorderList”,它在Struts的调控下获取数据表中的记录;
(3) “showList”执行完毕后转向 RecorderList.jsp,在该页面上显示动作类获取的数据。
Recorder数据表对应的SQL如下:
create table Recorder (
ID intidentity not null,
Namevarchar(20) notnull,
Artcles intnot null,
Books intnot null,
primarykey (ID)
);
4.1 简单数据库及其访问设计
在/src下创建包dbDao,在其下编写实体类Recorder.java及其hibernate映射文件Recorder.hbm.xml。在/src/dbShow下建立数据访问接口类RecorderDAO.java。不要使用Myeclipse环境的“Hibernate Reserve Enginner…”工具建立自动这三个文件,它不能兼容新版的Spring和Hibernate,会引起诸多错误和麻烦。
实体类Recorder.java代码如下:
packagedbDao;
publicclassRecorder
{ publicintid;
publicString name;
publicintarticles;
publicintbooks;
/**
* @hibernate.id generator-class="native"
* @hibernate.column name="ID"type="int" not-null="true"
*/
publicintgetId() { returnid; }
publicvoidsetId(int id) { this.id = id; }
/**
* @hibernate.propertycolumn="Name"type="string" length="20" not-null="true"
*/
publicString getName() { returnname; }
publicvoidsetName(String name) { this.name = name; }
/**
* @hibernate.propertycolumn="Artcles"type="int" not-null="true"
*/
publicintgetArticles() { returnarticles; }
publicvoidsetArticles(int articles) { this.articles = articles; }
/**
* @hibernate.propertycolumn="Books"type="int" not-null="true"
*/
publicintgetBooks() { returnbooks; }
publicvoidsetBooks(int books) { this.books = books; }
}
已经在实体类Recorder.java文件中增加了Ant-XDoclet注释,可以使用Myeclipse环境的Run XDoclet工具自动产生映射文件Recorder.hbm.xml,整理后的Recorder.hbm.xml代码如下:
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/HibernateMapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="dbDao.Recorder"table="Recorder">
<id name="id"type="int" >
<column name="ID"not-null="true"/>
<generator class="native"/>
</id>
<property name="name"type="string" column="Name"length="20" not-null="true"/>
<property name="articles"type="int" column="Artcles"not-null="true" />
<property name="books"type="int" column="Books"not-null="true" />
</class>
</hibernate-mapping>
数据访问接口类RecorderDAO.java代码如下:
packagedbDao;
importjava.util.List;
importorg.hibernate.Session;
importorg.hibernate.SessionFactory;
importorg.springframework.context.ApplicationContext;
importstaticorg.hibernate.criterion.Restrictions.eq;
publicclassRecorderDAO
{ privateSessionFactory sessionFactory;
publicSessionFactory getSessionFactory()
{ returnsessionFactory; }
publicvoidsetSessionFactory(SessionFactory sessionFactory)
{ this.sessionFactory = sessionFactory; }
publicList<Recorder> findAll() // 列表查询
{ Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
@SuppressWarnings("unchecked") // 回避泛类检查警告
List<Recorder>list = session.createCriteria(Recorder.class).list();
session.getTransaction().commit();
returnlist;
}
publicList<Recorder> findByProperty(String property, Object value) //按指定属性查询
{ Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
@SuppressWarnings("unchecked")
List<Recorder>list = session.createCriteria(Recorder.class).add(eq(property,value)).list();
session.getTransaction().commit();
returnlist;
}
publicvoidinsert(Recorder recorder) // 插入增加
{ Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.save(recorder);
session.getTransaction().commit();
}
publicvoiddelete(Recorder recorder) // 删除
{ Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.delete(recorder);
session.getTransaction().commit();
}
publicvoidchange(Recorder recorder) // 修改
{ Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.update(recorder);
session.getTransaction().commit();
}
publicstaticRecorderDAO getFromApplicationContext(ApplicationContext ctx)
{ return(RecorderDAO) ctx.getBean("recorderDAO"); }
}
DAO类说明:它集成了对实体类的通用操作,如 findAll()、findBy()等;特别地,它包含一个静态方法, 用于从应用上下文中获取该DAO类的一个实例:
getFromApplicationContext(ApplicationContext ctx);
这样只要获取了当前应用的上下文(Context)后,即可取得DAO实例并通过该实例操作数据对象了。
4.2 action的执行与导航
修改 struts.xml文件,添加如下的动作配置:
<action name="showList"class="dbAction.TableList">
<result name="toList">/TableList.jsp</result>
</action>
在起始页面index.jsp中添加:
<s:form action="showList"namespace="/" >
<s:submit value="记录列表"></s:submit>
</s:form>
这里面实际上只有一个 submit按钮。是动作类 TableList.java。
4.3 action的数据处理
数据处理action的“三板斧”为:
(1) 获取当前应用上下文;
(2) 获取实体类的DAO对象实例;
(3) 使用 DAO对象操作数据;
特别提醒:在使用 DAO对象操作数据时,经常使用 Java的集合类对象,例如 List等。
TableList.java的核心代码编写如下:
packagedbAction;
importjava.util.List;
importorg.apache.struts2.ServletActionContext;
importorg.springframework.context.ApplicationContext;
importorg.springframework.web.context.support.WebApplicationContextUtils;
importcom.opensymphony.xwork2.ActionSupport;
importdbDao.RecorderDAO;
importdbDao.Recorder;
publicclassTableList extends ActionSupport
{ privatestaticfinallongserialVersionUID=1L;
// 警告处理:serializable类Student未声明类型为long的静态终态serialVersionUID字段
List<Recorder> list;
RecorderDAOrecorderDAO;
publicList<Recorder> getList() { returnlist; }
publicvoidsetList(List<Recorder> list) { this.list= list; }
publicRecorderDAO getRecorderDAO() { returnrecorderDAO; }
publicvoidsetRecorderDAO(RecorderDAO RecorderDAO)
{ this.recorderDAO= RecorderDAO; }
publicString execute()
{ ApplicationContext ct =WebApplicationContextUtils
.getWebApplicationContext(ServletActionContext.getServletContext());
recorderDAO = RecorderDAO.getFromApplicationContext(ct);
list = recorderDAO.findAll();
intn = list.size();
for(inti = 0; i < n; i++)
{ Recorder c = list.get(i);
String name = c.getName();
System.out.println(name);
}
return"toList";
}
}
4.4 在结果页面上显示记录
上面的实现还有缺陷:并没有将 action获取的数据带到结果页面上。要做到这一点,必须进行两项工作:
(1) 在 action中将需要传递给结果页面的数据声明为类属性并为其编写 setter和 getter函数。
(2) 在页面中使用 Struts标签取出 action传递过来的数据。
因此首先将 StudentList.java 做如下修改:将 list 声明为类属性,并为其 list添加getter和setter, 注意execute()中对list引用的时候就不需要重复声明了。
package com.neuq.list;
import java.util.List;
import org.apache.struts2.ServletActionContext;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.neuq.common.Student;
import com.neuq.common.StudentDAO;
import com.opensymphony.xwork2.ActionSupport;
public class StudentList extends ActionSupport
{ List<Student> list;
StudentDAO studentDAO;
public List<Student> getList() { return list; }
public void setList(List<Student> list) { this.list = list; }
public StudentDAO getStudentDAO() { return studentDAO; }
public void setStudentDAO(StudentDAO studentDAO) { this.studentDAO = studentDAO; }
public String execute()
{ ApplicationContext ct = WebApplicationContextUtils
.getWebApplicationContext(ServletActionContext.getServletContext());
studentDAO = StudentDAO.getFromApplicationContext(ct);
list = studentDAO.findAll();
int n = list.size();
for (int i = 0; i < n; i++)
{ Student c = list.get(i);
String name = c.getName();
System.out.println(name);
}
return "toList";
}
}
然后编写 studentlist.jsp:
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<body>
<s:iterator value="list">
studentid: <s:property value="studentid" />
<br />
name: <s:property value="name" />
<br />
department: ${department}
<br />
qq: <s:text name="qq" />
<p>
</s:iterator>
</body>
</html>
这里面,尝试了几种不同的表现方法。
首先看<s:iterator>标签:它的作用是遍历由 value 指定的“集合型”对象(一般为Map或List类型);例子中,要遍历的是“list”对象(该对象由action传递过来,对应于action内的一个类属性)。其次要注意<s:property>标签,它的作用是将iterator中的对象的属性取出来,即<s:propertyvalue="name"/>的作用相当于调用list的getFirstname()方法(action中的getter在这里得到了“隐形的”调用)。从上面的代码中可以看出,除了使用<s:property>之外,还尝试了另外两种标签方法,一是<s:text>,它可以生成一个国际化的信息文本;另一个是${department},用的是OGNL表达式语言。网上有争论说,既然是 struts2,就尽量用struts2的标签;也有人说:OGNL是主流,应该和<s:>标签配合使用;请自行深入学习后选择。
4.5 Spring与struts结合
修改Spring配置文件applicationcontext.xml,加入:
(1) Hibernate最新版本的支持:在<bean id="sessionFactory"… >内添加两个key属性标签。
<propkey="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
(2)数据表资源的映射:在<bean id="sessionFactory"…>内添加对数据表资源的映射属性。
<property name="mappingResources">
<list>
<value>dbDao/Recorder.hbm.xml</value>
</list>
</property>
(3)数据访问接口DAO的支持:添加<bean …>。
<bean id="recorderDAO"class="dbDao.RecorderDAO">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
(4)action操作处理的支持:添加<bean …>。
<bean id="TableList"class="dbAction.TableList">
<property name="recorderDAO">
<ref bean="recorderDAO"/>
</property>
</bean>
整个applicationcontext.xml代码如下:
<?xml version="1.0"encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName"value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="url"value="jdbc:sqlserver://localhost:1433;databaseName=study"/>
<property name="username"value="sa" />
<property name="password"value="kzq666" />
</bean>
<bean id="sessionFactory"class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
<propkey="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>dbDao/Recorder.hbm.xml</value>
</list>
</property>
</bean>
<bean id="recorderDAO"class="dbDao.RecorderDAO">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="TableList"class="dbAction.TableList">
<property name="recorderDAO">
<ref bean="recorderDAO"/>
</property>
</bean>
</beans>
4.6 运行测试
运行工程,在浏览器地址栏中输入:http://localhost:8080/SSH_SQLServerB,即可显示该起始页面。点击“记录列表”按钮,显示页面和控制台中都可以看到相应信息输出。