SSH框架学习

一、建立web项目

       

之前想用myeclipse一次搞定,发现,那样做反而不利于理解,对于我这样的新手还是一步一步来的好。

计划是这样的,搭建web,添加struts,添加spring,数据库里面建立个user表,添加hibernate,首先把最基本的框架搭起来。

之后,又一点一点把框架丰富起来,添加role表,添加验证,ajax啥的。



搭建web不多说了,直接上图。

Context root URL里面,默认是和项目名一样,会发布到tomcat的:webapp\项目名\  这个地方,如果是:/,如上图,会发布到tomcat的:webapp\ROOT\  目录下。



myeclipse会默认添加一个index.jsp页面,只要servers运行起来能看到下面这个图,就说明基本的web就搭建好了。


二、加入基本的Struts

用myeclipse来添加struts,只是省了自己新建配置文件,导入包这步。其实自己做也不麻烦。


1、导入struts包。

struts最新版是2.3.7,下载地址http://struts.apache.org/download.cgi

下载下来的的压缩包解开,里面有个apps目录,里面是些.war文件。

其实war就是zip文件,重新命名下,就可以解压缩。把struts2-blank解压开来,能得到一个示例项目。既然人家都说这是blank了,就直接用这里面的包做最基础的包好了。


我不喜欢直接把包拷贝到lib目录下,宁可多建很多个Libraries包。


2、包导入好了,开始,建一个action。

先建立一个Package:demo.myssh.action,在下面建一个UserAction的类。

类的代码如下

[java]  view plain copy
  1. package demo.myssh.action;  
  2.   
  3. import com.opensymphony.xwork2.ActionSupport;  
  4.   
  5. @SuppressWarnings("serial")  
  6. public class UserAction extends ActionSupport {  
  7.   
  8.     @Override  
  9.     public String execute() throws Exception {  
  10.           
  11.         this.addActionMessage("UserAction working");  
  12.         this.addActionMessage("hello world.");  
  13.           
  14.         return ActionSupport.SUCCESS;  
  15.     }  
  16.   
  17. }  

3、顺便改造下默认的index.jsp,用actionmessage来接收UserAction的信息,这样好测试Action是否起作用了。

[html]  view plain copy
  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  
  2. <%@ taglib prefix="s" uri="/struts-tags" %>  
  3. <%  
  4. String path = request.getContextPath();  
  5. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";  
  6. %>  
  7.   
  8. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  9. <html>  
  10.   <head>  
  11.     <base href="<%=basePath%>">      
  12.     <title>My JSP 'index.jsp' starting page</title>  
  13.   </head>  
  14.     
  15.   <body>  
  16.     This is my JSP page. <br>  
  17.     <s:actionmessage/>  
  18.   </body>  
  19. </html>  

4、接下来,修改web.xml
[html]  view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app  
  3. version="3.0"   xmlns="http://java.sun.com/xml/ns/javaee"  
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
  5.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
  6.     http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">  
  7.     <display-name>myssh</display-name>  
  8.       
  9.     <filter>  
  10.         <filter-name>struts2</filter-name>  
  11.         <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>  
  12.     </filter>  
  13.     
  14.     <filter-mapping>  
  15.         <filter-name>struts2</filter-name>  
  16.         <url-pattern>*.action</url-pattern>  
  17.     </filter-mapping>  
  18.     
  19.     <welcome-file-list>  
  20.         <welcome-file>index.jsp</welcome-file>  
  21.     </welcome-file-list>  
  22.         
  23. </web-app>  

5、在src目录下,添加struts.xml文件

[html]  view plain copy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE struts PUBLIC  
  3.     "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"  
  4.     "http://struts.apache.org/dtds/struts-2.3.dtd">  
  5. <struts>  
  6.     <package name="mysshdemo" extends="struts-default">  
  7.         <action name="user" class="demo.myssh.action.UserAction">  
  8.             <result>/index.jsp</result>  
  9.         </action>  
  10.     </package>  
  11. </struts>  

这样,一个基本的struts就建立好了。访问http://localhost:8080/user.action,就能看到以下



三、在struts基础上加入spring

spring现在的版本是3.1.3,下载地址http://www.springsource.org/spring-framework#download 

spring的包里面没有提供例子,额,这点不如struts。


1、导入包。

首先需要添加struts的spring插件:struts2-spring-plugin-2.3.7.jar

其次,spring还需要一个外部的jar:commons-logging-1.1.1.jar,下载地址http://commons.apache.org/logging/download_logging.cgi

在这里,spring只需要以下几个jar



2、新建packa:demo.myssh.business,添加UserService类。

[java]  view plain copy
  1. package demo.myssh.business;  
  2.   
  3. public class UserService {  
  4.   
  5.     public String doing()  
  6.     {  
  7.         return "UserService working";  
  8.     }  
  9. }  

3、为了让UserService能够注入到UserAction,还需要为UserAction添加注入用的属性和方法

[java]  view plain copy
  1. package demo.myssh.action;  
  2.   
  3. import com.opensymphony.xwork2.ActionSupport;  
  4. import demo.myssh.business.UserService;  
  5.   
  6. @SuppressWarnings("serial")  
  7. public class UserAction extends ActionSupport {  
  8.   
  9.     @Override  
  10.     public String execute() throws Exception {  
  11.   
  12.         this.addActionMessage("UserAction working");  
  13.         // this.addActionMessage("hello world.");  
  14.         this.addActionMessage(userService.doing());// 修改下,确认注入成功。  
  15.   
  16.         return ActionSupport.SUCCESS;  
  17.     }  
  18.   
  19.     // 注入用属性  
  20.     private UserService userService;  
  21.   
  22.     // 注入用的方法  
  23.     public void setUserService(UserService userService) {  
  24.         this.userService = userService;  
  25.     }  
  26.   
  27. }  

