Hibernate入門 - 基礎配置

          作者:robbin (MSN:robbin_fan AT hotmail DOT com)

          版權聲明:本文嚴禁轉載,如有轉載請求,請和作者聯系

          Hibernate配置文件可以有兩種格式,一種是 hibernate.properties ,另一種是 hibernate.cfg.xml

          后者稍微方便一些,當增加hbm映射文件的時候,可以直接在 hibernate.cfg.xml 里面增加,不必像 hibernate.properties 必須在初始化代碼中加入。

          但不管怎么說,兩種的配置項都是一樣的,下面詳細介紹:

          在Hibernate的src目錄下有一個 hibernate.properties 模板,我們不必自己從頭寫,修改模板就可以了:)


          hibernate.query.substitutions true 1, false 0, yes 'Y', no 'N'

          這個配置意思是當你在Hibernate里面輸入true的時候,Hibernate會轉化為1插入數據庫,當你在Hibernate里面輸入false的時候,Hibernate會轉化為0插入數據庫,后面的Y,N同理。

          對于某些數據庫,例如Oracle來說,沒有boolean數據類型,就是采用1代表true,0代表false,因此使用這個配置在Hibernate里面直接用true/false會非常直觀。


          hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
          hibernate.connection.driver_class com.mysql.jdbc.Driver
          hibernate.connection.url jdbc:mysql:///test
          hibernate.connection.username root
          hibernate.connection.password

          這是一個連接MySQL數據庫的例子,很直觀,不必解釋,不同的數據庫的連接參數模板中全部給出了。
          對于某些數據庫,例如Oracle來說,沒有boolean數據類型,就是采用1代表true,0代表false,因此使用這個配置在Hibernate里面直接用true/false會非常直觀。


          hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
          hibernate.connection.driver_class com.mysql.jdbc.Driver
          hibernate.connection.url jdbc:mysql:///test
          hibernate.connection.username root
          hibernate.connection.password

          這是一個連接MySQL數據庫的例子,很直觀,不必解釋,不同的數據庫的連接參數模板中全部給出了。
          如果你不是在App Server環境中使用Hibernate,例如遠程客戶端程序,但是你又想用App Server的數據庫連接池,那么你還需要配置JNDI的參數,例如Hibernate連接遠程Weblogic上的數據庫連接池:

          hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
          hibernate.connection.datasource mypool
          hibernate.connection.provider_class net.sf.hibernate.connection.DatasourceConnectionProvider
          hibernate.jndi.class weblogic.jndi.WLInitialContextFactory
          hibernate.jndi.url t3://servername:7001/


          最后,如果你需要在EJB或者JTA中使用Hibernate,需要取消下行的注釋:

          hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory

          雜項配置:


          hibernate.show_sql false

          是否將Hibernate發送給數據庫的sql顯示出來,這是一個非常非常有用處的功能。當你在調試Hibernate的時候,讓Hibernate打印sql語句,可以幫助你迅速解決問題。


          #hibernate.connection.isolation 4

          指定數據庫的隔離級別,往往不同的數據庫有自己定義的隔離級別,未必是Hibernate的設置所能更改的,所以也不必去管它了。

          hibernate.jdbc.fetch_size 50
          hibernate.jdbc.batch_size 25

          這兩個選項非常非常非常重要!!!將嚴重影響Hibernate的CRUD性能!

          C = create, R = read, U = update, D = delete

          Fetch Size 是設定JDBC的Statement讀取數據的時候每次從數據庫中取出的記錄條數。

          例如一次查詢1萬條記錄,對于Oracle的JDBC驅動來說,是不會1次性把1萬條取出來的,而只會取出Fetch Size條數,當紀錄集遍歷完了這些記錄以后,再去數據庫取Fetch Size條數據。

          因此大大節省了無謂的內存消耗。當然Fetch Size設的越大,讀數據庫的次數越少,速度越快;Fetch Size越小,讀數據庫的次數越多,速度越慢。

          這有點像平時我們寫程序寫硬盤文件一樣,設立一個Buffer,每次寫入Buffer,等Buffer滿了以后,一次寫入硬盤,道理相同。

          Oracle 數據庫的JDBC驅動默認的Fetch Size=10,是一個非常保守的設定,根據我的測試,當Fetch Size=50的時候,性能會提升1倍之多,當Fetch Size=100,性能還能繼續提升20%,Fetch Size繼續增大,性能提升的就不顯著了。

          因此我建議使用Oracle的一定要將Fetch Size設到50。

          不過并不是所有的數據庫都支持Fetch Size特性,例如MySQL就不支持。

          MySQL就像我上面說的那種最壞的情況,他總是一下就把1萬條記錄完全取出來,內存消耗會非常非常驚人!這個情況就沒有什么好辦法了 :(

          Batch Size是設定對數據庫進行批量刪除,批量更新和批量插入的時候的批次大小,有點相當于設置Buffer緩沖區大小的意思。

          Batch Size越大,批量操作的向數據庫發送sql的次數越少,速度就越快。我做的一個測試結果是當Batch Size=0的時候,使用Hibernate對Oracle數據庫刪除1萬條記錄需要25秒,Batch Size = 50的時候,刪除僅僅需要5秒!!!

          可見有多么大的性能提升!很多人做Hibernate和JDBC的插入性能測試會奇怪的發現Hibernate速度至少是JDBC的兩倍,就是因為Hibernate使用了Batch Insert,而他們寫的JDBC沒有使用Batch的緣故。

          以我的經驗來看,Oracle數據庫 Batch Size = 30 的時候比較合適,50也不錯,性能會繼續提升,50以上,性能提升的非常微弱,反而消耗內存更加多,就沒有必要了。


          #hibernate.jdbc.use_scrollable_resultset true

          設定是否可以使用JDBC2.0規范的可滾動結果集,這對Hibernate的分頁顯示有一定的作用,默認就好了。


          #hibernate.cglib.use_reflection_optimizer false

          默認打開,啟用cglib反射優化。cglib是用來在Hibernate中動態生成PO字節碼的,打開優化可以加快字節碼構造的速度。

          不過,當你在調試程序過程中,特別是和proxy,lazy loading相關的應用中,代碼出錯,但是出錯提示信息有語焉不詳,那么你可以把cglib優化關掉,這樣Hibernate會輸出比較詳細的調試信息,幫助你debug。

          Hibernate一共包括了23個jar包,令人眼花繚亂。本文將詳細講解Hibernate每個jar包的作用,便于你在應用中根據自己的需要進行取舍。

          下載Hibernate,例如2.0.3穩定版本,解壓縮,可以看到一個hibernate2.jar和lib目錄下有22個jar包:

          hibernate2.jar:
          Hibernate的庫,沒有什么可說的,必須使用的jar包

          cglib-asm.jar:
          CGLIB庫,Hibernate用它來實現PO字節碼的動態生成,非常核心的庫,必須使用的jar包

          dom4j.jar:
          dom4j 是一個Java的XML API,類似于jdom,用來讀寫XML文件的。dom4j是一個非常非常優秀的Java XML API,具有性能優異、功能強大和極端易用使用的特點,同時它也是一個開放源代碼的軟件,可以在SourceForge上找到它。在IBM developerWorks上面可以找到一篇文章,對主流的Java XML API進行的性能、功能和易用性的評測,dom4j無論在那個方面都是非常出色的。我早在將近兩年之前就開始使用dom4j,直到現在。如今你可以看到越來越多的Java軟件都在使用dom4j來讀寫XML,特別值得一提的是連Sun的JAXM也在用dom4j。這是必須使用的jar 包,Hibernate用它來讀寫配置文件。

          odmg.jar:
          ODMG是一個ORM的規范,Hibernate實現了ODMG規范,這是一個核心的庫,必須使用的jar包。

          commons-collections.jar:
          Apache Commons包中的一個,包含了一些Apache開發的集合類,功能比java.util.*強大。必須使用的jar包。

          commons-beanutils.jar:
          Apache Commons包中的一個,包含了一些Bean工具類類。必須使用的jar包。

          commons-lang.jar:
          Apache Commons包中的一個,包含了一些數據類型工具類,是java.lang.*的擴展。必須使用的jar包。

          commons-logging.jar:
          Apache Commons包中的一個,包含了日志功能,必須使用的jar包。這個包本身包含了一個Simple Logger,但是功能很弱。在運行的時候它會先在CLASSPATH找log4j,如果有,就使用log4j,如果沒有,就找JDK1.4帶的 java.util.logging,如果也找不到就用Simple Logger。commons-logging.jar的出現是一個歷史的的遺留的遺憾,當初Apache極力游說Sun把log4j加入JDK1.4,然而JDK1.4項目小組已經接近發布JDK1.4產品的時間了,因此拒絕了Apache的要求,使用自己的java.util.logging,這個包的功能比log4j差的很遠,性能也一般。后來Apache就開發出來了commons-logging.jar用來兼容兩個logger。因此用 commons-logging.jar寫的log程序,底層的Logger是可以切換的,你可以選擇log4j,java.util.logging或者它自帶的Simple Logger。不過我仍然強烈建議使用log4j,因為log4j性能很高,log輸出信息時間幾乎等于System.out,而處理一條log平均只需要5us。你可以在Hibernate的src目錄下找到Hibernate已經為你準備好了的log4j的配置文件,你只需要到Apache 網站去下載log4j就可以了。commons-logging.jar也是必須的jar包。

          使用Hibernate必須的jar包就是以上的這幾個,剩下的都是可選的。

          ant.jar:
          Ant編譯工具的jar包,用來編譯Hibernate源代碼的。如果你不準備修改和編譯Hibernate源代碼,那么就沒有什么用,可選的jar包

          optional.jar:
          Ant的一個輔助包。

          c3p0.jar:
          C3PO是一個數據庫連接池,Hibernate可以配置為使用C3PO連接池。如果你準備用這個連接池,就需要這個jar包。

          proxool.jar:
          也是一個連接池,同上。

          commons-pool.jar, commons-dbcp.jar:
          DBCP數據庫連接池,Apache的Jakarta組織開發的,Tomcat4的連接池也是DBCP。

          實際上Hibernate自己也實現了一個非常非常簡單的數據庫連接池,加上上面3個,你實際上可以在Hibernate上選擇4種不同的數據庫連接池,選擇哪一個看個人的偏好,不過DBCP可能更通用一些。另外強調一點,如果在EJB中使用Hibernate,一定要用App Server的連接池,不要用以上4種連接池,否則容器管理事務不起作用。

          connector.jar:
          JCA 規范,如果你在App Server上把Hibernate配置為Connector的話,就需要這個jar。不過實際上一般App Server肯定會帶上這個包,所以實際上是多余的包。

          jaas.jar:
          JAAS是用來進行權限驗證的,已經包含在JDK1.4里面了。所以實際上是多余的包。

          jcs.jar:
          如果你準備在Hibernate中使用JCS的話,那么必須包括它,否則就不用。

          jdbc2_0-stdext.jar:
          JDBC2.0的擴展包,一般來說數據庫連接池會用上它。不過App Server都會帶上,所以也是多余的。

          jta.jar:
          JTA規范,當Hibernate使用JTA的時候需要,不過App Server都會帶上,所以也是多余的。

          junit.jar:
          Junit包,當你運行Hibernate自帶的測試代碼的時候需要,否則就不用。

          xalan.jar, xerces.jar, xml-apis.jar:
          Xerces 是XML解析器,Xalan是格式化器,xml-apis實際上是JAXP。一般App Server都會帶上,JDK1.4也包含了解析器,不過不是Xerces,是Crimson,效率比較差,不過Hibernate用XML只不過是讀取配置文件,性能沒什么緊要的,所以也是多余的。

          Hibernate 是對JDBC的輕量級對象封裝,Hibernate本身是不具備Transaction處理功能的,Hibernate的Transaction實際上是底層的JDBC Transaction的封裝,或者是JTA Transaction的封裝,下面我們詳細的分析:

          Hibernate可以配置為JDBCTransaction或者是JTATransaction,這取決于你在hibernate.properties中的配置:

          #hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
          #hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory

          如果你什么都不配置,默認情況下使用JDBCTransaction,如果你配置為:

          hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory

          將使用JTATransaction

          不管你準備讓Hibernate使用JDBCTransaction,還是JTATransaction,我的忠告就是什么都不配,將讓它保持默認狀態,如下:

          #hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
          #hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory

          在下面的分析中我會給出原因。

          一、JDBC Transaction

          看看使用JDBC Transaction的時候我們的代碼例子:

          Session session = sf.openSession();
          Transaction tx = session.beginTransactioin();
          ...
          session.flush();
          tx.commit();
          session.close();

          這是默認的情況,當你在代碼中使用Hibernate的Transaction的時候實際上就是JDBCTransaction。那么JDBCTransaction究竟是什么東西呢?來看看源代碼就清楚了:

          Hibernate2.0.3源代碼中的類

          net.sf.hibernate.transaction.JDBCTransaction:

          public void begin() throws HibernateException {
             
          ...
                  if (toggleAutoCommit) session.connection().setAutoCommit(false);
          ...
          }

          這是啟動Transaction的方法,看到 connection().setAutoCommit(false) 了嗎?是不是很熟悉?

          再來看

          public void commit() throws HibernateException {  
          ...
              try {
                  if ( session.getFlushMode()!=FlushMode.NEVER ) session.flush();
                  try {
                      session.connection().commit();
                      committed = true;
                  }
          ...
              toggleAutoCommit();
          }


          這是提交方法,看到connection().commit() 了嗎?下面就不用我多說了,這個類代碼非常簡單易懂,通過閱讀使我們明白Hibernate的Transaction都在干了些什么?我現在把用 Hibernate寫的例子翻譯成JDBC,大家就一目了然了:

          Connection conn = ...;         <---   session = sf.openSession();

          conn.setAutoCommit(false);     <---   tx = session.beginTransactioin();

          ...                            <---   ...

          conn.commit();                 <---   tx.commit(); (對應左邊的兩句)
          conn.setAutoCommit(true);

          conn.close();                  <---   session.close();

          看明白了吧,Hibernate的JDBCTransaction根本就是conn.commit而已,根本毫無神秘可言,只不過在Hibernate 中,Session打開的時候,就會自動conn.setAutoCommit(false),不像一般的JDBC,默認都是true,所以你最后不寫 commit也沒有關系,由于Hibernate已經把AutoCommit給關掉了,所以用Hibernate的時候,你在程序中不寫 Transaction的話,數據庫根本就沒有反應。


          JTATransaction

          如果你在EJB中使用Hibernate,或者準備用JTA來管理跨Session的長事務,那么就需要使用JTATransaction,先看一個例子:

          javax.transaction.UserTransaction tx = new InitialContext().lookup("javax.transaction.UserTransaction");

          Session s1 = sf.openSession();
          ...
          s1.flush();
          s1.close();

          ...

          Session s2 = sf.openSession();
          ...
          s2.flush();
          s2.close();

          tx.commit();

          這是標準的使用JTA的代碼片斷,Transaction是跨Session的,它的生命周期比Session要長。如果你在EJB中使用 Hibernate,那么是最簡單不過的了,你什么Transaction代碼統統都不要寫了,直接在EJB的部署描述符上配置某某方法是否使用事務就可以了。

          現在我們來分析一下JTATransaction的源代碼, net.sf.hibernate.transaction.JTATransaction:

          public void begin(InitialContext context, ...
            ...
            ut = (UserTransaction) context.lookup(utName);
            ...

          看清楚了嗎? 和我上面寫的代碼 tx = new (Initial Context)().lookup("javax.transaction.UserTransaction"); 是不是完全一樣?

          public void commit() ...
            ...
            if (newTransaction) ut.commit();
            ...


          JTATransaction的控制稍微復雜,不過仍然可以很清楚的看出來Hibernate是如何封裝JTA的Transaction代碼的。

          但是你現在是否看到了什么問題? 仔細想一下,Hibernate Transaction是從Session中獲得的,tx = session.beginTransaction(),最后要先提交tx,然后再session.close,這完全符合JDBC的 Transaction的操作順序,但是這個順序是和JTA的Transactioin操作順序徹底矛盾的!!! JTA是先啟動Transaction,然后啟動Session,關閉Session,最后提交Transaction,因此當你使用JTA的 Transaction的時候,那么就千萬不要使用Hibernate的Transaction,而是應該像我上面的JTA的代碼片斷那樣使用才行。
          總結:
          1、在JDBC上使用Hibernate

          必須寫上Hibernate Transaction代碼,否則數據庫沒有反應。此時Hibernate的Transaction就是Connection.commit而已

          2、在JTA上使用Hibernate

          寫JTA的Transaction代碼,不要寫Hibernate的Transaction代碼,否則程序會報錯

          3、在EJB上使用Hibernate

          什么Transactioin代碼都不要寫,在EJB的部署描述符里面配置

          |---CMT(Container Managed Transaction)
          |
          |---BMT(Bean Managed Transaction)
                  |
                  |----JDBC Transaction
                  |
                  |----JTA Transaction
           
          posted on 2008-12-03 18:11 caihaibo 閱讀(262) 評論(0)  編輯  收藏 所屬分類: hibernate
          主站蜘蛛池模板: 高安市| 武义县| 高淳县| 长乐市| 盖州市| 崇左市| 天祝| 田东县| 同仁县| 开化县| 广东省| 桦南县| 张家港市| 宜兰县| 微博| 祁连县| 德格县| 库伦旗| 扬中市| 高清| 武功县| 略阳县| 仁寿县| 西充县| 福贡县| 天镇县| 尼木县| 临泉县| 甘洛县| 禄劝| 景宁| 香港 | 盖州市| 兴宁市| 宜春市| 临沧市| 凌源市| 江都市| 乐业县| 谢通门县| 健康|