<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    JAVA—咖啡館

    ——歡迎訪問rogerfan的博客,常來《JAVA——咖啡館》坐坐,喝杯濃香的咖啡,彼此探討一下JAVA技術,交流工作經驗,分享JAVA帶來的快樂!本網站部分轉載文章,如果有版權問題請與我聯系。

    BlogJava 首頁 新隨筆 聯系 聚合 管理
      447 Posts :: 145 Stories :: 368 Comments :: 0 Trackbacks
    spring、Hibernate、Struts組建輕量級架構 ____輕量級J2EE架構

    版本0.1 版權 © 2005 成湘均

    --------------------------------------------------------------------------------

    目錄

    前言
    1. 整體框架
    1.1. UI層
    1.2. 業務層
    1.3. 數據持久層
    1.4. 域對象層
    2. Struts
    2.1. 概述
    2.2. 快速入門
    2.3. ActionForm
    2.4. Action
    2.5. 客戶端驗證
    3. springFramework
    3.1. 概述
    3.2. 為什么使用Spring
    3.3. 快速入門
    3.4. 搭建Web應用環境
    3.5. Spring IOC
    3.6. Spring AOP
    4. hibernate
    4.1. 概述
    4.2. 為什么使用Hibernate
    4.3. 快速入門
    4.4. 工具的配合使用
    4.4.1. 生成影射文件
    4.4.2. 生成映射對象
    4.4.3. 根據映射對象生成映射文件
    4.5. 專用詞
    4.5.1. cascade(級聯)
    4.5.2. inverse(反轉)
    4.5.3. Lazy Loading(延時裝載)
    4.6. 一對一表關聯操作
    4.7. 多對一表關聯操作
    4.8. 一對多表關聯操作
    4.9. 多對多表關聯操作
    4.10. 與spring的結合使用
    4.11. Hibernate DAO
    5. log4j
    5.1. 概述
    5.2. 快速入門
    前言
    在java的應用領域,有許多成熟的開源軟件,利用它們一樣可以打造優越、靈巧的應用框架,本文首先將先介紹 所要構建的系統結構和借助的開源產品。然后逐一講解各開源軟件的具體運用。希望本文能給那些正在學習這些 開源軟件的同行提供參考。續該文之后筆者將結合這些開源軟件,借助實際項目,做更深入的應用講解。

    本文中涉及到的例子、源代碼均經過本人調試,且正常運行的,但是不否定這其中有不合理的使用。運行環境: win2000 adivance server + tomcat5.0.25 + mysql-4.0.14-nt

    第 1 章 整體框架
    筆者在項目中所搭建的架構參考了 “Mark Eagle” 的《Wiring Your Web Application with Open Source Java》 一文中所闡述的架構思想。


    圖 1.1. 架構圖:引用《Wiring Your Web Application with Open Source Java》一文

    從架構圖中可以看出系統分為四層:

    UI層:借助Struts實現

    業務層:借助SpringFramework進行業務組件的組裝關聯。

    數據持久層:借助Hibernate實現

    域對象層:將所有域對象劃分在一個層面

    為什么采用這樣的四層架構?

    通過成熟的開源產品實現各層,同自己編寫代碼實現,相比之下能縮短開發周期,且架構所用到的開源產品均有 很廣泛的用戶群,經受過實踐的考驗,質量和性能更有保障。

    層與層之間松散耦合,增加代碼重用率。

    各層分工明確,這樣也利于團隊的明確分工。

    1.1. UI層
    UI是User Interface的縮寫,這一層是面向用戶的界面,是用戶與系統之間交互的媒介。如,用戶在界面發送請求, 系統接收請求,進行處理,然后通過界面將結果呈現于用戶。這一過程包括了用戶動作、數據傳遞、界面顯示。大 家熟悉的MVC模式就是將這三者分離,減少三者耦合。

    我們在該層借助了Struts來實現:

    用ActionForm類封裝與用戶互動的數據元素。

    用Action類實現業務邏輯、動作處理、鏈接轉向。實現MVC中的C

    借助Struts標簽來完成數據呈現。實現MVC中的V。

    關于struts具體的應用請參考相關章節。

    1.2. 業務層
    在實際的項目開發中,每個領域都會有自己獨特的業務邏輯,正因為這樣,致使項目中代碼高度耦合,原本有可能被重用的代碼 或功能,因為與具體的業務邏輯綁定在一塊而導致很難被重用。因此我們將實現這些具體邏輯的代碼抽取出來分為單獨的一層, 其目的是希望通過層,來降低它與系統其他部分的耦合度。

    現實中世界是變化的,既然該層實現的是現實中具體的業務邏輯,那該層的實現代碼不可避免的會發生變更。怎樣讓該層適應 最大的變化,做到最小的改動?通常我們在編碼的時候會盡量考慮到同一業務多種實現的兼容和可擴展的能力。因此我們在 該層借助了Spring,通過依賴注入、AOP應用、面向接口編程,來降低業務組件之間的耦合度,增強系統擴展性。

    關于Spring的具體使用請參考相關章節。

    1.3. 數據持久層
    開發中與數據庫進行數據交互必不可少,通常我們歸為CRUD(添加、讀取、修改、刪除),這些操作占據了系統開發中大部分的時間, 同時我們還需要考慮與數據庫交互的性能問題,如連接池、數據緩存等等。因此該層實現我們借助了Hibernate。

    Hibernate是一個ORM工具,它不僅僅是實現了數據庫訪問性能優化和與數據庫交互的常用操作(CRUD),還將數據表與對象進行了 關聯,讓我們可以脫離數據表,而直接針對對象來與數據庫交互,我們不再需要用字符串去描述表中字段,不再需要一個個”+“號去 組裝Sql語句。這使得編碼中可書寫性提高。

    該層的具體實現,請參看Hibernate相關章節。

    1.4. 域對象層
    該層應該說是ORM思想的產物,ORM用對象關聯數據表,我們將這些對象的集合歸為一個專門的層即Domain Layer。 域對象是各層之間數據通信的載體。實際上域對象也是一個完完全全的業務對象,如User對象、Book對象。通過對業 務的對象化,這有利于業務邏輯的重用。

    第 2 章 Struts
    2.1. 概述
    Struts是用于實現Web項目中UI層的開源產品,是MVC模式的經典實現案例。它屬于Apache組織中的開源產品之一, 我們可以從官方網站http://struts.apache.org得到其所有資料及源碼。

    為什么使用Struts?

    Struts將業務數據、頁面顯示、動作處理進行分離,這有利各部分的維護。

    Struts采用Front Controller模式來實現動作處理,讓所有的動作請求都是經過一個統一入口, 然后進行分發。這樣方便我們在入口中加入一些全局控制代碼的實現,如安全控制、日志管理、國際化 編碼等。

    通過Struts提供的ActionForm封裝web form 中的元素,使重用web表單成為可能。

    借助Struts Validator框架幫助完成web層的驗證工作,通常情況下我們不用再去為每個web頁面寫其驗證代碼,只需通 過配置即可實現。這也減少了我們的開發量,由于驗證代碼的集中管理,也為維護帶來便利。

    2.2. 快速入門
    1、下載Struts,將其相關jar包引用到Web項目。

    2、在web項目的web.xml中配置Action影射,將相應請求動作交給Struts提供的ActionServlet類進行統一控制。

    例 2.1. web.xml

    程序代碼 程序代碼
    <servlet>
     <servlet-name>action</servlet-name>
     <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
     <init-param>
     <param-name>config</param-name>
     <param-value>/WEB-INF/struts-config.xml</param-value>
     </init-param>
     <init-param>
     <param-name>debug</param-name>
     <param-value>2</param-value>
     </init-param>
     <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
    </servlet-mapping>


       
    在web.xml中定義了所有帶.do后綴的文件請求都會觸發ActionServlet類。在url-pattern節點可以靈活定義 適合自身的映射表達式,如,對某個目錄下請求的映射:/myDirectory/*.do

    在配置org.apache.struts.action.ActionServlet類時設置一些參數其含義如下:

    config:制定Struts配置文件路徑,默認為/WEB-INF/struts-config.xml

    debug:設定日志記錄級別。

    3、在web.xm配置所需要用到的的Struts標簽文件

    例 2.2. web.xml

    程序代碼 程序代碼
    <taglib>
    <taglib-uri>struts-bean.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>struts-html.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>struts-logic.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>struts-template.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-template.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>struts-tiles.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>c.tld</taglib-uri>
    <taglib-location>/WEB-INF/c.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>Validate.tld</taglib-uri>
    <taglib-location>/WEB-INF/Validate.tld</taglib-location>
    </taglib>


       
    4、建立ActionForm、Action。

    例 2.3. struts-config.xml

    程序代碼 程序代碼
    <struts-config>
     
     <form-beans>
      <form-bean name="helloWorldForm" type ="struts.test.HelloWorldForm">
       <form-property name="name" type="java.lang.String" />
      </form-bean>
     </form-beans>

     <action-mappings>
      <action path="/helloWorld" name="helloWorldForm" type="struts.test.HelloWorldAction"
      scope="request" input="/HelloWorld.jsp" validate="false" />
    </action-mappings>

    </struts-config>


       
    例 2.4. HelloWorldForm.java

    程序代碼 程序代碼
    public class HelloWorldForm extends ActionForm
    {
     private String name = null;
      
     public String getName()
     {
      return this.name;
     }
     public void setName(String name)
     {
      this.name = name;
     }
    }
       
       
    例 2.5. HelloWorldAction.java

    程序代碼 程序代碼
    public class HelloWorldAction extends Action
    {

     public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
       HttpServletResponse response) throws Exception
     {
      HelloWorldForm loginForm = (HelloWorldForm)form;
      String name = loginForm.getName();
      request.setAttribute("HelloWorldForm",loginForm);
      return mapping.getInputForward();
     }

    }

       
    5、視圖呈現

    例 2.6. HelloWorld.jsp

    程序代碼 程序代碼
    <%@page language="java" contentType = "text/html;charset=GBK"%>
    <%@ taglib uri="struts-html.tld" prefix="html" %>
    <%@ taglib uri="struts-tiles.tld" prefix="tiles" %>
    <%@ taglib uri="struts-bean.tld" prefix="bean" %>
    <table border="1">
     <tr>
      <html:form action="helloWorld.do" method="post">
      <td>
       <table>
        <tr>
         <td>用戶名:</td>
         <td><html:text property="name" /></td>
        </tr>
        <tr colspan="2">
         <td><html:submit property="show" value="提交" /></td>
        </tr>
       </table>
      </td>
      </html:form>
     </tr>
    </table>
    <bean:write name="name" />


       
    6、演示效果


    圖 2.1. 訪問Helloworld.jsp

    本章演示了一個簡單的示例,在文本框輸入內容,提交后顯示其內容。功能雖然很簡單但是涉及到了struts應用的大部分知識:

    在web.xml中配置Action影射規則,如,*.do文件的請求觸發Action。

    ActionFrom中的屬性對應到表單域中表單元素,如,jsp頁面名為name的Text,對應到ActionForm中name屬性。

    繼承Action類后即可實現具體的控制器,如,HelloAction類接受到請求后將ActionForm對象存放到request 范圍,然后轉發給配置的鏈接地址。

    借助struts提供的標簽進行視圖呈現。如,bean:write 標簽訪問ActionForm對象的屬性值。

    2.3. ActionForm
    ActionFrom是用來傳輸表單數據的對象,通過配置文件可以關聯到對應的Action,實現在UI層與業務層之間的數據傳輸。 實現機制如下:

    Struts提供了一個org.apache.struts.action.ActionForm類,里面實現了將請求表單中的元素賦值給其具體的 實現類屬性。因此自定義ActionForm類時,只需繼承該類即可。在自定義ActionForm類時請保證其屬性名稱與所對應 表單名稱一致。

    當觸發Action時,通過配置文件匹配到對應的ActionFrom實例,以參數形式傳入。

    ActionForm的實現雖然簡單,但是隨著界面的增加,ActionForm類也會增加,造成代碼膨脹。在Struts1.1以上版本提供了 ActionForm的另一個實現類,org.apache.struts.action.DynaActionForm,該類充當所有ActionForm的代理類,只需在 配置ActionFrom時指定用該類實現即可:

    例 2.7. struts-config.xml

    程序代碼 程序代碼
    <form-beans>
      <form-bean name="helloWorldForm" type ="org.apache.struts.action.DynaActionForm">
       <form-property name="name" type="java.lang.String" />
      </form-bean>
    </form-beans>


       
    在訪問helloWorldForm實例時如下:

    例 2.8. HelloAction.java

    程序代碼 程序代碼
    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
       HttpServletResponse response) throws Exception
    {
     DynaActionForm dynaForm = (DynaActionForm)form;
     String name =(String)dynaForm.get("name");
     return null;
    }
       
    2.4. Action
    Action充當了控制器的角色,每個請求都會觸發到Action類,我們在這里處理邏輯業務,分發數據,鏈接轉向。 其實現機制:

    在web.xml配置影射規則,符合其影射條件的請求都會交給org.apache.struts.action.ActionServlet類處理, 在該類中將會實現org.apache.struts.action.Action類中的相應方法。在自定義Action類時只需繼承該類, 即可讓自定義的Action得到觸發。

    execute()方法中 ActionMapping、ActionForm實例通過讀取配置文件獲得。

    2.5. 客戶端驗證
    在UI中對用戶提交的數據作驗證是必不可少的,Struts也提供了相應的實現。下面將演示一個登錄界面的校驗:

    1、在配置文件中申明校驗實現類,Struts中校驗工作通過org.apache.struts.validator.ValidatorPlugIn類實現。

    例 2.9. struts-config.xml

    程序代碼 程序代碼
    <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
    <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
    </plug-in>


       
    在實例化ValidatorPlugIn類時會裝載validator-rules.xml、validation.xml文件,這些文件中包含了驗證規則 和要被驗證的具體表單信息。

    2、完善校驗規則文件。在validator-rules.xml中定義了常用的客戶端驗證規則,如,不為空,只允許 數字等。特殊的實現需要在此擴充。

    3、定義要被驗證的表單信息。

    例 2.10. validation.xml

    程序代碼 程序代碼
    <form-validation>
     <formset>
      <form name="loginForm">  (1)
       <field property="name" page="0" depends="required"> (2)
        <arg0 key="register.username"/> (3)
       </field>
    </form>
    </formset>
    </form-validation>

       
    (1) 定義要被驗證的ActionForm名稱。

    (2) 定義要被驗證的具體屬性,及規則。depends="required"指明將調用validator-rules.xml中的required規則。

    (3) 從資源文件中匹配具體的驗證信息。


    4、建立存放驗證提示信息的資源文件。如當用戶名為空時顯示提示信息“必須填寫用戶名”

    例 2.11. ApplicationResources_zh_CN.properties

    register.username=\u7528\u6237\u540d
       
    5、在界面設置觸發條件,如onsubmit時響應驗證動作。

    例 2.12. Login.jsp

    程序代碼 程序代碼
    <%@page language="java" contentType = "text/html;charset=GBK"%>
    <%@ taglib uri="struts-html.tld" prefix="html" %>
    <table border="1">
     <tr>
      <html:form action="login.do" method="post" onsubmit="return validateCheckLoginForm(this);">
      <html:hidden property="loginType" value="0"/>
      <td>
       <table>
        <tr>
         <td>用戶名:</td>
         <td><html:text property="name" /></td>
        </tr>
        <tr>
         <td>密碼:</td>
         <td><html:password property="password" /></td>
        </tr>
        <tr colspan="2">
         <td><html:submit property="login" value="登錄" /></td>
        </tr>
       </table>
      </td>
      </html:form>
     </tr>
      <html:javascript formName="checkLoginForm" dynamicJavascript="true" staticJavascript="true"/>
    </table>


       
    當用戶名為空時點擊登錄,將出現如下提示:


    圖 2.2. 演示效果

    第 3 章 springFramework
    3.1. 概述
    對spring的描述莫過于作者本人之言

    Developing software applications is hard enough even with good tools and technologies. Implementing applications using platforms which promise everything but turn out to be heavy-weight, hard to control and not very efficient during the development cycle makes it even harder. Spring provides a light-weight solution for building enterprise-ready applications, while still supporting the possibility of using declarative transaction management, remote access to your logic using RMI or webservices, mailing facilities and various options in persisting your data to a database. Spring provides an MVC framework, transparent ways of integrating AOP into your software and a well-structured exception hierarchy including automatic mapping from proprietary exception hierarchies. 即使擁有良好的工具和優秀技術,應用軟件開發也是困難重重。如果使用了超重量級,難于控制,不能有效控制開 發周期的平臺 那么就讓應用開發變得更為困難。Spring為已建立的企業級應用提供了一個輕量級的解決方案,這個方案包括聲明 性事務管理, 通過RMI或webservices遠程訪問業務邏輯,mail支持工具以及對于數據和數據庫之間持久層的各種配置的支持。 Spring還提供了 一個MVC應用框架,可以通過集成AOP透明的嵌入你的軟件和一個優秀的異常處理體系,這個異常體系可以自動 從屬性異常體系 進行映射。

    --springFramework reference

    springFramework是種非侵入式輕量級框架,允許自由選擇和組裝各部分功能,還提供和其他軟件集成的接口,如與Hibernate、Struts 的集成(后面的章節中會提到)。它提供的功能有Spring IOC、spring AOP、Spring orM、Spring DAO、Spring MVC.筆者在項目 中用到的主要是IOC和AOP功能,ORM用hibernate取代,MVC用Struts取代。本文講述springFramework在web環境下的使用。

    3.2. 為什么使用Spring
    1、利用延時注入思想組裝代碼,提高系統擴展性,靈活性,實現插件式編程。

    2、利用AOP思想,集中處理業務邏輯,減少重復代碼,構建優雅的解決方案。

    3、利用其對Hibernate的SessionFactory、事務管理的封裝,更簡潔的應用Hibernate。

    3.3. 快速入門
    要使用Spring非常簡單,來體驗下:

    例 3.1. MyClass.java

    程序代碼 程序代碼
    public interface MyClass
    {
     public void execute();
    }

       
    例 3.2. MyClassImpl.java

    程序代碼 程序代碼
    public class MyClassImpl implements MyClass
    {
     public void execute()
     {
      ...
     }
    }

       
    通過Spring注入MyClassImpl的實例,需在配置文件中做如下配置:

    例 3.3. SpringConfig.xml

    程序代碼 程序代碼
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans default-lazy-init="false" default-dependency-check="none" default-autowire="no">
     <bean id="myClass" class="MyClassImpl" />
    </beans>

       
    這樣在代碼中就可以通過Spring體驗到什么叫延時裝載了

    例 3.4.

    程序代碼 程序代碼
     ApplicationContext ac = new FileSystemXmlApplicationContext("SpringConfig.xml"); (1)
     MyClass cls = (MyClass)ac.getBean("myClass");       (2) 
     cls.execute();
                  

        
    (1) 載入Spring配置文檔,上例中SpringConfig.xml放置在工作路徑根目錄中。這種引用方式其配制文件只能相對于工作路徑 的引用。

    (2) 實例化配置的對象,以配置文件的bean節點ID值作為實例引用的關鍵字。


    上面的例子中得到了實現類的實例,但是代碼中并沒有硬編碼具體實現類,而是將這種依賴轉移到配置文件中。

    3.4. 搭建Web應用環境
    1、下載springFramework最新版本http://www.springframework.org,將springFramework下的*.jar 拷貝到項目lib中,并引用。

    2、在Web.xml中配置spring的啟動方式。

    springFramework提供兩種形式的web context,基于Listener接口的實現和基于Servlet接口的實現。

    例 3.5. web.xml

    程序代碼 程序代碼
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
    <display-name>Test</display-name>
    <context-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>/WEB-INF/spring_bean.xml</param-value>
    </context-param>
    <listener>
     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!--
    <servlet>
     <servlet-name>context</servlet-name>
     <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
     <load-on-startup>2</load-on-startup>
    </servlet>
    -->
    <servlet>
     <servlet-name>Init</servlet-name>
     <servlet-class>com.m5.Initializtion</servlet-class>
     <load-on-startup>3</load-on-startup>
    </servlet>
    </web-app>
       
    tomcat啟動時會裝載/WEB-INF/spring_bean.xml文件,如果不指定contextConfigLocation參數, 默認裝載/WEB-INF/applicationContext.xml文件。然后在tomcat啟動時初始化一個自定義servlet, 在這里實現springFramework的裝載。

    例 3.6. Initializtion.java

    程序代碼 程序代碼
    public class Initializtion extends HttpServlet
    {
     public void init(ServletConfig config) throws ServletException
     {
      WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext
      (config.getServletContext());
      super.init(config); 
      InitSpring.Init((AbstractApplicationContext)wac); (1)
     }
    }

       
    (1) InitSpring是一個符合單例模式的類。


    例 3.7. InitSpring.java

    程序代碼 程序代碼
    public class InitSpring
    {
     AbstractApplicationContext wac = null;
     private static InitSpring instance = new InitSpring();
     private InitSpring()
     {
      
     }
     
     public static void Init(AbstractApplicationContext wac)
     {
      instance.wac = wac;
     }
     
     public static Object getInstance(String objName)
     {
      return instance.wac.getBean(objName);
     }
     
     public static Object getInstance(Class objClass)
     {
      return getInstance(objClass.getName());
     }
    }

      
    經過以上處理,在項目任何一個類中如需得到Spring配置文件中的一個類實例,如下即可:

    InitSpring.getObject(beanId);
      
    3.5. Spring IOC
    IOC(Inversion of Control),譯作反轉控制,其功能是將類之間的依賴轉移到外部的配置文件中, 避免在調用類中硬編碼實現類,因此也被稱作依賴注入(Dependency Injection)。在以往的開發中, 通常利用工廠模式(Factory)來解決此類問題,其實不管是工廠模式還是依賴注入,調用類與實現類不可 能沒有任何依賴,工廠模式中工廠類通常根據參數來判斷該實例化哪個實現類,Spring IOC將需要實例的 類在配置文件文件中配置。使用Spring IOC能得到工廠模式同樣的效果,而且編碼更加簡潔。看段代碼比較 一下:

    1、用工廠模式來實現

    例 3.8. Product.java

    程序代碼 程序代碼
    public interface Product
    {
     public void execute();
    }

       
    例 3.9. ConcreteProductA.java

    程序代碼 程序代碼
    public class ConcreteProductA implements Product
    {
     public void execute()
     {
      ...
     }
    }
       
    例 3.10. ConcreteProductB.java

    程序代碼 程序代碼
    public class ConcreteProductB implements Product
    {
     public void execute()
     {
      ...
     }
    }

       
    例 3.11. Factory.java

    程序代碼 程序代碼
    public class Factory
    {
     
     public Product CreateProduct(object param)
     {
      return ConstructObjects(param);
     }
     
     private Product ConstructObjects(object param)
     {
      ...
     }
    }

       
    例 3.12. Client.java(調用類)

    程序代碼 程序代碼
    public class Client
    {
     public Client()
     {
      //實例化ConcreteProductA
      Product product = Factory.CreateProduct(paramA);
      
      //實例化ConcreteProductB
      Product product = Factory.CreateProduct(paramB);
      ...
     }
    }

       
    在ConstructObjects方法中設定實例化實現類的邏輯,這樣對于調用類來說,不直接實例化實現類,縱然實現類發生變化, 調用代碼仍然可以不作修改,給維護與擴展帶來便利。

    2、Spring IOC實現

    例 3.13. SpringConfig.xml

    <bean id="productA" class="ConcreteProductA" />
    <bean id="productB" class="ConcreteProductB" />

       
    例 3.14. Client.java(調用類)

    程序代碼 程序代碼
    public class Client
    {
     public Client()
     {
      //實例化ConcreteProductA
      Product product = (Product)InitSpring.getObject("productA");
      
      //實例化ConcreteProductB
      Product product = (Product)InitSpring.getObject("productB");
      ...
     }
    }

       
    調用代碼中沒有硬編碼實現類,比較工廠模式,少了Factory類。

    Spring為依賴注入提供三種實現方式:接口注入、設值注入、構造注入。利用這些可以靈活的解決 類之間的依賴關系,讓你為所欲為的組裝代碼。與其說Spring IOC是一個工具,還不如說搭建了一 個思想的舞臺。繼續看代碼:

    來實現一個操作多個數據源的切換

    例 3.15. DataSource.java

    程序代碼 程序代碼
    public class DataSource
    {
      private String driverClassName;
      private String url;
      private String username;
      private String password;

      public String getDriverClassName()
      {
      return this.driverClassName;
      }

      public void setDriverClassName(String driverClassName)
      {
      this.driverClassName = driverClassName;
      }

      public String getUrl()
      {
      return this.url;
      }

      public void setUrl(String url)
      {
      this.url = url;
      }

      public String getUsername()
      {
      return this.Username;
      }

      public void setUsername(String username)
      {
      this.username = username;
      }

      public String getPassword()
      {
      return this.password;
      }

      public void setPassword(String password)
      {
      this.password = password;
      }
    }

       
    例 3.16. DataAccessor.java

    程序代碼 程序代碼
    public class DataAccessor
    {
     private DataSource dataSource;
     public void setDriver(DataSource dataSource)
     {
      this.dataSource = dataSource;
     }

     public void save(String sql)
     {
      Statement s = getStatement();
      try
    {
    s.getConnection().setAutoCommit(false);
       int rows = s.executeUpdate(sql);
     s.getConnection().commit();

      }
    catch(Exception e)
    {
       s.getConnection().rollback();
       ... 
    }
      finally
      {
       ...
      }
     }

     private Statement getStatement()
     {
      Statement s;
      try
      {
       Class.forName(dataSource.getDriverClassName()).newInstance();
    java.sql.Connection conn =
       java.sql.DriverManager.getConnection(dataSource.getUrl(),dataSource.getUser(),dataSource.getPassword());
    try
    {
    s = c.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
    }
      }
      catch(Exception e)
      {
       ...
      }
      return s;
     }
    }

       
    例 3.17. BussinessA.java

    程序代碼 程序代碼
    public class BussinessA
    {
     private DataAccessor dao;
     public void setDao(DataAccessor dao)
     {
      this.dao = dao;
     }

     public void execute()
     {
      dao.save("insert into tb1 ...");
     }
    }

       
    例 3.18. BussinessB.java

    程序代碼 程序代碼
    public class BussinessB
    {
     private DataAccessor dao;
     public void setDao(DataAccessor dao)
     {
      this.dao = dao;
     }

     public void execute()
     {
      dao.save("insert into tb2 ...");
     }

    }

       
    全部代碼就這樣了,執行BussinessA.java、BussinessB.java代碼即可完成數據插入操作,從代碼中看, 這兩個類具體操作的是什么數據庫?什么樣的操作細節?讓你失望了,代碼中找不到這樣的關聯,看配置文件吧:

    例 3.19. SpringConfig.xml

    程序代碼 程序代碼
    <bean id="dataSourceA" class="DataSource" destroy-method="close">
    <property name="driverClassName"><value>org.gjt.mm.mysql.Driver</value></property>
    <property name="url">
       <value>jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=GBK</value>
    </property>
    <property name="username"><value>root</value></property>
    <property name="password"><value></value></property>
    </bean>

    <bean id="dataSourceB" class="DataSource" destroy-method="close">
    <property name="driverClassName"><value>org.gjt.mm.mysql.Driver</value></property>
    <property name="url">
       <value>jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=GBK</value>
    </property>
    <property name="username"><value>root</value></property>
    <property name="password"><value></value></property>
    </bean>

    <bean id="daoA" class="DataAccessor">
      <property name="dataSource">
       <ref local="dataSourceA"/>
      </property>
    </bean>

    <bean id="daoB" class="DataAccessor">
      <property name="dataSource">
       <ref local="dataSourceB"/>
      </property>
    </bean>

    <bean id="bussinessA" class="BussinessA">
      <property name="dao">
       <ref local="daoA"/>
      </property>
    </bean>

    <bean id="bussinessB" class="BussinessB">
      <property name="dao">
       <ref local="daoB"/>
      </property>
    </bean>


       
    看完配置文件應該明白了,這里承擔了所有的依賴關系。

    首先,我們通過設值注入方法設置數據源相關參數

    然后,我們將數據源實例注入給數據訪問類

    最后,我們為每個具體業務類注入相應訪問器

    是不是感覺想玩積木似的,在組裝你的代碼?

    例 3.20. DaoTest.java

    程序代碼 程序代碼
    public void testSave()
    {
     BussinessA bussinessA = (BussinessA)InitSpring.getObject("bussinessA");
     bussinessA.execute();

     bussinessB bussinessB = (BussinessB)InitSpring.getObject("bussinessB");
     bussinessB.execute();
    }

       
    執行這段測試代碼,數據庫Test1、Test2中tb1、tb2表將分別插入對應的數據,從實現代碼來看操作多個數據庫和 操作一個數據庫完全一樣,即使當數據源,數據訪問類不斷變化,應用代碼也可以做到不用任何修改。

    希望看完本章節的內容能讓讀者與我共鳴,Spring IOC是一種優雅的思想,借助它發揮你無窮的想象吧。

    3.6. Spring AOP
    AOP的全稱為Aspect oriented Programming,譯為面向方面編程,針對具體業務領域、業務邏輯。AOP目的是 將復雜邏輯進行分離,抽取共性,讓各部分實現實現的功能更為專一。如,Spring AOP思想實現的經典案例“事務 管理”,每個訪問數據庫的業務類都有可能用到事務控制,將其事務管理代碼從具體類中抽取出來,放到一個單獨的 地方處理,這樣大大簡化了具體業務類的代碼。如圖:


    圖 3.1. AOP分離前


    圖 3.2. AOP分離后

    從圖中能清楚地看出分離的效果,下面繼續借助圖例來闡述AOP的一些觀念


    圖 3.3. 事務管理之AOP實現序列圖

    Aspect(方面):我們所關注的,可以被抽取的業務邏輯,如圖中的“事務管理”。

    JoinPoint(連接點):程序在執行過程中明確的點,如圖中execute()方法。

    Advice(通知):在特定的連接點執行的動作。如在執行execute()方法前的預處理, 在執行完execute() 方法后的后處理。

    PointCut(切入點):如圖中在客戶調用execute()時才產生圖中所示動作,還也可以 設定產生同樣動作的方法,如save(),update(),甚至申明成“save.*”,這些申明的 集合就稱之為切入點。

    TargetObject(目標對象):包含連接點的對象,也稱為被代理的對象。如圖中的“業務組件”

    理解完概念,接下來看實際例子:

    在Spring IOC章節中,DataAccessor.java的Save方法中是 未經事務管理分離的實現,我們采用AOP實現后,期望將實現代碼變成如下:

    例 3.21. DataAccessor.java

    程序代碼 程序代碼
     public void save(String sql)
     {
       Statement s = getStatement();
       int rows = s.executeUpdate(sql);
     }

       
    多么美妙的想法,看實現方法:

    1、AOP中的“方面”已經明確,即“事務管理”,通常用攔截器來實現其功能。我們可以通過實現 AOP聯盟提供的通用AOP接口MethodInterceptor自定義攔截器。本例借助Spring中提供的 org.springframework.jdbc.datasource.DataSourceTransactionManager事務管理 類實現。

    2、”目標類“我們也已經明確,即DataAccessor.java。

    3、定義切入點,該例中我們期望在執行save方法時做事務管理控制。

    4、在SpringConfig.xml文件中進行相關配置

    例 3.22. SpringConfig.xml

    程序代碼 程序代碼
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName"><value>org.gjt.mm.mysql.Driver</value></property>
    <property name="url">
       <value>jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=GBK</value>
    </property>
    <property name="username"><value>root</value></property>
    <property name="password"><value></value></property>
    </bean>

    <bean id="dataAccessor" class="DataAccessor">
      <property name="dataSource">
       <ref local="dataSource"/>
      </property>
    </bean>

    <bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> (1)
      <property name="dataSource">
       <ref local="dataSource" />
      </property>
    </bean>

    <bean id="dataAccessorProxy"
     class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> (2)
      <property name="transactionManager">      (3)
       <ref bean="transactionManager" />
      </property>
      <property name="target">         (4)
       <ref local="dataAccessor" />
      </property>
      <property name="transactionAttributes">      (5)
       <props>
        <prop key="save">PROPAGATION_REQUIRED</prop>
       </props>
      </property>
     </bean>

       
    (1) 申明事務管理的實現類。

    (2) 通過Spring提供的代理工廠類,注入AOP相關實現信息。

    (3) 注入事務實現類。

    (4) 注入目標類。

    (4) 定義切點(save)。


    經過以上的配置,改動下DataAccessor.java類的實現,因為這里我們用的dataSource類為Spring提供的 org.apache.commons.dbcp.BasicDataSource,且我們可以通過JdbcTemplate執行相關數據庫訪問操作。

    例 3.23. DataAccessor.java

    程序代碼 程序代碼
    public class DataAccessor
    {
     private DataSource dataSource;
     
     public void setDataSource(DataSource dataSource)
     {
      this.dataSource = dataSource;
     }
     
     public void save()
     {
      JdbcTemplate template = new JdbcTemplate(this.dataSource);
      template.update("insert into game (name) values ('test')");
     }
    }

      
    save()中代碼完全是我們所期望完全樣子,不用關系異常處理,不用關系事務管理,設想一下,如果項目 中有100百個這樣的業務類,那能節省你不少敲這種重復代碼的時間了,最重要的是我們讓某個業務邏輯得 到集中處理,便于日后維護與擴展。 這就是借助AOP的好處。以后面對一個復雜的業務邏輯處理時,別忘了 想想是否能借助面向方面的思想去解決問題。

    第 4 章 hibernate
    4.1. 概述
    對象到關系型數據映射(ORM)是架構中熱門的話題,hibernate是諸多優秀的ORM工具之一,使用和受推崇程度較高。 國內也有專門的hibernate網站與論壇,其中人氣最高當屬 java視線論壇, 感謝他們的無私與辛勤,以致hibernate官方發行包中多了一份中文指南。

    4.2. 為什么使用Hibernate
    1、減輕了編寫Sql語句工作量

    例 4.1. 傳統的數據庫訪問代碼

    insert into table (field_1,field_2,field_n) values('"+ field_value1 +"','" + field_value2 + "','" + field_value3 + "'")
       
    例 4.2. 借助Hibernate后數據庫訪問代碼

    session.save(table_Object)
       
    由代碼比較可以看出,數據表可以跟對象一樣被操作,這樣代碼顯得更加簡潔,可讀性也增強。在實際開發中,這里是業務變動 頻繁的地方,保證代碼的可讀性和易維護,很有價值。

    2、Hibernate封裝了數據庫訪問、事務管理、數據緩存等工作。省去了自己去編寫這些代碼。

    3、將數據表數據映射到對象中,以對象作為傳輸媒介,能更好的在系統各層傳輸數據。

    4.3. 快速入門
    在體驗hibernate前,請首先下載hibernate最新發行包,www.hibernate.org。 然后將hibernate相關jar包加入項目lib,并引用。(本章節的所有例子均以Mysql數據庫為例)

    1、建立表:

    表 4.1. book

    id name price
    1 《Basic》 12.00
    2 《Pasic》 15.00

    2、建立映射文件:

    例 4.3. book.hbm.xml

    程序代碼 程序代碼
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
    <hibernate-mapping>
    <class
    name="com.m5.Book"  (1)
    table="book"   (2)
    >

    <id
    name="id"
    type="java.lang.Integer"
    column="id"
    >
    <generator class="native" />
    </id>

    <property
    name="name"
    type="java.lang.String"
    column="name"
    not-null="true"
    length="100"
    />
    <property
    name="price"
    type="long"
    column="price"
    not-null="true"
    length="10"
    />
    </class>
    </hibernate-mapping>

       
    (1) 對應的映射類.

    (2) 對應的表。


    通常增一個表就需要增加類似上面的映射文件。(別緊張,這樣的工作可以借助工具生成,后續的內容會介紹)。

    3、建立影射對象:

    例 4.4. book.java

    程序代碼 程序代碼
    package com.m5;
    public class Book
    {
     private int id;
     private String name;
     private long price;
     
     public Book()
     {
      
     }
     
     public int getId()
     {
      return id;
     }
     
     public void setId(int id)
     {
      this.id = id;
     }
     
     public String getName()
     {
      return name;
     }
     
     public void setName(String name)
     {
      this.name = name;
     }
     
     public long getPrice()
     {
      return price; 
     }
     
     public void setPrice(long price)
     {
      this.price = price; 
     }
    }

       
    4、建立hibernate的配置文件

    例 4.5. hibernate.cfg.xml

    程序代碼 程序代碼
    <?xml version='1.0' encoding='gb2312'?>
    <!DOCTYPE hibernate-configuration
    PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
    <hibernate-configuration>
    <session-factory >
    <property name="hibernate.connection.driver_class">org.gjt.mm.mysql.Driver</property> (1)
    <property name="hibernate.connection.url">(2) 
       jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=GBK
      </property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password"></property>
    <property name="show_sql">true</property>
    <property name="dialect">net.sf.hibernate.dialect.MySQLDialect</property>
    <property name="hibernate.cglib.use_reflection_optimizer">true</property>
    <property name="hibernate.query.substitutions">true 1, false 0</property>
    <property name="hibernate.query.substitutions">male 1, female 0</property>
    <mapping resource="book.hbm.xml"/> (3)
    </session-factory>
    </hibernate-configuration>

       
    (1) 指定數據庫驅動類,該文件配置的數據庫驅動為mysql,hibernate還支持其它的數據庫,更改為對應的驅動類即可。

    (2) 數據庫的連接字符串

    (3) 指定映射文件,該文件配置的book.hbm.xml表明放在類引用路徑的根目錄。如web項目的classes目錄。


    5、數據庫訪問代碼

    例 4.6. MyHibernate.java

    程序代碼 程序代碼
    import java.io.File;
    import net.sf.hibernate.*;
    import net.sf.hibernate.cfg.Configuration;
    public class MyHibernate {

     public static void main(String[] args)
     {
      String name = "java";
      long price = 10;
      try
      {
       File file = new File("d:\\hibernate.cfg.xml"); 
       Configuration config = new Configuration().configure(file);  (1)
       SessionFactory sessionFactory = config.buildSessionFactory();
       Session session = sessionFactory.openSession();
       Book book = new Book();      
       book.setName(name);
       book.setPrice(price);
       session.save(book);    (2)
       session.flush();
      }
      catch(Exception ex)
      {
       System.out.println(ex.toString());
      }
     }
    }

       
    (1) 導入配置文件。

    (2) 將book對象進行數據庫持久,即完成insert的功能。


    執行上面的代碼,book數據表將插入一條數據,這就是ORM。hibernate的使用是不是也沒有想象中的復雜?當然 實際開發中不僅僅只會有如此簡單的數據表操作,后續章節將逐步深入講述hibernate的使用。希望該例子能讓你 對hibernate有了較直觀的理解,至少明白hibernate的映射規則:

    首先,通過影射文件設定映射類與數據表之間的結構關聯。

    然后,裝載配置文件

    最后執行操作,將數據表數據映射到對象,或將對象數據持久到數據表中。

    4.4. 工具的配合使用
    在寫本節前首先感謝夏昕提供的《hibernate開發指南》,該文中詳細的介紹了怎樣生成映射文件 和值對象,以及開發中借助工具常用的方法。

    4.4.1. 生成影射文件
    前面的例子已經介紹了,hibernate完成持久操作需要一個映射文件和影射類。從映射文件可以看出其結構類似 對數據表的描述,我們只要根據數據表就能夠找出生成映射文件的規律。于是我們有了工具來替代手動書寫, 從而減少工作量,提高開發效率。下面介紹hibernate官方提供的Middlegen-Hibernate工具。

    1、下載Middlegen-Hibernate,www.hibernate.org,解壓縮到c:\Middlegen目錄。

    2、修改Middlegen-Hibernate的配置文件,Middlegen-Hibernate通過ant來構建運行(如果對ant不熟悉請先閱讀 ant章節的內容),打開根目錄的build.xml文件:

    例 4.7. build.xml

    程序代碼 程序代碼
    <?xml version="1.0"?>
    <!DOCTYPE project [
    <!ENTITY database SYSTEM "file:./config/database/mysql.xml">  (1)
    ]>
    <project name="Middlegen Hibernate" default="all" basedir=".">



    <property file="${basedir}/build.properties"/>
    <property name="name" value="MyTest"/>      (2)
     
     ...

    <property name="build.dir" value="c:\sample"/>    (3)

     ...

      <hibernate
      destination="${build.gen-src.dir}"
      package="com.m5"       (4)     
      genXDocletTags="true"      (5)  
      genIntergratedCompositeKeys="false"
      javaTypeMapper="middlegen.plugins.hibernate.HibernateJavaTypeMapper"/>
     
     ... 

    </project>

      
    (1) 改成對應數據庫的映射文件,本例以mysql為例,因此引用mysql.xml。

    (2) 項目名稱。

    (3) 映射文件的輸出路徑。

    (4) 對應的包名。

    (5) 是否生成XDoclet標簽,這里設置成true,以便利用映射文件生成值對象。


    3、找到\config\database目錄下對應的數據庫配置文件,本文用的是mysql,因此打開mysql.xml

    例 4.8. mysql.xml

    程序代碼 程序代碼
    <property name="database.script.file" value="${src.dir}/sql/${name}-mysql.sql"/>
    <property name="database.driver.file" value="${lib.dir}/mysql-connector-java-3.0.14-production-bin.jar"/>
    <property name="database.driver.classpath" value="${database.driver.file}"/>
    <property name="database.driver" value="org.gjt.mm.mysql.Driver"/>

    <property name="database.url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&
    characterEncoding=GBK"/>  (1)     
    <property name="database.userid" value="root"/>  (2)
    <property name="database.password" value=""/>   (3)

    <property name="database.schema" value=""/>
    <property name="database.catalog" value=""/>
    <property name="jboss.datasource.mapping" value="mySQL"/>

       
        
    (1) 修改數據庫連接字符串。

    (1) 配置數據庫用戶名。

    (1) 配置數據庫密碼。


    4、進入Middlegen-Hibernate,運行ant,將會看到如下圖的界面:

    根據界面的標題進行各項對應的操作,最后單擊“Generate”按鈕即可生成映射文件。如,本文以book表為例,生成 book表的映射文件:

    例 4.9. book.hbm.xml

    程序代碼 程序代碼
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

    <hibernate-mapping>
    <!--
    Created by the Middlegen Hibernate plugin 2.1

    http://boss.bekk.no/boss/middlegen/
    http://www.hibernate.org/
    -->

    <class
    name="com.m5.Book"
    table="book"
    >
    <meta attribute="class-description" inherit="false">
    @hibernate.class
    table="book"
    </meta>

    <id
    name="id"
    type="java.lang.Integer"
    column="id"
    >
    <meta attribute="field-description">
    @hibernate.id
    generator-class="assigned"
    type="java.lang.Integer"
    column="id"


    </meta>
    <generator class="assigned" />
    </id>

    <property
    name="name"
    type="java.lang.String"
    column="name"
    not-null="true"
    length="100"
    >
    <meta attribute="field-description">
    @hibernate.property
    column="name"
    length="100"
    not-null="true"
    </meta>
    </property>
    <property
    name="price"
    type="long"
    column="price"
    not-null="true"
    length="10"
    >
    <meta attribute="field-description">
    @hibernate.property
    column="price"
    length="10"
    not-null="true"
    </meta>
    </property>

    <!-- Associations -->

    </class>
    </hibernate-mapping>


        
    4.4.2. 生成映射對象
    從book.hbm.xml文件中可以發現有類似這樣的代碼“ @hibernate.class table=‘book’”,這就是XDoclet 標簽。可以通過Hibernate Extension工具包中的hbm2java工具來轉換這些標簽生稱對應的值對象。

    1、下載Hibernate Extension工具包,http://www.hibernate.org

    2、hbm2java對本地類庫引用的配置文件為:tools\bin\setenv.bat

    例 4.10. setenv.bat

    程序代碼 程序代碼
    @echo off
    rem -------------------------------------------------------------------
    rem Setup environment for hibernate tools
    rem -------------------------------------------------------------------

    set JDBC_DRIVER=C:\jars\mysql-connector-java-3.0.14-production-bin.jar (1)
    set HIBERNATE_HOME=C:\hibernate-2.1    (2)

    set HIBERNATETOOLS_HOME=%~dp0..
    echo HIBERNATETOOLS_HOME set to %HIBERNATETOOLS_HOME%

    if "%HIBERNATE_HOME%" == "" goto noHIBERNATEHome

    set CORELIB=%HIBERNATE_HOME%\lib
    set LIB=%HIBERNATETOOLS_HOME%\lib
    set CP=%CLASSPATH%;%JDBC_DRIVER%;%HIBERNATE_HOME%\hibernate2.jar;%CORELIB%\commons-logging-1.0.4.jar;
    %CORELIB%\commons-lang-1.0.1.jar;%CORELIB%\cglib-full-2.0.2.jar;%CORELIB%\dom4j-1.4.jar;
    %CORELIB%\odmg-3.0.jar;%CORELIB%\xml-apis.jar;%CORELIB%\xerces-2.4.0.jar;%CORELIB%\xalan-2.4.0.jar;
    %LIB%\jdom.jar;%CORELIB%\commons-collections-2.1.1.jar;%LIB%\..\hibernate-tools.jar  (3)  

    if not "%HIBERNATE_HOME%" == "" goto end

    :noHIBERNATEHome
    echo HIBERNATE_HOME is not set. Please set HIBERNATE_HOME.
    goto end

    :end

        
    (1) 對應到本地JDBC_DRIVER jar包路徑。

    (2) hibernate根目錄。

    (3) 對hibernate中jar包的引用,hibernate因版本不同而jar包的名字也會有可能不同,因此 請核對。


    3、進入hbm2java.bat所在目錄,運行 hbm2java 影射文件路徑 --output=輸出值對象路徑。如: hbm2java C:\sample\gen-src\com\m5\Book.hbm.xml --output=C:\sample\classes。在 C:\sample\classes目錄下生成以包名組織的文件\com\m5\Book.java

    例 4.11. Book

    程序代碼 程序代碼
    package com.m5;

    import java.io.Serializable;
    import org.apache.commons.lang.builder.ToStringBuilder;


    /**

    * @hibernate.class

    * table="book"


    */
    public class Book implements Serializable {

    /** identifier field */
    private Integer id;

    /** persistent field */
    private String name;

    /** persistent field */
    private long price;

    /** full constructor */
    public Book(Integer id, String name, long price) {
    this.id = id;
    this.name = name;
    this.price = price;
    }

    /** default constructor */
    public Book() {
    }

    /**

     * @hibernate.id

     * generator-class="native"

     * type="java.lang.Integer"

     * column="id"

     */
    public Integer getId() {
    return this.id;
    }

    public void setId(Integer id) {
    this.id = id;
    }

    /**
     * @hibernate.property

     * column="name"

     * length="100"

     * not-null="true"

     */
    public String getName() {
    return this.name;
    }

    public void setName(String name) {
    this.name = name;
    }

    /**

     * @hibernate.property

     * column="price"

     * length="10"

     * not-null="true"

     */
    public long getPrice() {
    return this.price;
    }

    public void setPrice(long price) {
    this.price = price;
    }

    public String toString() {
    return new ToStringBuilder(this)
    .append("id", getId())
    .toString();
    }

    }

        
    4.4.3. 根據映射對象生成映射文件
    上節提到了用hbm2java將影射文件生成映射對象,依靠xdoclet標簽完成。xdoclet也是依靠此標簽完成與影射文件的 同步。這樣實際開發中會帶來很大的便利,我們只要維護代碼,而不需要手動維護與影射文件的同步。xdoclet標簽 可以由上節講的方法去轉化得來,當然如果熟悉了xdoclet標簽,手動完成即可。xdoclet的使用很方便,可以加入我 們已有的ant任務中(如果尚未了解Ant,請參看相關章節)。

    1、下載xdoclet。

    2、建立構建文件

    例 4.12. build.xml

    程序代碼 程序代碼
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <project name="XDoclet Examples" default="hibernate" basedir=".">
    <property name="xdoclet.root.dir" value="c:/xdoclet-1.2.2"/>
    <property name="xdoclet.lib.dir" value="${xdoclet.root.dir}/lib"/>
     <property name="samples.gen-src.dir" value="./gen-src"/>

    <path id="classpath">
    <fileset dir="${xdoclet.lib.dir}">
    <include name="*.jar"/>
    </fileset>
    </path>
    <taskdef
    name="hibernatedoclet"
    classname="xdoclet.modules.hibernate.HibernateDocletTask"
    classpathref="classpath"
    />
    <target name="hibernate" description="Generate mapping documents">

    <echo>+---------------------------------------------------+</echo>
    <echo>| |</echo>
    <echo>| R U N N I N G H I B E R N A T E D O C L E T |</echo>
    <echo>| |</echo>
    <echo>+---------------------------------------------------+</echo>

    <hibernatedoclet
    destdir="${samples.gen-src.dir}"
    excludedtags="@version,@author,@todo,@see"
    addedtags="@xdoclet-generated at ${TODAY},@copyright The XDoclet Team,@author XDoclet,@version ${version}"
    force="false"
    verbose="true">

    <fileset dir="${samples.gen-src.dir}">
    <include name="com/m5/Book.java"/>
    </fileset>
      <hibernate version="2.1"/>

    </hibernatedoclet>
    </target>
    </project>


        
    build.xml中的目錄結構均為筆者環境的,使用時請修改成對應的目錄。

    3、運行ant,在輸出目錄生成對應的影射文件。

    建議:如果你覺得hibernate的映射文件放在一個xml文件更為方便,可以通過修改xdoclet的源碼, 使其生成的映射文件全部放置在制定的xml文件中,這樣生成新的映射文件時不需要去維護hibernate 的配置文件中對影射文件的引用,當然也有弊端,多人開發時,關于版本控制沖突,以及可讀性降低。 以上建議僅供參考。

    4.5. 專用詞
    在講述關聯關系之前,有必要解釋下一些專用詞的含義

    4.5.1. cascade(級聯)
    級聯在編程中經常接觸,寫過觸發器來修改或刪除關聯表相記錄的一定會知道,觸發器的作用是當 主控表信息改變時,用來保證其關聯表中數據同步更新。比如一個employee存放職員信息,一個 timecard存放職員的考勤信息,當從職員表中刪除一個職員時,timecard表中對應的考勤信息 已經沒有意義,因為其所屬的職員已不存在,如果繼續留在timecard表中就成了沒用的也稱臟數據。 理想的做法是在刪除職員信息的同時將該職員信息對應的考勤信息也刪除。在hibernate中如果要達到這個 效果只需要設置cascade屬性值即可。當然是否進行級聯關系要根據實際情況慎重考慮。

    4.5.2. inverse(反轉)
    表與表之間的關聯,我們通常將主動發起關聯請求的表稱為主動表,被關聯的表成為被動表,hibernate中 將此概念冠以在表所對應的對象上,因此將主動發起關聯請求的對象稱為主動對象或主控對象,被關聯的對象 稱為被動對象或被控對象。hibernate由主動對象維護關聯關系,在實際中經常碰到一個對象的關聯角色并不 那么明確,如雙向關聯,這時inverse值用來標明由誰來維護關聯關系。設為true時反轉控制角色,即由該 屬性關聯的對象維護關聯關系。

    4.5.3. Lazy Loading(延時裝載)
    延時裝載主要是從性能方面的考慮,對于 “select coulmn1 from table”和“select * from table”語句 的性能比較,相信大家不會有異議,第一條的執行性能要高于第二條,當然這個表中字段存儲的信息應該能充分 體現出優越性為前提,比如說一個employee表中存放有,職員姓名、年齡、照片等,如果只需要查看姓名和年齡, 那么照片信息就不應該附帶出來。表與表之間的關聯也應如此,如果不需要用到關聯表中的數據就不應該去進行關 聯操作,或在需要的時候才啟動關聯操作。讓數據在最恰當的時候才出現,這就是延時裝載。

    4.6. 一對一表關聯操作
    前面章節的例子是單表的操作,實際開發中表之間的關聯操作是必不可少的。 本章以書籍與出版社之間的關聯為例,來講述一對一的關聯操作。

    一對一關系在hibernate中以one-to-one表示,本例中以Book類為主動連接方,因此在Book.java中加入 關聯Publish的屬性。一對一關聯在hibernate中有兩種方式:

    主鍵關聯:不需借助外部字段,直接通過兩個表的主鍵進行關聯,因此必須保證兩個表的主鍵值一 致,這通常通常借助foreign標識符生成器策略來完成。簡單來說,這種情況就是兩個表的主鍵 相等的內連接。

    唯一外鍵關聯:在主動方加入外鍵進行關聯,這樣主動方與被動方的影射關系實際上就成了多對一的關聯。

    為方便查詢,在此描述one-to-one節點的屬性含義(也可參考hibernate的官方指導手冊,有中英文對照很方便)

    程序代碼 程序代碼
    <one-to-one
    name="propertyName"   (1)   
    class="ClassName"   (2)
    cascade="all|none|save-update|delete" (3)
    constrained="true|false"  (4)
    outer-join="true|false|auto"  (5)
    property-ref="propertyNameFromAssociatedClass"  (6)
    access="field|property|ClassName"  (7)
    />

      
    (1) name:映射屬性的名稱。

    (2) class(可選):被關聯的類的名稱,如果省略此屬性,則通過反射機制得到與此屬性名稱一致的類。

    (3) cascade(可選):表明操作是否從父對象級聯到被關聯的對象,all,為所有變更動作都進行級聯操作;none,為 從來不作級聯操作;save-update,為insert,update動作時作級聯操作。delete,為delete動作時作級聯操作。

    (4) constrained(可選):表明該類對應的表對應的數據庫表,和被關聯的對象所對應的數據庫表之間,通過一個外鍵 引用對主鍵進行約束。這個選項影響save()和delete()在級聯執行時的先后順序。

    (5) outer-join(可選):是否允許外連接抓取;默認是auto,關聯對象沒有采用proxy機制時使用外聯接。

    (6) property-ref(可選):指定關聯類的一個屬性,這個屬性將會和本外鍵相對應。默認為關聯類的主鍵。

    (7) access(可選):Hibernate用來訪問屬性的策略,默認是property.


    首先來看通過主鍵進行一對一的關聯操作:

    表 4.2. book

    id name price
    1 《Basic》 12.00
    2 《Pasic》 15.00

    表 4.3. Publish

    id name address
    1 機械出版社 北京朝陽區
    2 教育出版社 北京海底區

    1、建立映射文件

    例 4.13. hibernate_map.xml

    程序代碼 程序代碼
    <hibernate-mapping>
     <!--one to one-->
    <class name="hibernate.relation.oneToOne.Book" table="Book">
      <id name="id" column="id" type="java.lang.Integer">
       <generator class="foreign">
        <param name="property">publish</param>
       </generator>
      </id>
      <property name="name" type="java.lang.String" column="name" length="100" not-null="true" />
      <property name="price" type="long" column="price" length="100" not-null="true" />
      <one-to-one name="publish" class="hibernate.relation.oneToOne.Publish" cascade="none" outer-join="auto" constrained="false" />
      </class>
     
      <class name="hibernate.relation.oneToOne.Publish" table="Publish">
      <id name="id" column="id" type="java.lang.Integer">
       <generator class="native"></generator>
      </id>
      <property name="name" type="java.lang.String" column="name" length="100" not-null="true" />
      <property name="address" type="java.lang.String" column="address" length="100" not-null="true" />
      </class>
    </hibernate-mapping>


       
    2、建立映射類

    例 4.14. Book.java

    程序代碼 程序代碼
    package hibernate.relation.oneToOne;
    import java.io.Serializable;
    import org.apache.commons.lang.builder.ToStringBuilder;
    public class Book implements Serializable {


    private Integer id;
    private String name;
    private long price;
    private Publish publish = null;

    public Book(Integer id, String name, long price) {
    this.id = id;
    this.name = name;
    this.price = price;
    }

    public Book() {
    }

    public Integer getId() {
    return this.id;
    }

    public void setId(Integer id) {
    this.id = id;
    }


    public String getName() {
    return this.name;
    }

    public void setName(String name) {
    this.name = name;
    }

    public long getPrice() {
    return this.price;
    }

    public void setPrice(long price) {
    this.price = price;
    }


    public Publish getPublish()
    {
     return this.publish;
    }

    public void setPublish(Publish publish)
    {
     this.publish = publish;
    }

    public String toString() {
    return new ToStringBuilder(this)
    .append("id", getId())
    .toString();
    }

    }

       
    例 4.15. Publish.java

    程序代碼 程序代碼
    package hibernate.relation.oneToOne;

    import java.io.Serializable;
    import org.apache.commons.lang.builder.ToStringBuilder;
    public class Publish implements Serializable {
    private Integer id;
    private String name;
    private String address;
    public Publish(Integer id, String name, String address) {
    this.id = id;
    this.name = name;
    this.address = address;
    }
    public Publish() {
    }

    public Integer getId() {
    return this.id;
    }

    public void setId(Integer id) {
    this.id = id;
    }
    public String getName() {
    return this.name;
    }

    public void setName(String name) {
    this.name = name;
    }
    public String getAddress() {
    return this.address;
    }
    public void setAddress(String address) {
    this.address = address;
    }
    public String toString() {
    return new ToStringBuilder(this)
    .append("id", getId())
    .toString();
    }
    }
       
    3、演示代碼

    例 4.16. BookTest.java

    程序代碼 程序代碼
    public class BookTest extends TestCase
    {
     private String hql;
     private SessionFactory sessionFactory;
     private Session session;
     private List list;
     
     protected void setUp() throws Exception
     {
      File file = new File("d:\\hibernate.cfg.xml");
      Configuration config = new Configuration().configure(file);
      sessionFactory = config.buildSessionFactory();
      session = sessionFactory.openSession();
     }
     
     public void testSave() throws HibernateException   
     {
      Book book = new Book();
      book.setName("《Basic》");
      book.setPrice(Long.parseLong("12"));
      Publish publish = new Publish();
      publish.setName("機械出版社");
      publish.setAddress("北京朝陽");
      book.setPublish(publish);
      session.save(book);
      session.flush();
     }
      
     public void tesQuery() throws HibernateException   
     {
      String publishName = null;
      hql = "from hibernate.relation.oneToOne.Book as book where book.id = 1";
      List books = session.find(hql);
      for ( int i=0; i < books.size(); i++ )
      {
       Book book = (Book)books.get(i);
       publishName = book.getPublish().getName();
       System.out.println("對應的出版社為:" + publishName);
      }
     }
    }

       
    插入時將執行如下語句:

    Hibernate: insert into Publish (name, address) values (?, ?)

    Hibernate: insert into Book (name, price, id) values (?, ?, ?)

    這是因為我們在Book映射文件中設置了其id值為外鍵生成策略,所以Hibernate會先插入 Publish,然后用publish的主鍵值填充Book的主鍵值,以保證Publish與Book表的主鍵 值相等。

    執行查詢語句將執行如下語句:

    Hibernate: select book0_.id as id, book0_.name as name, book0_.price as price from Book book0_ where (book0_.id=39 )

    Hibernate: select publish0_.id as id0_, publish0_.name as name0_, publish0_.address as address0_ from Publish publish0_ where publish0_.id=?

    以上示例通過Book、publish兩個表的主鍵形成關聯,接下來看如何通過外鍵關聯完成該例子。

    4.7. 多對一表關聯操作
    我們在Book表中添加publishId的外鍵,用來與publish表形成關聯。

    表 4.4. book

    id publishId name price
    1 1 《Basic》 12.00
    2 2 《Pasic》 15.00

    1、建立映射文件

    例 4.17. hibernate_map.xml

    <hibernate-mapping>
     <!--one to one-->
    <class name="hibernate.relation.oneToOne.Book" table="Book">
      <id name="id" column="id" type="java.lang.Integer">
       <generator class="native" />
      </id>
      <property name="name" type="java.lang.String" column="name" length="100" not-null="true" />
      <property name="price" type="long" column="price" length="100" not-null="true" />
      <many-to-one name="publish" class="hibernate.relation.oneToOne.Publish" cascade="all" outer-join="auto"/>
      </class>
     ...
    </hibernate-mapping>

       
    只將book映射文件的one-to-one修改成many-to-one,其他的保持不變,執行BookTest.java文件,將看到 與one-to-one同樣的效果。(注意:cascade="all",這里設置級聯是必須的,因為在插入book時應該先得到 publishid的值。)

    上面的例子都是以Book為主動關聯方進行操作,如果需要在操作Publish時獲取關聯的Book對象,我們需要在 Publish中加入與Book的關聯映射,這樣Book與Publish之間就形成了雙向關聯,這里假設Publish與Book是 一對多的關系,具體操作請看下一章節。

    4.8. 一對多表關聯操作
    1、在publish映射中加入一對多關系

    例 4.18. hibernate_map.xml

    ...
    <class name="hibernate.relation.oneToOne.Publish" table="Publish">
      <id name="id" column="id" type="java.lang.Integer">
       <generator class="native"></generator>
      </id>
      <property name="name" type="java.lang.String" column="name" length="100" not-null="true" />
      <property name="address" type="java.lang.String" column="address" length="100" not-null="true" />
      <set name="book" table="book" lazy="false" cascade="none" sort="unsorted" inverse="false">
       <key column="publishId" />
       <one-to-many class="hibernate.relation.manyToOne.Book" />
      </set>
    </class>

       
    2、在Publish映射類中加入book屬性

    例 4.19. Publish.java

    private Set book = new HashSet();
    public Set getBook()
    {
     return book;
    }
    public void setBook(Set book)
    {
     this.book = book;
    }
       
    這樣就能在操作Publish時也能獲取到與之關聯的Book信息,看測試代碼:

    例 4.20. Publish.java

    Publish publish = (Publish)session.get(Publish.class,Integer.valueOf(1));
    Set books = publish.getBook();
    for (Iterator it = books.iterator(); it.hasNext();)
    {
     Book book = (Book)it.next();
     System.out.println("對應的書籍為:" + book.getName());
    }    
       
    執行上面的代碼顯示的結果為:

    Hibernate: select publish0_.id as id0_, publish0_.name as name0_, publish0_.address as address0_ from Publish publish0_ where publish0_.id=?

    Hibernate: select book0_.id as id__, book0_.publishId as publishId__, book0_.id as id0_, book0_.name as name0_, book0_.price as price0_, book0_.publishId as publishId0_ from Book book0_ where book0_.publishId=?

    對應的書籍為:《Basic》

    4.9. 多對多表關聯操作
    多對多的關聯在實際的開發中也是經常被用到的,假設現有一個員工表來存放所有員工的信息, 一個福利項目表存放福利明細,要記錄每個員工享有的福利明細,同一福利項,多個員工均可享有, 一個員工也可以享有多項福利,這就形成了多對多的關聯,在這里我們加入一個福利明細表充當 關聯其兩者的中間表(多對多的關聯一般都是通過中間表進行關聯的)。看具體實現:

    1、表結構如下:

    表 4.5. Welfare(福利項目表)

    id(主鍵遞增) name money
    1 飯補 250.00
    2 交通補助 200.00
    3 崗位補助 500.00

    表 4.6. Empolyee(人員表)

    id(主鍵遞增) name job
    1 王一 部門經理
    2 李二 程序員

    表 4.7. Empolyee_Welfare(員工福利明細表)

    id(主鍵遞增) EmpolyeeID(員工ID) WelfareID(福利項ID)
    1 1(王一) 1(飯補)
    2 1(王一) 2(交通補助)
    3 1(王一) 3(崗位補助)
    4 2(李二) 1(飯補)
    5 2(李二) 2(交通補助)

    2、編寫影射文件

    例 4.21. Hibernate_map.xml

    程序代碼 程序代碼
    <class name="hibernate.relation.manyToMany.Employee" table="Employee">
      <id name="id" column="id" type="java.lang.Integer">
       <generator class="native"></generator>
      </id>
      <property name="name" type="java.lang.String" column="name" length="100" not-null="true" />
      <property name="job" type="java.lang.String" column="job" length="100" not-null="true" />
      <set name="welfare" table="Empolyee_Welfare" lazy="false">
       <key column="EmployeeId" />
       <many-to-many class="hibernate.relation.manyToMany.Welfare" column="WelfareId" />
      </set>
    </class>
    <class name="hibernate.relation.manyToMany.Welfare" table="Welfare">
      <id name="id" column="id" type="java.lang.Integer">
       <generator class="native"></generator>
      </id>
      <property name="name" type="java.lang.String" column="name" length="100" not-null="true" />
      <property name="money" type="java.lang.Double" column="money" length="18" not-null="true" />
      <set name="employee" table="Empolyee_Welfare" lazy="false" inverse="true">
       <key column="WelfareId" />
       <many-to-many class="hibernate.relation.manyToMany.Employee" column="EmployeeId" />
      </set>
    </class>


       
    3、編寫映射類

    例 4.22. Employee.java

    程序代碼 程序代碼
    public class Employee
    {
     private int id;
     private String name;
     private String job;
     private Set welfare = new HashSet();

     ...

     public Set getWelfare()
     {
      return this.welfare;
     }
      
     public void setWelfare(Set welfare)
     {
      this.welfare = welfare;
     }
    }

       
    例 4.23. Welfare.java

    程序代碼 程序代碼
    public class Welfare
    {
     private int id;
     private String name;
     private Double money;
     private Set employee = new HashSet();
     
     ...

     public Set getEmployee()
     {
      return this.employee;
     }
     
     public void setEmployee(Set employee)
     {
      this.employee = employee;
     }
    }

       
    4、測試代碼

    例 4.24. Employee.java

    程序代碼 程序代碼
    public void testSave() throws Exception
     {
      
      try
      {
       tx = session.beginTransaction();
       Employee employee = new Employee();
       employee.setName("王一");
       employee.setJob("程序員");
       
       Welfare welfare = new Welfare();
       welfare.setMoney(Double.valueOf("250"));
       welfare.setName("飯補");
       
       employee.getWelfare().add(welfare);
       welfare.getEmployee().add(employee);
       
       session.save(employee);
       session.save(welfare);
       tx.commit();
      }
      catch(Exception ex)
      {
       if ( tx !=null )
       {
        tx.rollback();
       }
      }
      finally
      {
       session.close();
      }
     }

       
    執行以上代碼Empolyee、Welfare、Empolyee_Welfare表中將各插入一條數據:

    Hibernate: insert into Employee (name, job) values (?, ?)

    Hibernate: insert into Welfare (name, money) values (?, ?)

    Hibernate: insert into salary (EmployeeId, WelfareId) values (?, ?)

    4.10. 與spring的結合使用
    上面的例子中我的測試代碼都是通過如下代碼來建立hibernate的Session。

    程序代碼 程序代碼
    File file = new File("d:\\hibernate.cfg.xml");
    Configuration config = new Configuration().configure(file);
    sessionFactory = config.buildSessionFactory();
    session = sessionFactory.openSession();

      
    這樣做的目的是為了更直觀的說明對hibernate的使用,本節將演示結合Spring的使用,使代碼更為簡潔。

    首先,修改spring的配置文件,如下:

    程序代碼 程序代碼
    <beans default-lazy-init="false" default-dependency-check="none" default-autowire="no">
    <description>
    </description>
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName">
    <value>org.gjt.mm.mysql.Driver</value>
    </property>
    <property name="url">
    <value>jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=GBK</value>
    </property>
    <property name="username">
    <value>root</value>
    </property>
    <property name="password">
    <value></value>
    </property>
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
    <property name="dataSource">
    <ref local="dataSource"/>
    </property>
    <property name="mappingResources">
    <list>
    <value>Hibernate_Map.hbm.xml</value>
    </list>
    </property>
    <property name="hibernateProperties">
    <props>
    <prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
    <prop key="hibernate.show_sql">true</prop>
    <prop key="hibernate.cglib.use_reflection_optimizer">true</prop>
    <prop key="hibernate.query.substitutions">true 1, false 0</prop>
    </props>
    </property>
    </bean>
    <beans>


      
    上面增加的“dataSource”和“sessionFactory”節點,可以完全替換掉hibernate.cfg.xml的配置信息, 并且在org.springframework.orm.hibernate.LocalSessionFactoryBean類中封裝了對hibernate的 session調用。請看如下調用代碼:

    程序代碼 程序代碼
    SessionFactory sessionFactory = (SessionFactory)InitSpring.getInstance("sessionFactory");
    Session session = SessionFactoryUtils.getSession(sessionFactory, false);
    session.save(...);

      
    上面的代碼通過spring配置文件,實例sessionFactory,然后通過SessionFactoryUtils的getSession方法得到session實例。 關于在程序啟動時怎樣初始化Spring,請參考???

    我們還可以通過Spring來實現Hibernate DAO,得到更簡潔的調用:

    程序代碼 程序代碼
    <bean id="DAOTarget" class="hibernate.dao.test.MyDAO"
    singleton="true"
    lazy-init="default"
    dependency-check="default"
    autowire="default"
    >
    <property name="sessionFactory">
       <ref local="sessionFactory" />
    </property>
    </bean>


      
    例 4.25. MyDAO.java

    程序代碼 程序代碼
    public class MyDAO extends HibernateDaoSupport
    {
      getHibernateTemplate().save(...); 
    }

      
       
    上面的代碼如果看的不是很明白,沒有關系,在DAO章節將詳細講述其實現,這里代碼只是想說明, 借助spring能讓hibernate的應用更加簡潔。

    4.11. Hibernate DAO
    在開始本章之前,先介紹DAO模式的概念,DAO是Data Access Object的縮寫,DAO模式思想是將業務邏輯代碼與 數據庫交互代碼分離,降低兩者耦合。通過DAO模式可以使結構變得更為清晰,代碼更為簡潔,本節示例將結合Spring, 演練Hibernate Dao所帶來的優越性。

    為什么借助Spring實現Hibernate DAO:

    Spring幫我們封裝了針對Hibernate DAO的常用操作。

    將業務對象與DAO對象的依賴轉移到Spring配置文件中。

    借助Spring AOP功能實現DAO對象中數據庫訪問的統一事務管理。

    在開始演練Spring DAO之前,請看一段不借助DAO模式的代碼:

    例 4.26. MyApp.java

    程序代碼 程序代碼
    public void AddUser()
    {
     SessionFactory sessionFactory = (SessionFactory)InitSpring.getInstance("sessionFactory");
     Session session = SessionFactoryUtils.getSession(sessionFactory, false);
     User user = new User();
     user.setName("王一");
     String hql = " from User as user where user.name = '"+ user.name +"' "
     List list = session.find(hql);

     //進行邏輯判斷,如果已經存在相同的用戶則不允許添加
     if ( list.size() > 0 )
     {
      ...
     }
     else
     {
      try
      {
       Transaction tx = session.beginTransaction();
       session.save(user);
       tx.commit();
      }
      catch(Exception ex)
      {
       if ( tx !=null )
       {
        tx.rollback();
        ...
       }
      }
      finally
      {
       ...
       session.close();
      }
     
     }

    }

       
    以上的代碼實現的功能是添加一個用戶,且在添加用戶之前判斷數據庫是否有同名用戶,應該說邏輯不算復雜, 我們將這種業務邏輯與數據庫訪問代碼寫在一塊有如下不足之處:

    降低重用性:該例子中實現了查找用戶和添加用戶的代碼,這種功能很肯能在項目的很多地方都需要用到,但是 我們又不能直接調用AddUser()中實現的類似代碼,因為該方法與具體的業務邏輯邦定在一塊。

    有可能降低代碼可讀性:當業務邏輯變得復雜時,在該例中既要實現業務邏輯,又要實現數據庫訪問代碼。

    造成重復編碼:從上面例子的代碼中我們可以看到附加了業務邏輯以外的代碼,如事務管理、異常捕獲。 最理想的業務類完成的功能應該是只實現其自身的業務邏輯。

    我們期望的代碼是:

    例 4.27. MyApp.java

    public void AddUser()
    {
     User user = new User();
     user.setName("王一");
     if ( findUser(user).size() >0 )
     {
      ...
     }
     else
     {
      saveUser(user); 
     }
    }
       
    下面借助Spring來實現Hibernate Dao,來看看是否能達到我們期望的效果:

    1、將訪問數據庫的代碼從業務類MyApp.java中分離出來,放在DAO對象UserDao.java中

    例 4.28. UserDao.java

    public class UserDao extends HibernateDaoSupport
    {
     public List findUser(User user)
     {
      String hql = " from User as user where user.name = '"+ user.name +"' ";
      return getHibernateTemplate().find(hql);
     }

     public void saveUser(User user)
     {
      getHibernateTemplate().save(user);
     }
    }
       
    2、將業務類MyApp.java與DAO對象關聯(在MyApp.java類中增加DAO的屬性,以便通過Spring 注入DAO實例)。

    例 4.29. Spring_Config.xml

    <bean id="userDao" class="hibernate.dao.test.UserDao"
    singleton="true"
    lazy-init="default"
    dependency-check="default"
    autowire="default"
    >
    <property name="sessionFactory">
       <ref local="sessionFactory" />
    </property>
    </bean>
    <bean id="myApp" class="hibernate.dao.test.MyApp"
    singleton="true"
    lazy-init="default"
    dependency-check="default"
    autowire="default">
    <property name="dao">
       <ref local="userDao" />
    </property>
    </bean>

       
    3、業務類MyApp.java的實現

    例 4.30. MyApp.java

    public class MyApp
    {
     private UserDao dao;
     public void setDao(UserDao dao)
     {
      this.dao = dao;
     }
     
     public void AddUser()
     {
      User user = new User();
      user.setName("王一");
      if ( dao.findUser(user).size() >0 )
      {
       ...
      }
      else
      {
       dao.saveUser(user); 
      }
     }
    }
       
    從MyApp.java可以看到與我們期望的效果幾乎一樣,在實際開發中我們應該讓業務類與DAO類分別針對其接口 做實現,這樣在代碼中可以只針對于接口做引用,從而降低調用類與具體實現類的耦合,這里為更簡潔的說明問 題省略對其各自接口的定義。

    第 5 章 log4j
    5.1. 概述
    log4j是用于java語言的日志記錄工具,一個完整的商業軟件,日志是必不可少的。現實開發 中日志記錄多種多樣,有打印在控制臺中,有記錄成文本文件,有保存到數據庫中等。日志信息也許需要 分為調試日志,運行日志,異常日志等。這些雖然實現簡單,但是也繁瑣。本章將介紹用log4j來實現日志 記錄的種種情況。

    5.2. 快速入門
    1、下載log4j,http://logging.apache.org/log4j,將log4j.jar拷貝到項目的lib中, 并引用。

    2、建立log4j的配置文件,本文中命名為log4j.xml,內容如下:

    例 5.1. log4j.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> (1)   
    <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
     <appender name="file" class="org.apache.log4j.FileAppender"> (2)
      <param name="File" value="D:/mytest.log" />    (3)
      <layout class="org.apache.log4j.PatternLayout">    (4)
       <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c:(%F:%L) %n - %m%n"/>
      </layout> 
     </appender>
     <root>
      <priority value ="INFO"/>  (5)
      <appender-ref ref="file" />  (6)
     </root>
    </log4j:configuration>
       
    (1) 申明驗證該文檔的dtd文件,”SYSTEM“說明是從本地尋找,因此需將log4j.dtd文件放入申明的路徑中

    (2) 該節點配置成日志以文件形式輸出(org.apache.log4j.FileAppender)。log4j還提供打印日志到控制臺 (org.apache.log4j.ConsoleAppender),以信息流格式傳送到任何地方(org.apache.log4j.WriterAppender)。

    (3) 指定日志文件的路徑和名稱。

    (4) 指定記錄日志的布局格式。log4j提供以html格式的布局(org.apache.log4j.HTMLLayout),自定義布局格式 (org.apache.log4j.PatternLayout),包含一些簡單的日志信息,級別和信息字符串(org.apache.log4j.SimpleLayout) 包含詳細的日志信息,如時間、線程、類別等信息。

    (5) 設置日志輸出的級別。log4j的日志常用級別如下,按優先級別從高到低分為:

    Fatal:顯示致命錯誤。

    Error:顯示錯誤信息。

    Warn:顯示警告信息。

    Info:顯示程序運行日志。

    debug:顯示調試信息。

    只有日志級別大于或等于被設置級別,相應的日志才被記錄。如本配置文件配置級別為Info,程序中除了debug級別的日志, 其它級別的日志都會被輸出。

    (6) 引用輸出日志的方式。


    3、演示使用log4j記錄日志

    例 5.2. MyLog.java

    程序代碼 程序代碼
    package com.m5;
    import org.apache.log4j.Logger;
    import org.apache.log4j.xml.DOMConfigurator;
    public class MyLog {
     public static void main(String[] args)
     {
      String log4j = "d:\\log4j.xml" ;
      DOMConfigurator.configure(log4j);
      Logger logger = Logger.getLogger(MyLogServlet.class.getName());
      try
      {
       int i = 10;
       int n = 0 ;
       int m = 10/0;
      }
      catch(Exception ex)
      {
       logger.info(ex.toString());
      }
     }
    }
     
      
    上面的程序會將捕捉到的異常信息寫入D:/mytest.log文件。日志的書寫格式輸出目的地均可以通過log4j進行配置。 關于具體配置請參考log4j.xml及其說明,在此不再一一演示其效果。從上面的演示代碼中可以看出對log4j的引用 也非常簡潔,在實際運用中可以優化下導入log4j的配置文件部分。MyLog.java中的代碼是為了最簡捷清晰的說明對 log4j的使用。

    4、實際環境中的應用(以web服務為tomcat的web項目為例)

    在每個類中記錄日志之前都敲一次裝載log4j的配置文件的代碼,顯然是不合理的。通常我們在程序啟動之前完成這些初始化工作。 spring一文中描述了對spring配置文件的初始化方法,同樣,初始化log4j配置文件的裝載也可以通過這個自定義的啟動類完成。

    例 5.3. web.xml

    程序代碼 程序代碼
    <?xml version="1.0" encoding="UTF-8"?>
     <web-app xmlns="http://java.sun.com/xml/ns/j2ee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
     <display-name>Test</display-name>
     <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/spring_bean.xml</param-value>
     </context-param>
     <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
     </listener>
     <!--
     <servlet>
      <servlet-name>context</servlet-name>
      <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
      <load-on-startup>2</load-on-startup>
     </servlet>
     -->
     <servlet>
      <servlet-name>Init</servlet-name>
      <servlet-class>com.m5.Base</servlet-class>
      <init-param>

      <param-name>log4jConfigLocation</param-name>   (1)

      <param-value>/WEB-INF/log4j.xml</param-value>
     </init-param>
     <load-on-startup>3</load-on-startup>
    </servlet>
    </web-app>

     
     
    (1) 配置log4j的配置文件路徑。


    例 5.4. Base.java

    程序代碼 程序代碼
    public class Base extends HttpServlet
    {
     WebApplicationContext wac = null;
     public void init(ServletConfig config) throws ServletException
     {
      //ApplicationContext ac = new FileSystemXmlApplicationContext("bean.xml");
      wac = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext());
      super.init(config); 
      InitSpring.Init((AbstractApplicationContext)wac); 
      String root = getServletConfig().getServletContext().getRealPath("/");
      String log4j = getInitParameter("log4jConfigLocation"); 
      DOMConfigurator.configure(root + log4j);    
     }
    }

      
    (1) 得到配置文件路徑。

    (2) 裝載配置文件,DOMConfigurator符合sigle模式,配置文件只需裝載一次,全局即可調用。


    通過以上配置,項目中可以通過如下引用:

    例 5.5. MyLog.java

    程序代碼 程序代碼
    package com.m5;
    import org.apache.log4j.Logger;
    import org.apache.log4j.xml.DOMConfigurator;
    public class MyLog {
     public static void main(String[] args)
     {
      //String log4j = "d:\\log4j.xml" ;
      //DOMConfigurator.configure(log4j);
      Logger logger = Logger.getLogger(MyLogServlet.class.getName());
      try
      {
       int i = 10;
       int n = 2 ;
       int m = i/n;
      }
      catch(Exception ex)
      {
       logger.info(ex.toString());
      }
     }
    }
     
     
    規范合理的日志記錄能讓開發人員和維護人員事半功倍,在記錄日志時還應該考慮不同的角色對日志內容可能會有 不同的需求。比如,軟件正常情況下提供給用戶的日志應該簡潔明了,調試時提供給程序員的日志應該詳細明確。 請看如下代碼:

    程序代碼 程序代碼
    package com.m5;
    import org.apache.log4j.Logger;
    import org.apache.log4j.xml.DOMConfigurator;
    public class MyLog {

     public static void main(String[] args)
     {
      String log4j = "d:\\log4j.xml" ;
      DOMConfigurator.configure(log4j);
      Logger logger = Logger.getLogger(MyLogServlet.class.getName());
      try
      {
       int i = 10;
       int n = 2 ;
       int m = i/n;
       logger.debug("以下信息為除法器運算跟蹤:");
       logger.warn("請注意被除數不能為0");
       logger.info("除數為" + Integer.toString(i));
       logger.info("被除數為" + Integer.toString(n));
       logger.info("運算結果為:" + Integer.toString(m) );
      }
      catch(Exception ex)
      {
       logger.error(ex.toString());
      }
     }
    }

      
    調試的時候我們可以在log4j.xml配置文中指定級別為debug,輸出所有日志。正式運行時將級別設定為error,只輸出 錯誤日志。這樣就不用每次在軟件正式使用前注釋或者刪除調試的信息了。可以想象一下,如果要注釋成百上千個段調試 代碼,也是項繁瑣的工作,再說在正式運行的時候如果出錯,想看詳細信息又得修改原代碼,然后再編譯。特別是異地非 遠程控制的情況下如果要得到詳細的調試日志那是件苦不堪言的事情,因為用戶不會幫你去改代碼。

    log4j的配置文件還可以是屬性文件,在此不再另述。

    posted on 2007-06-28 13:45 rogerfan 閱讀(970) 評論(0)  編輯  收藏 所屬分類: 【Java知識】【開源技術】
    主站蜘蛛池模板: 久久久久亚洲av无码专区导航| 免费无码成人AV片在线在线播放| 国产婷婷高清在线观看免费| 亚洲沟沟美女亚洲沟沟| 久久www免费人成看国产片| 亚洲国产婷婷综合在线精品 | 97se亚洲国产综合自在线 | 久久狠狠躁免费观看2020| 国产亚洲情侣一区二区无| 无遮挡呻吟娇喘视频免费播放| 成年男女免费视频网站| 亚洲一本之道高清乱码| 99久久这里只精品国产免费| 亚洲人配人种jizz| 三年片在线观看免费观看高清电影 | 日本特黄特色aa大片免费| 亚洲国产成人91精品| 在线天堂免费观看.WWW| 亚洲三级在线视频| 免费观看毛片视频| 国产精品久久久久久亚洲小说| 波多野结衣免费视频观看| 成人免费夜片在线观看| 亚洲成av人影院| 91久久精品国产免费直播| 亚洲日韩看片无码电影| 一区二区三区亚洲视频| 大地资源网高清在线观看免费| 91亚洲国产成人精品下载| 永久免费毛片手机版在线看| 一级视频在线免费观看| 亚洲精品视频观看| 免费v片在线观看无遮挡| 午夜精品一区二区三区免费视频 | 亚洲日韩看片无码电影| 亚洲男人的天堂在线va拉文| 日韩电影免费在线观看中文字幕| 亚洲中文字幕日本无线码| 亚洲女同成av人片在线观看| 女人18毛片特级一级免费视频 | 久久精品中文字幕免费|