4、在web.xml文件中加入spring的监听器

[html]  view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
  5.     http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">  
  6.     <display-name>myssh</display-name>  
  7.   
  8.     <filter>  
  9.         <filter-name>struts2</filter-name>  
  10.         <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>  
  11.     </filter>  
  12.   
  13.     <filter-mapping>  
  14.         <filter-name>struts2</filter-name>  
  15.         <url-pattern>*.action</url-pattern>  
  16.     </filter-mapping>  
  17.   
  18.     <!-- 加入spring的监听器 -->  
  19.     <listener>  
  20.         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  21.     </listener>  
  22.   
  23.     <welcome-file-list>  
  24.         <welcome-file>index.jsp</welcome-file>  
  25.     </welcome-file-list>  
  26.   
  27. </web-app>  


5、修改struts.xml文件

[html]  view plain copy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE struts PUBLIC  
  3.     "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"  
  4.     "http://struts.apache.org/dtds/struts-2.3.dtd">  
  5. <struts>  
  6.     <package name="mysshdemo" extends="struts-default">  
  7.         <action name="user" class="userAction">  
  8.             <result>/index.jsp</result>  
  9.         </action>  
  10.     </package>  
  11. </struts>  



6、在WEB-INF目录下新建applicationContext.xml文件

[html]  view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.   
  7.     <bean id="userAction" class="demo.myssh.action.UserAction">  
  8.         <property name="userService" ref="userService" />  
  9.     </bean>  
  10.   
  11.     <bean id="userService" class="demo.myssh.business.UserService"></bean>  
  12. </beans>  

这时候访问:http://localhost:8080/user.action

到此,基本的struts2+spring3搭建完成。


要点:这里有3个对应关系一定要注意

1、struts配置文件中,action的class属性要对应到spring配置文件中的bean的id,这样,struts就只负责传递,而所有servlet都有spring来管理。

我试过,struts.xml中,

不用<action name="user" class="userAction">

用<action name="user" class="demo.myssh.action.UserAction">也能正常运行。但是奇怪的是我之前如果这么用就无法注入。不确定是struts版本的问题还是哪里的问题。

个人理解,如果class里面配置的是个id,那么,action将有spring来直接管理。如果class里面配置的是类名,则action将有struts来处理。

推荐还是将action交给spring来管理的好,逻辑上更清晰,也不容易出现奇怪的问题。


2、spring配置文件中,注入类不是直接使用其类名,而是通过bean的id来获取。要注入哪个类,就在ref中填入那个的bean的id。

3、spring配置中注入属性的名称必须和java类里的属性名一致,并且,属性还要提供set方法。


四、struts+spring搭建,spring注解注入

这里,还可以用spring的注解注入的方式进行注入。

导入的包和之前一样不变,web.xml不变

applicationContext.xml

[html]  view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xmlns:context="http://www.springframework.org/schema/context"  
  5.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7.            http://www.springframework.org/schema/context   
  8.            http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
  9.   
  10.     <context:component-scan base-package="demo.myssh.business" />  
  11. </beans>  

struts.xml

[html]  view plain copy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE struts PUBLIC  
  3.     "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"  
  4.     "http://struts.apache.org/dtds/struts-2.3.dtd">  
  5. <struts>  
  6.     <package name="mysshdemo" extends="struts-default">  
  7.         <action name="user" class="demo.myssh.action.UserAction">  
  8.             <result>/index.jsp</result>  
  9.         </action>  
  10.     </package>  
  11. </struts>  

UserAction.java

[java]  view plain copy
  1. package demo.myssh.action;  
  2.   
  3. import org.springframework.beans.factory.annotation.Autowired;  
  4. import org.springframework.stereotype.Controller;  
  5.   
  6. import com.opensymphony.xwork2.ActionSupport;  
  7. import demo.myssh.business.UserService;  
  8.   
  9.   
  10. public class UserAction extends ActionSupport {  
  11.   
  12.     @Override  
  13.     public String execute() throws Exception {  
  14.   
  15.         this.addActionMessage("UserAction working");  
  16.         // this.addActionMessage("hello world.");  
  17.         this.addActionMessage(userService.doing());// 修改下,确认注入成功。  
  18.   
  19.         return ActionSupport.SUCCESS;  
  20.     }  
  21.   
  22.     // 注入属性  
  23.     @Autowired  
  24.     private UserService userService;  
  25. }  

UserService.java

[java]  view plain copy
  1. package demo.myssh.business;  
  2.   
  3. import org.springframework.stereotype.Service;  
  4.   
  5. @Service  
  6. public class UserService {  
  7.   
  8.     public String doing()  
  9.     {  
  10.         return "UserService working.";  
  11.     }  
  12. }  

运行结果和之前的一样。

这样的注入方式可以省去一些代码,不需要大量的配置文件。

没有大量的配置文件,好是省了不少代码,坏处是没有统一地方查看。

不过个人认为,如果采取这样的注入方式的话,不如直接把struts踢掉直接用spring3的mvc好了。


五、在struts和spring基础上加入hibernate

Hibernate有很机械的pojo类和hbm文件要写,这部分用myeclipse来做,能省不少事情,终于又感觉到myeclipse的好处了。


