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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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裏面建個表

  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy

    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的配置裏面,添加

    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

    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。我選擇了前者。


    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代碼如下

    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的代碼

    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. }  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章