1、先在mysql里面建个表

[sql]  view plain copy
  1. CREATE TABLE `t_user` (  
  2.   `pk_user_id` bigint(20) NOT NULL AUTO_INCREMENT,  
  3.   `login_name` varchar(45) DEFAULT NULL,  
  4.   `email` varchar(45) DEFAULT NULL,  
  5.   `passwordvarchar(45) DEFAULT NULL,  
  6.   PRIMARY KEY (`pk_user_id`)  
  7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  

2、让myeclipse连接到数据库,还会帮我们自动生成hibernate的配置文件,和一个sessionFactory类。


3、用myeclipse生成实体类和映射配置文件,在myeclipse的db browser里面,打开数据库,选中表。

最后会生成这样一堆文件

整理一下,我是这么放的


4、把myeclipse添加的lib引用去掉,换成hibernate4。

就是这两个hibernate 3.3的,去掉。

hibernate最新版是4.1.8,下载地址http://sourceforge.net/projects/hibernate/files/hibernate4/

解压开后,在lib目录下有个叫required的目录,下面放的是最基本的包,这个我喜欢,比struts和spring做的好。

引入这些包就好了

此外还需要mysql的连接包,最新版本5.1.22,下载地址:http://www.mysql.com/downloads/connector/j/


5、现在开始来修改,从web页面开始


在网站根目录下,增加一个user目录,下面添加一个add.jsp文件。

[html]  view plain copy
  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  
  2. <%@ taglib prefix="s" uri="/struts-tags" %>  
  3. <%  
  4. String path = request.getContextPath();  
  5. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";  
  6. %>  
  7.   
  8. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  9. <html>  
  10.   <head>  
  11.     <title>My JSP 'add.jsp' starting page</title>    
  12.     <meta http-equiv="pragma" content="no-cache">  
  13.     <meta http-equiv="cache-control" content="no-cache">  
  14.   </head>  
  15.   <body>  
  16.     <form name="form1" method="post" action="user!add.action">  
  17.       <p>  
  18.         <label for="loginname">loginname</label>  
  19.         <input type="text" name="loginname">  
  20.       </p>  
  21.       <p>  
  22.         <label for="email">email</label>  
  23.         <input type="text" name="email">  
  24.       </p>  
  25.       <p>  
  26.         <label for="password">password</label>  
  27.         <input type="text" name="password">  
  28.       </p>  
  29.       <p>  
  30.         <input type="submit" name="submit" value="提交">  
  31.       </p>  
  32.     </form>  
  33.   This is my JSP page. <br>  
  34.   </body>  
  35. </html>  


这个页面,将内容提交到了UserAction类的add方法,现在来修改UserAction类。

UserAction类里面要添加add方法,和接收页面信息的属性及方法。

[java]  view plain copy
  1. package demo.myssh.action;  
  2.   
  3. import com.opensymphony.xwork2.ActionSupport;  
  4. import demo.myssh.business.UserService;  
  5. import demo.myssh.model.User;  
  6.   
  7. @SuppressWarnings("serial")  
  8. public class UserAction extends ActionSupport {  
  9.   
  10.     @Override  
  11.     public String execute() throws Exception {  
  12.   
  13.         this.addActionMessage("UserAction working");  
  14.         // this.addActionMessage("hello world.");  
  15.         this.addActionMessage(userService.doing());// 修改下,确认注入成功。  
  16.   
  17.         return ActionSupport.SUCCESS;  
  18.     }  
  19.   
  20.     // 注入用属性  
  21.     private UserService userService;  
  22.   
  23.     // 注入用的方法  
  24.     public void setUserService(UserService userService) {  
  25.         this.userService = userService;  
  26.     }  
  27.   
  28.   
  29.     public String add() throws Exception {  
  30.         userService.save(new User(loginname, email, password));  
  31.         return ActionSupport.SUCCESS;  
  32.     }  
  33.   
  34.     private String loginname;  
  35.     private String email;  
  36.     private String password;  
  37.   
  38.     public void setLoginname(String loginname) {  
  39.         this.loginname = loginname;  
  40.     }  
  41.   
  42.     public void setEmail(String email) {  
  43.         this.email = email;  
  44.     }  
  45.   
  46.     public void setPassword(String password) {  
  47.         this.password = password;  
  48.     }  
  49. }  

这里面,用到了UserService类里面的save方法,继续修改UserService。

[java]  view plain copy
  1. package demo.myssh.business;  
  2.   
  3. import demo.myssh.dao.UserDAO;  
  4. import demo.myssh.model.User;  
  5.   
  6. public class UserService {  
  7.   
  8.     public String doing() {  
  9.         return "UserService working";  
  10.     }  
  11.   
  12.     private UserDAO userDAO;  
  13.   
  14.     public void setUserDAO(UserDAO userDAO) {  
  15.         this.userDAO = userDAO;  
  16.     }  
  17.   
  18.     public void save(User user) {  
  19.         userDAO.save(user);  
  20.     }  
  21. }  

UserDAO这个类不需要多少修改,只需要把里面日志有关的地方去掉就可以了。因为,我没有引入那个类。大家都喜欢用log4j,打算之后加入log4j。

[java]  view plain copy
  1. package demo.myssh.dao;  
  2.   
  3. import java.util.List;  
  4. import org.hibernate.LockMode;  
  5. import org.hibernate.Query;  
  6. import org.hibernate.criterion.Example;  
  7. import demo.myssh.model.User;  
  8.   
  9. public class UserDAO extends BaseHibernateDAO {  
  10.   
  11.     // property constants  
  12.     public static final String LOGIN_NAME = "loginName";  
  13.     public static final String EMAIL = "email";  
  14.     public static final String PASSWORD = "password";  
  15.   
  16.     public void save(User transientInstance) {  
  17.   
  18.         try {  
  19.             getSession().save(transientInstance);  
  20.   
  21.         } catch (RuntimeException re) {  
  22.   
  23.             throw re;  
  24.         }  
  25.     }  
  26.   
  27.     public void delete(User persistentInstance) {  
  28.   
  29.         try {  
  30.             getSession().delete(persistentInstance);  
  31.   
  32.         } catch (RuntimeException re) {  
  33.   
  34.             throw re;  
  35.         }  
  36.     }  
  37.   
  38.     public User findById(java.lang.Long id) {  
  39.   
  40.         try {  
  41.             User instance = (User) getSession().get("User", id);  
  42.             return instance;  
  43.         } catch (RuntimeException re) {  
  44.   
  45.             throw re;  
  46.         }  
  47.     }  
  48.   
  49.     public List findByExample(User instance) {  
  50.   
  51.         try {  
  52.             List results = getSession().createCriteria("User")  
  53.                     .add(Example.create(instance)).list();  
  54.   
  55.             return results;  
  56.         } catch (RuntimeException re) {  
  57.   
  58.             throw re;  
  59.         }  
  60.     }  
  61.   
  62.     public List findByProperty(String propertyName, Object value) {  
  63.   
  64.         try {  
  65.             String queryString = "from User as model where model."  
  66.                     + propertyName + "= ?";  
  67.             Query queryObject = getSession().createQuery(queryString);  
  68.             queryObject.setParameter(0, value);  
  69.             return queryObject.list();  
  70.         } catch (RuntimeException re) {  
  71.   
  72.             throw re;  
  73.         }  
  74.     }  
  75.   
  76.     public List findByLoginName(Object loginName) {  
  77.         return findByProperty(LOGIN_NAME, loginName);  
  78.     }  
  79.   
  80.     public List findByEmail(Object email) {  
  81.         return findByProperty(EMAIL, email);  
  82.     }  
  83.   
  84.     public List findByPassword(Object password) {  
  85.         return findByProperty(PASSWORD, password);  
  86.     }  
  87.   
  88.     public List findAll() {  
  89.   
  90.         try {  
  91.             String queryString = "from User";  
  92.             Query queryObject = getSession().createQuery(queryString);  
  93.             return queryObject.list();  
  94.         } catch (RuntimeException re) {  
  95.   
  96.             throw re;  
  97.         }  
  98.     }  
  99.   
  100.     public User merge(User detachedInstance) {  
  101.   
  102.         try {  
  103.             User result = (User) getSession().merge(detachedInstance);  
  104.   
  105.             return result;  
  106.         } catch (RuntimeException re) {  
  107.   
  108.             throw re;  
  109.         }  
  110.     }  
  111.   
  112.     public void attachDirty(User instance) {  
  113.   
  114.         try {  
  115.             getSession().saveOrUpdate(instance);  
  116.   
  117.         } catch (RuntimeException re) {  
  118.   
  119.             throw re;  
  120.         }  
  121.     }  
  122.   
  123.     public void attachClean(User instance) {  
  124.   
  125.         try {  
  126.             getSession().lock(instance, LockMode.NONE);  
  127.   
  128.         } catch (RuntimeException re) {  
  129.   
  130.             throw re;  
  131.         }  
  132.     }  
  133. }  

BaseHibernateDAO 不用改,沿用

[java]  view plain copy
  1. package demo.myssh.dao;  
  2.   
  3. import org.hibernate.Session;  
  4.   
  5.   
  6. public class BaseHibernateDAO implements IBaseHibernateDAO {  
  7.       
  8.     public Session getSession() {  
  9.         return HibernateSessionFactory.getSession();  
  10.     }  
  11.       
  12. }  

IBaseHibernateDAO 不用改,沿用

[java]  view plain copy
  1. package demo.myssh.dao;  
  2.   
  3. import org.hibernate.Session;  
  4.   
  5. public interface IBaseHibernateDAO {  
  6.     public Session getSession();  
  7. }  

HibernateSessionFactory这个类要修改,因为hibernate 4 创建session的方法变了。

[java]  view plain copy
  1. package demo.myssh.dao;  
  2.   
  3. import org.hibernate.HibernateException;  
  4. import org.hibernate.Session;  
  5. import org.hibernate.cfg.Configuration;  
  6. import org.hibernate.service.ServiceRegistry;  
  7. import org.hibernate.service.ServiceRegistryBuilder;  
  8.   
  9. public class HibernateSessionFactory {  
  10.   
  11.     private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";  
  12.     private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();  
  13.     private static Configuration configuration = new Configuration();  
  14.     private static org.hibernate.SessionFactory sessionFactory;  
  15.     private static String configFile = CONFIG_FILE_LOCATION;  
  16.     private static ServiceRegistry serviceRegistry;  
  17.   
  18.     static {  
  19.         try {  
  20.             //hibernate 3 的方法  
  21.             // configuration.configure(configFile);  
  22.             // sessionFactory = configuration.buildSessionFactory();  
  23.   
  24.             //hibernate 4 的方法  
  25.             serviceRegistry = new ServiceRegistryBuilder().applySettings(  
  26.                     configuration.configure().getProperties())  
  27.                     .buildServiceRegistry();  
  28.             sessionFactory = configuration.buildSessionFactory(serviceRegistry);  
  29.         } catch (HibernateException e) {  
  30.             System.err.println("%%%% Error Creating SessionFactory %%%%");  
  31.             e.printStackTrace();  
  32.         }  
  33.     }  
  34.   
  35.     private HibernateSessionFactory() {  
  36.     }  
  37.   
  38.     public static Session getSession() throws HibernateException {  
  39.         Session session = (Session) threadLocal.get();  
  40.   
  41.         if (session == null || !session.isOpen()) {  
  42.             if (sessionFactory == null) {  
  43.                 rebuildSessionFactory();  
  44.             }  
  45.             session = (sessionFactory != null) ? sessionFactory.openSession()  
  46.                     : null;  
  47.             threadLocal.set(session);  
  48.         }  
  49.   
  50.         return session;  
  51.     }  
  52.   
  53.   
  54.     public static void rebuildSessionFactory() {  
  55.         try {  
  56.             // configuration.configure(configFile);  
  57.             // sessionFactory = configuration.buildSessionFactory();  
  58.   
  59.             serviceRegistry = new ServiceRegistryBuilder().applySettings(  
  60.                     configuration.configure().getProperties())  
  61.                     .buildServiceRegistry();  
  62.             sessionFactory = configuration.buildSessionFactory(serviceRegistry);  
  63.         } catch (HibernateException e) {  
  64.             System.err.println("%%%% Error Creating SessionFactory %%%%");  
  65.             e.printStackTrace();  
  66.         }  
  67.     }  
  68.   
  69.   
  70.     public static void closeSession() throws HibernateException {  
  71.         Session session = (Session) threadLocal.get();  
  72.         threadLocal.set(null);  
  73.   
  74.         if (session != null) {  
  75.             session.close();  
  76.         }  
  77.     }  
  78.   
  79.   
  80.     public static org.hibernate.SessionFactory getSessionFactory() {  
  81.         return sessionFactory;  
  82.     }  
  83.   
  84.     public static void setConfigFile(String configFile) {  
  85.         HibernateSessionFactory.configFile = configFile;  
  86.         sessionFactory = null;  
  87.     }  
  88.   
  89.   
  90.     public static Configuration getConfiguration() {  
  91.         return configuration;  
  92.     }  
  93.   
  94. }  

User实体类不用改,不过还是贴出来吧。

[java]  view plain copy
  1. package demo.myssh.model;  
  2.   
  3. public class User implements java.io.Serializable {  
  4.   
  5.     private static final long serialVersionUID = -8290754809696899650L;  
  6.     private Long userID;  
  7.     private String loginName;  
  8.     private String email;  
  9.     private String password;  
  10.   
  11.   
  12.     /** default constructor */  
  13.     public User() {  
  14.     }  
  15.   
  16.     /** full constructor */  
  17.     public User(String loginName, String email, String password) {  
  18.         this.loginName = loginName;  
  19.         this.email = email;  
  20.         this.password = password;  
  21.     }  
  22.   
  23.     // Property accessors  
  24.   
  25.     public Long getUserID() {  
  26.         return this.userID;  
  27.     }  
  28.   
  29.     public void setUserID(Long userID) {  
  30.         this.userID = userID;  
  31.     }  
  32.   
  33.     public String getLoginName() {  
  34.         return this.loginName;  
  35.     }  
  36.   
  37.     public void setLoginName(String loginName) {  
  38.         this.loginName = loginName;  
  39.     }  
  40.   
  41.     public String getEmail() {  
  42.         return this.email;  
  43.     }  
  44.   
  45.     public void setEmail(String email) {  
  46.         this.email = email;  
  47.     }  
  48.   
  49.     public String getPassword() {  
  50.         return this.password;  
  51.     }  
  52.   
  53.     public void setPassword(String password) {  
  54.         this.password = password;  
  55.     }  
  56.   
  57. }  

还有映射文件,记得添加下package,让hibernate能找到实体类

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC    
  3.         "-//Hibernate/Hibernate Mapping DTD 3.0//EN"    
  4.         "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">  
  5.   
  6. <hibernate-mapping  package="demo.myssh.model">  
  7.     <class name="User" table="t_user" catalog="myssh">  
  8.         <id name="userID" type="java.lang.Long">  
  9.             <column name="pk_user_id" />  
  10.             <generator class="identity"></generator>  
  11.         </id>  
  12.         <property name="loginName" type="java.lang.String">  
  13.             <column name="login_name"  length="45"/>  
  14.         </property>  
  15.         <property name="email" type="java.lang.String">  
  16.             <column name="email"  length="45"/>  
  17.         </property>  
  18.         <property name="password" type="java.lang.String">  
  19.             <column name="password"  length="45"/>  
  20.         </property>  
  21.     </class>  
  22. </hibernate-mapping>  

然后是hibernate的配置文件,

[html]  view plain copy
  1. <?xml version='1.0' encoding='utf-8'?>  
  2. <!DOCTYPE hibernate-configuration PUBLIC  
  3.     "-//Hibernate/Hibernate Configuration DTD//EN"  
  4.     "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">  
  5.   
  6. <hibernate-configuration>  
  7.     <session-factory>  
  8.         <!-- properties -->  
  9.         <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>  
  10.         <property name="connection.driver_class">com.mysql.jdbc.Driver</property>  
  11.   
  12.         <property name="connection.url">jdbc:mysql://localhost:3306/myssh</property>  
  13.         <property name="connection.username">root</property>  
  14.         <property name="connection.password"></property>  
  15.   
  16.         <property name="connection.autocommit">true</property>  
  17.   
  18.         <!-- mapping files -->  
  19.         <mapping resource="demo/myssh/model/User.hbm.xml" />  
  20.     </session-factory>  
  21. </hibernate-configuration>  
这里注意,要添加autocommit属性,因为是用的myeclipse提供的sessionFactory。mapping路径也不要忘了。


最后,修改spring的配置文件applicationContext.xml,提供注入

[html]  view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
  4.     xmlns:util="http://www.springframework.org/schema/util"  
  5.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7.      http://www.springframework.org/schema/util  
  8.      http://www.springframework.org/schema/util/spring-util-3.0.xsd">  
  9.   
  10.   
  11.     <bean id="userAction" class="demo.myssh.action.UserAction">  
  12.         <property name="userService" ref="userService" />  
  13.     </bean>  
  14.   
  15.     <bean id="userService" class="demo.myssh.business.UserService">  
  16.         <property name="userDAO" ref="userDAO"></property>  
  17.     </bean>  
  18.   
  19.     <bean id="userDAO" class="demo.myssh.dao.UserDAO">  
  20.     </bean>  
  21.       
  22. </beans>  

运行访问:http://localhost:8080/user/add.jsp

提交以后,又转回index.jsp,查看数据库

大功告成,最基本简单的ssh框架搭建终于完成。


程序结构再贴下




六、加入log4j

log4j是个很多人喜欢用的日志工具,随大流咯。

log4j的最新版是2.0-beat3,稳妥起见,我用了log4j 1.2.17,下载地址:http://logging.apache.org/log4j/1.2/download.html

导入包,在src目录下添加log4j的配置文件:log4j.properties,下面这个是网络上下载来的,看起来不错。

[plain]  view plain copy

    1. #------------------------------------------------------------------------------  
    2. #  
    3. #  The following properties set the logging levels and log appender.  The  
    4. #  log4j.rootCategory variable defines the default log level and one or more  
    5. #  appenders.  For the console, use 'S'.  For the daily rolling file, use 'R'.  
    6. #  For an HTML formatted log, use 'H'.  
    7. #  
    8. #  To override the default (rootCategory) log level, define a property of the  
    9. #  form (see below for available values):  
    10. #  
    11. #        log4j.logger. =  
    12. #  
    13. #    Available logger names:  
    14. #      TODO  
    15. #  
    16. #    Possible Log Levels:  
    17. #      FATAL, ERROR, WARN, INFO, DEBUG  
    18. #  
    19. #------------------------------------------------------------------------------  
    20. log4j.rootCategory=INFO, S  
    21.   
    22. log4j.logger.com.dappit.Dapper.parser=ERROR  
    23. log4j.logger.org.w3c.tidy=FATAL  
    24.   
    25. #------------------------------------------------------------------------------  
    26. #  
    27. #  The following properties configure the console (stdout) appender.  
    28. #  See http://logging.apache.org/log4j/docs/api/index.html for details.  
    29. #  
    30. #------------------------------------------------------------------------------  
    31. log4j.appender.S = org.apache.log4j.ConsoleAppender  
    32. log4j.appender.S.layout = org.apache.log4j.PatternLayout  
    33. log4j.appender.S.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n  
    34.   
    35. #------------------------------------------------------------------------------  
    36. #  
    37. #  The following properties configure the Daily Rolling File appender.  
    38. #  See http://logging.apache.org/log4j/docs/api/index.html for details.  
    39. #  
    40. #------------------------------------------------------------------------------  
    41. log4j.appender.R = org.apache.log4j.DailyRollingFileAppender  
    42. log4j.appender.R.File = logs/bensApps.log  
    43. log4j.appender.R.Append = true  
    44. log4j.appender.R.DatePattern = '.'yyy-MM-dd  
    45. log4j.appender.R.layout = org.apache.log4j.PatternLayout  
    46. log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n  
    47.   
    48. #------------------------------------------------------------------------------  
    49. #  
    50. #  The following properties configure the Rolling File appender in HTML.  
    51. #  See http://logging.apache.org/log4j/docs/api/index.html for details.  
    52. #  
    53. #------------------------------------------------------------------------------  
    54. log4j.appender.H = org.apache.log4j.RollingFileAppender  
    55. log4j.appender.H.File = logs/bensApps.html  
    56. log4j.appender.H.MaxFileSize = 100KB  
    57. log4j.appender.H.Append = false  
    58. log4j.appender.H.layout =org.apache.log4j.HTMLLayout  


在hibernate的配置里面,添加

[html]  view plain copy

    1. <property name="show_sql">true</property>  
    2. <property name="format_sql">true</property>  


这样,就能在控制台显示sql语句了,而且还排了下版。


七、加入c3p0连接池并改用spring提供的sessionFactory

前面的那个,虽然是将ssh整合在一起了,但是,是通过myeclipse自动编写的一个sessionFactory来访问,显然不够,现在加入c3p0链接池并修改成使用spring的sessionFactory来操作数据。


c3p0是一个第三方的链接池,hibernate自己貌似没做。不过在hibernate的压缩包里面,有个c3p0的目录,既然hibernate给出来了,就不用再找了。

spring的sessionFactory是用代理的方式重新包装了hibernate的sessionFactory,所以,还要引入spring的aop包,此外还要引入另外两个第三方的包

aopalliance 和 cglib-nodep。两个都没搜到官方网站,就有下载地址。


修改applicationContext.xml

[html]  view plain copy
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    4.     xmlns:p="http://www.springframework.org/schema/p"  
    5.     xmlns:util="http://www.springframework.org/schema/util"  
    6.     xmlns:tx="http://www.springframework.org/schema/tx"  
    7.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
    8.      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    9.      http://www.springframework.org/schema/util  
    10.      http://www.springframework.org/schema/util/spring-util-3.0.xsd  
    11.      http://www.springframework.org/schema/tx   
    12.      http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  
    13.   
    14.     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"  
    15.         destroy-method="close">  
    16.         <property name="driverClass" value="com.mysql.jdbc.Driver" />  
    17.         <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/myssh" />  
    18.         <property name="user" value="root" />  
    19.         <property name="password" value="" />  
    20.   
    21.         <!-- 连接池中保留的最小连接数。 -->  
    22.         <property name="minPoolSize" value="5" />  
    23.         <!-- 连接池中保留的最大连接数。Default: 15 -->  
    24.         <property name="maxPoolSize" value="15" />  
    25.         <!-- 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->  
    26.         <property name="initialPoolSize" value="3" />  
    27.         <!-- 最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->  
    28.         <property name="maxIdleTime" value="10" />  
    29.         <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->  
    30.         <property name="acquireIncrement" value="3" />  
    31.         <!-- JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。   
    32.             如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0 -->  
    33.         <property name="maxStatements" value="0" />  
    34.         <!-- 每60秒检查所有连接池中的空闲连接。Default: 0 -->  
    35.         <property name="idleConnectionTestPeriod" value="60" />  
    36.         <!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->  
    37.         <property name="acquireRetryAttempts" value="30" />  
    38.         <!-- 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试   
    39.             获取连接失败后该数据源将申明已断开并永久关闭。Default: false -->  
    40.         <property name="breakAfterAcquireFailure" value="false" />  
    41.         <!-- 因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的 时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable   
    42.             等方法来提升连接测试的性能。Default: false -->  
    43.         <property name="testConnectionOnCheckout" value="false" />  
    44.     </bean>  
    45.   
    46.     <!-- Hibernate SessionFactory -->  
    47.     <bean id="sessionFactory"  
    48.         class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">  
    49.         <property name="dataSource">  
    50.             <ref local="dataSource" />  
    51.         </property>  
    52.         <property name="mappingDirectoryLocations">  
    53.             <list>  
    54.                 <value>classpath*:/demo/myssh/model</value>  
    55.             </list>  
    56.         </property>  
    57.         <property name="hibernateProperties">  
    58.             <props>  
    59.                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>  
    60.                 <prop key="hibernate.show_sql">true</prop>  
    61.                 <prop key="hibernate.format_sql">true</prop>  
    62.                 <prop key="hibernate.generate_statistics">true</prop>  
    63.                 <prop key="hibernate.autoReconnect">true</prop>  
    64.                 <prop key="hibernate.max_fech_depth">5</prop>  
    65.                 <prop key="hibernate.jdbc.batch_size">50</prop>  
    66.                 <prop key="hibernate.jdbc.fetch_size">100</prop>  
    67.             </props>  
    68.         </property>  
    69.     </bean>  
    70.       
    71.     <tx:annotation-driven />   
    72.   
    73.     <bean id="transactionManager"  
    74.         class="org.springframework.orm.hibernate4.HibernateTransactionManager">  
    75.         <property name="sessionFactory" ref="sessionFactory" />  
    76.     </bean>  
    77.       
    78.       
    79.   
    80.     <bean id="userAction" class="demo.myssh.action.UserAction">  
    81.         <property name="userService" ref="userService" />  
    82.     </bean>  
    83.   
    84.     <bean id="userService" class="demo.myssh.business.UserService">  
    85.         <property name="userDAO" ref="userDAO"></property>  
    86.     </bean>  
    87.   
    88.     <bean id="userDAO" class="demo.myssh.dao.UserDAO">  
    89.         <property name="sessionFactory" ref="sessionFactory"></property>  
    90.     </bean>  
    91.   
    92. </beans>  

demo.myssh.dao下面只留下UserDAO,其他都删掉。

spring的sessionFactory自动打开session会预期一个spring的事务,所以,要么,将所有DAO方法放进事务,要么,自己手动open一下session。我选择了前者。

[java]  view plain copy

    1. package demo.myssh.dao;  
    2.   
    3. import java.util.List;  
    4. import org.hibernate.LockMode;  
    5. import org.hibernate.Query;  
    6. import org.hibernate.SessionFactory;  
    7. import org.hibernate.criterion.Example;  
    8. import org.springframework.transaction.annotation.Transactional;  
    9.   
    10. import demo.myssh.model.User;  
    11.   
    12. public class UserDAO {  
    13.   
    14.     // property constants  
    15.     public static final String LOGIN_NAME = "loginName";  
    16.     public static final String EMAIL = "email";  
    17.     public static final String PASSWORD = "password";  
    18.       
    19.     private SessionFactory sessionFactory;  
    20.   
    21.     public void setSessionFactory(SessionFactory sessionFactory) {  
    22.         this.sessionFactory = sessionFactory;  
    23.     }  
    24.   
    25.     @Transactional   
    26.     public void save(User transientInstance) {  
    27.       
    28.         //HibernateTemplate   
    29.         try {  
    30.             //getSession().save(transientInstance);  
    31.             this.sessionFactory.getCurrentSession().save(transientInstance);  
    32.   
    33.   
    34.         } catch (RuntimeException re) {  
    35.   
    36.             throw re;  
    37.         }  
    38.     }  
    39. }  

这样算是一个完整的SSH框架搭建完成了。

撒花。。。。。。


八、现在对SSH框架的理解

不多说了,上图


顺便发点牢骚:

java和.net在开发web项目的时候,.net是商业软件,由一方提供全套解决方案,你面对的文档是一种,而且微软的文档中经常有例子。而java是开源的,是由多方提供不同构件组成的,你需要面对各种格式规范不一样的文档,在文档查找和阅读方面,真的是非常头疼啊。现在做的这个东西,已经用到了由9个不懂厂商/人提供的jar包,苦啊。

当然,就向技术总监说的,.net这种一方提供的东西,他做好了也就好了,如果他没做好,你想解决很困难。java这种多方提供的东西,这个不行你还能换另外一个,总会有解决。

九、Junit4单元测试

框架完成,开始一点一点添加其他内容。

myeclipse10自带有junit4,直接用就好,当然如果要下载也行。https://github.com/KentBeck/junit/downloads

在之前的基础上,我将dao和service层都改成了接口调用,其他没变。


对UserDAO进行测试,在myeclipse里面直接添加junit test case就好,然后再引入spring的test包:org.springframework.test-3.1.3.RELEASE

UserDAOImplTest代码如下

[java]  view plain copy

    1. package demo.myssh.dao.impl;  
    2.   
    3. import org.junit.Before;  
    4. import org.junit.Test;  
    5. import org.junit.runner.RunWith;  
    6. import org.springframework.beans.factory.annotation.Autowired;  
    7. import org.springframework.beans.factory.annotation.Qualifier;  
    8. import org.springframework.test.annotation.Repeat;  
    9. import org.springframework.test.context.ContextConfiguration;  
    10. import org.springframework.test.context.junit4.*;  
    11. import demo.myssh.dao.IUserDAO;  
    12. import demo.myssh.model.User;  
    13.   
    14. @RunWith(SpringJUnit4ClassRunner.class)  
    15. @ContextConfiguration({"file:WebRoot/WEB-INF/applicationContext.xml"})  
    16. public class UserDAOImplTest {  
    17.   
    18.     @Autowired  
    19.     @Qualifier("user")  
    20.     private User user;  
    21.       
    22.     @Autowired  
    23.     @Qualifier("iUserDAO")  
    24.     private IUserDAO userDao;  
    25.       
    26.     @Before  
    27.     public void setUp() throws Exception {  
    28.         user.setEmail("1email");  
    29.         user.setLoginName("1login name");  
    30.         user.setPassword("1assword");  
    31.     }  
    32.       
    33.     @Test  
    34.     @Repeat(5)  
    35.     public final void testSave() {  
    36.         userDao.save(user);  
    37.         //fail("Not yet implemented");   
    38.     }  
    39. }  

选择文件,run as -- junit test,

简单的测试,这样就算ok了。


十、Junit+GroboUtils进行多线程测试

Junit4不能模拟多线程的情况,需要其他支持,我用的是GroboUtils,最新版本5,下载地址:http://groboutils.sourceforge.net/downloads.html


GroboUtils测试的代码是用网上抄来的,来源:http://www.coderli.com/multi-thread-junit-grobountils

UserDAOImplTest的代码

[java]  view plain copy
    1. package demo.myssh.dao.impl;  
    2.   
    3. import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;  
    4. import net.sourceforge.groboutils.junit.v1.TestRunnable;  
    5. import org.junit.Before;  
    6. import org.junit.Test;  
    7. import org.junit.runner.RunWith;  
    8. import org.springframework.beans.factory.annotation.Autowired;  
    9. import org.springframework.beans.factory.annotation.Qualifier;  
    10. import org.springframework.test.annotation.Repeat;  
    11. import org.springframework.test.context.ContextConfiguration;  
    12. import org.springframework.test.context.junit4.*;  
    13. import demo.myssh.dao.IUserDAO;  
    14. import demo.myssh.model.User;  
    15.   
    16. @RunWith(SpringJUnit4ClassRunner.class)  
    17. @ContextConfiguration({ "file:WebRoot/WEB-INF/applicationContext.xml" })  
    18. public class UserDAOImplTest {  
    19.   
    20.     @Autowired  
    21.     @Qualifier("iUserDAO")  
    22.     private IUserDAO userDao;  
    23.   
    24.     @Test  
    25.     @Repeat(2)  
    26.     public void MultiRequestsTest() {  
    27.         // 构造一个Runner  
    28.         TestRunnable runner = new TestRunnable() {  
    29.             @Override  
    30.             public void runTest() throws Throwable {  
    31.                 // 测试内容  
    32.                 // System.out.println("a");  
    33.                 userDao.save(new User("aa""bb""cc"));  
    34.             }  
    35.         };  
    36.         int runnerCount = 2;  
    37.         // Rnner数组,相当于并发多少个。  
    38.         TestRunnable[] trs = new TestRunnable[runnerCount];  
    39.         for (int i = 0; i < runnerCount; i++) {  
    40.             trs[i] = runner;  
    41.         }  
    42.         // 用于执行多线程测试用例的Runner,将前面定义的单个Runner组成的数组传入  
    43.         MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(trs);  
    44.         try {  
    45.             // 开发并发执行数组里定义的内容  
    46.             mttr.runTestRunnables();  
    47.         } catch (Throwable e) {  
    48.             e.printStackTrace();  
    49.         }  
    50.     }  
    51. }  

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值