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

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

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

    持久層的組成(轉(zhuǎn))

    持久層的組成
        這一節(jié)的名字應(yīng)該換成“基于Hibernate的持久層的組成”更合適一點(diǎn),可是它太長了。既然Hibernate是用來開發(fā)持久層,那么我先介紹一下這個(gè)持久層中的各個(gè)元素。
    1.    POJO:Plain Old Java Object,你可以把它看作是簡單的JavaBean。一般說來,一張數(shù)據(jù)庫表對應(yīng)一個(gè)POJO,也就是對象/關(guān)系的一一映射。
    2.    DAO:對于每一個(gè)POJO,一般都有一個(gè)DAO與之對應(yīng),承擔(dān)所有關(guān)于該P(yáng)OJO的訪問控制。實(shí)際上也就是控制了對數(shù)據(jù)庫中一張表的訪問控制。
    3.    *.hbm.xml文件:這個(gè)文件定義了POJO和數(shù)據(jù)庫中的表是如何映射的,比如POJO中的字段對應(yīng)數(shù)據(jù)庫表中的哪個(gè)字段等等。一般每個(gè)映射都用單獨(dú)的文件來描述,也就是有一個(gè)POJO就有一個(gè)*.hbm.xml文件。
    4.    *.cfg.xml文件:這個(gè)文件定義了Hibernate的基本信息,比如數(shù)據(jù)庫驅(qū)動(dòng),用戶名,密碼等等連接信息,也包括了所有要用的*.hbm.xml文件,在初始化的時(shí)候,Hibernate會(huì)讀取這個(gè)文件來找相應(yīng)的映射文件完成對象/關(guān)系。
    我們還是以上文的例子來詳細(xì)描述一下這里提到的各個(gè)元素的內(nèi)容。
    1.    Student.java:
    代碼片段4:
    1. public class Student  implements java.io.Serializable 
    2. {
    3.     private String id;
    4.     private String name;
    5.     private Set courseSelections = new HashSet(0);
    6.     public Student() 
    7.     {
    8.     }
    9.     public String getId() 
    10.     {
    11.         return this.id;
    12.     }
    13.     
    14.     public void setId(String id) 
    15.     {
    16.         this.id = id;
    17.     }
    18.     public String getName() 
    19.     {
    20.         return this.name;
    21.     }
    22.     
    23.     public void setName(String name) 
    24.     {
    25.         this.name = name;
    26.     }
    27.     public Set getCourseSelections() 
    28.     {
    29.         return this.courseSelections;
    30.     }
    31.     
    32.     public void setCourseSelections(Set courseSelections) 
    33.     {
    34.         this.courseSelections = courseSelections;
    35.     }
    36. }

    這個(gè)類就是一個(gè)POJO,你可以很明顯的看出來它就是一個(gè)JavaBean。我想解釋它的courseSelection字段。很顯然,在數(shù)據(jù)庫表student中,沒有這個(gè)字段。這里的這個(gè)字段是因?yàn)橐粋€(gè)外鍵引用,course_selection的student_id是一個(gè)外鍵,引用了student表中的id字段。那么在Student類中courseSelection來記錄這樣的外鍵關(guān)系,也就是說,當(dāng)我們獲取了Student對象以后,就可以直接獲取他的選課記錄,這樣就為上層的調(diào)用提供了很大的方便。這里有點(diǎn)模糊沒關(guān)系,我在介紹映射定義文件(*.hbm.xml)的時(shí)候還會(huì)提到這個(gè)問題。
    2.    StudentDAO.java
    代碼片段5:
    1. public class StudentDAO 
    2. {
    3.     Session session;
    4.     public StudentDAO()
    5.     {
    6.         Configuration cfg = new Configuration();
    7.         cfg.configure("/hibernate.cfg.xml");
    8.         SessionFactory sessionFactory = cfg.buildSessionFactory();
    9.         session = sessionFactory.openSession();
    10.     }
    11.     
    12.     public void save(Student transientInstance) 
    13.     {
    14.         session.save(transientInstance);
    15.     }
    16.     
    17.     public void delete(Student persistentInstance) 
    18.     {
    19.         session.delete(persistentInstance);
    20.     }
    21.     
    22.     public Student findById(java.lang.String id) 
    23.     {
    24.         List list = session.createCriteria(Student.class).add(
    25. Expression.eq("id", id)).list();
    26.         if (list.size() > 0) 
    27.         {
    28.             return (Student)list.get(0);
    29.         }
    30.         return null;
    31.     }
    32. }

    這里的構(gòu)造函數(shù)是用來啟動(dòng)Hibernate,并獲取session。打開一個(gè)session就相當(dāng)于打開了一個(gè)數(shù)據(jù)庫連接,然后我們就可以對這個(gè)session進(jìn)行操作,完成數(shù)據(jù)庫操作,完全不用寫SQL語句。我這里Hibernate的啟動(dòng)方式寫的很不規(guī)范,系統(tǒng)應(yīng)該只需要完成一次Hibernate啟動(dòng)就可以在不同的DAO中使用,我把它寫在構(gòu)造函數(shù)里面純粹是為了簡化演示代碼。
    你可以看到save和delete方法都很簡單直接對對象操作,而findById就有些麻煩,因?yàn)檫@里有一個(gè)查詢過程在里面。Hibernate里面查詢可以用Criteria這個(gè)類來完成,我們也常用Hibernate獨(dú)有的HQL(Hibernate Query Language)來完成查詢。當(dāng)然Hibernate也是支持原生SQL的。關(guān)于查詢的詳細(xì)信息請參考其他文章或書籍,我只是演示一個(gè)流程,介紹一些概念。
    3.    Student.hbm.xml
    代碼片段6:
    1. <hibernate-mapping>
    2.     <class name="Student" table="STUDENT">
    3.         <id name="id" type="string">
    4.             <column name="ID" length="10" />
    5.             <generator class="assigned" />
    6.         </id>
    7.         <property name="name" type="string">
    8.             <column name="NAME" not-null="true" />
    9.         </property>
    10.         <set name="courseSelections" inverse="true">
    11.             <key>
    12.                 <column name="STUDENT_ID" length="10" 
    13. not-null="true" />
    14.             </key>
    15.             <one-to-many class="CourseSelection" />
    16.         </set>
    17.     </class>
    18. </hibernate-mapping>

    這個(gè)文件定義了Student類和Student表是如何映射的。class元素定義了Sudent類和STUDENT表映射,然后就定義了各個(gè)屬性是如何映射的。如果一個(gè)屬性是數(shù)據(jù)庫的key,那么會(huì)用id標(biāo)簽來定義,column定義了當(dāng)前類的屬性和數(shù)據(jù)庫中的哪個(gè)字段對應(yīng),generator是id特有的。一般來說id是自增的,由于我的數(shù)據(jù)庫是用的Oracle,它沒有自增字段,要實(shí)現(xiàn)自增必須用Sequence,這超出了本文的范圍,所以我就用assigned來簡化示例代碼。assigned表示id是用戶給定的。
    有一個(gè)比較特別的標(biāo)簽是set,它對應(yīng)著數(shù)據(jù)庫中的外鍵關(guān)系,上文我提到的通過Student對象可以獲得所有相關(guān)的選課記錄就是通過這里的定義實(shí)現(xiàn)的。name屬性對應(yīng)了Student類中的字段名,key表示哪個(gè)字段是外鍵,one-to-many表示Student和CourseSelection是一對多關(guān)系,這和事實(shí)相符。類似的還有many-to-one,many-to-many,不過這些都不常用,我不介紹了。Hibernate根據(jù)這個(gè)映射定義文件,在實(shí)例化一個(gè)POJO(比如Student)的時(shí)候,會(huì)自動(dòng)的把定義過映射的屬性用數(shù)據(jù)庫中的數(shù)據(jù)填充,set也包括在內(nèi)。
    4.    hibernate.cfg.xml
    代碼片段7:
    1. <hibernate-configuration>
    2.     <session-factory>
    3.         <property name="connection.username">test</property>
    4.         <property name="connection.url">
    5. jdbc:oracle:thin:@10.85.33.199:1521:glee</property>
    6.         <property name="dialect">
    7. org.hibernate.dialect.Oracle9Dialect</property>
    8.         <property name="connection.password">test</property>
    9.         <property name="connection.driver_class">
    10. oracle.jdbc.OracleDriver</property>
    11.         <mapping resource="Student.hbm.xml"></mapping>
    12.         <mapping resource="CourseSelection.hbm.xml"></mapping>
    13.         <mapping resource="Course.hbm.xml"></mapping>
    14.     </session-factory>
    15. </hibernate-configuration>

    這個(gè)文件我不解釋了,自己看吧。結(jié)合上文StudentDAO的例子,我想你應(yīng)該能看明白。
    看了這么多,或許你會(huì)有點(diǎn)頭皮發(fā)麻,POJO,DAO,配置文件...好像要寫的東西還是很多。值得慶幸的是現(xiàn)在Hibernate已經(jīng)發(fā)展的比較成熟了,有很多工具來幫助我們完成這些工作,比如MiddleGen,Hibernate Synchronizer等等。我使用的開發(fā)工具是Eclipse+MyEclipse,我所要做的只是把數(shù)據(jù)庫表建好,然后MyEclipse提供的工具會(huì)自動(dòng)根據(jù)數(shù)據(jù)庫表生成POJO,DAO,*.hbm.xml,甚至hibernate.cfg.xml都是自動(dòng)完成的(前提是MyEclipse知道你的數(shù)據(jù)庫連接信息)。我并不打算介紹如何用IDE來開發(fā)Hibernate,你可以參考IDE的幫助文檔。
    到這里為止,使用Hibernate進(jìn)行開發(fā)的基本組成元素我都介紹好了,強(qiáng)烈建議你馬上實(shí)踐一遍,即使有些不理解,也先依葫蘆畫瓢一個(gè)。對了,別忘了把Hibernate的包down下來放到classpath里面。

    三、Session與SessionFactory
    Session可以說是Hibernate的核心,Hibernate對外暴露的接口就是Session。所以我這里講一下有關(guān)Session的常用函數(shù)和特性。
    在講Session之前,我想先提一下SessionFactory,這個(gè)東西不復(fù)雜,只要配置好就行了。顧名思義,SessionFactory就是用來創(chuàng)建Session的。SessionFactory是線程安全的,也就是說對于同一個(gè)數(shù)據(jù)庫的所有操作共享一個(gè)SessionFactory就行了。回頭看代碼片段5,我們可以看到SessionFactory的常用配置方式。
    代碼片段8:
    1. Configuration cfg = new Configuration();
    2. cfg.configure("/hibernate.cfg.xml");
    3. SessionFactory sessionFactory = cfg.buildSessionFactory();

    我們通過Configuration來讀取配置文件,然后就可以創(chuàng)建SessionFactory,這段代碼在    所有系統(tǒng)中都大同小異,一般就是xml配置文件的名字不一樣,所以也沒什么好說的。
    當(dāng)我們有了SessionFactory以后就可以獲取Session了。調(diào)用SessionFactory.openSession()就會(huì)返回一個(gè)Session實(shí)例,然后我們操作這個(gè)Session來訪問數(shù)據(jù)庫。值得一提的是Session并不是線程安全的,也就是每一個(gè)線程都必須有自己的Session。所以我們一般通過以下方法來獲取和關(guān)閉Session:
    代碼片段9:
    1. public static Session currentSession() throws HibernateException 
    2.     {
    3.         Session session = (Session) threadLocal.get();
    4.         if (session == null || !session.isOpen()) 
    5.         {
    6.             if (sessionFactory == null
    7.             {
    8.                 try 
    9.                 {
    10.                     cfg.configure(CONFIG_FILE_LOCATION);
    11.                     sessionFactory = cfg.buildSessionFactory();
    12.                 } 
    13.                 catch (Exception e) 
    14.                 {
    15.                     e.printStackTrace();
    16.                 }
    17.             }
    18.             session = (sessionFactory != null) ?
    19.                  sessionFactory.openSession(): null;
    20.             threadLocal.set(session);
    21.         }
    22.         return session;
    23. }
    24. public static void closeSession() throws HibernateException 
    25.     {
    26.         Session session = (Session) threadLocal.get();
    27.         threadLocal.set(null);
    28.         if (session != null
    29.         {
    30.             session.close();
    31.         }
    32. }

    可以看到,我們通過threadLocal來保存每個(gè)線程的session,這樣就保證了各個(gè)線程之    間的互不干擾,也保證了系統(tǒng)只有一個(gè)SessionFactory實(shí)例(對于大多數(shù)應(yīng)用來說已經(jīng)    足夠了)。如果你使用MyEclipse進(jìn)行開發(fā)的話,它會(huì)自動(dòng)生成一個(gè)    HibernateSessionFactory.java,其中就包含了以上代碼。
    好了,現(xiàn)在我們已經(jīng)獲得了Session,下面我來介紹以下Session的常用函數(shù),這些函數(shù)都有很多重載函數(shù),我只介紹以下大概是干嘛的,不一一解釋,詳細(xì)信息你可以查看Hibernate的API。
    1.Session.get(),獲取某個(gè)類的實(shí)例,一般都是通過id來獲取比如
    Session.get(Student.class, "0361095");
    這句話的意思就是獲取id(primary key)為“0361095”的Student對象。這里要注    意的是第二個(gè)參數(shù)必須是Object,也就是說,如果是long類型的1,那么必須轉(zhuǎn)換    成new Long(1)再傳入。
    2.Session.load(),用法和意義都和get一樣,不過它們還是有點(diǎn)區(qū)別,我稍后解釋。
    3.Session.save(),將某個(gè)實(shí)例保存到數(shù)據(jù)庫中去(往往在數(shù)據(jù)庫中形成一條新的記錄)。
    4.Session.update(),更新某個(gè)實(shí)例,這個(gè)實(shí)例必須和數(shù)據(jù)庫中有對應(yīng),否則會(huì)報(bào)錯(cuò)。
    5.Session.delete(),刪除某個(gè)實(shí)例,也就是刪除這個(gè)實(shí)例對應(yīng)的數(shù)據(jù)表中的數(shù)據(jù)。
    6.Session.saveOrUpdate(),保存或者更新某個(gè)實(shí)例,調(diào)用這個(gè)方法你就不用去關(guān)心到        底是save還是update了,它會(huì)自己判斷,然后調(diào)用相應(yīng)的函數(shù)。其實(shí)save和update        涉及到實(shí)體對象生命周期中的三種狀態(tài),這個(gè)比較重要,我在后面會(huì)單獨(dú)講的。
    對于get和load的區(qū)別,很難講清楚,這里涉及到Hibernate的緩存機(jī)制,是一個(gè)非常    復(fù)雜的話題,我不打算深入討論這個(gè)內(nèi)容。簡單的來講,Session實(shí)現(xiàn)了Hibernate的一    級緩存,SessionFactory實(shí)現(xiàn)了Hibernate的二級緩存。load方法會(huì)先查找一級緩存再查    找二級緩存,最后再去數(shù)據(jù)庫中找,而get只會(huì)查找一級緩存,然后就去數(shù)據(jù)庫中找了。    這只是是get和load的一個(gè)區(qū)別,另外的區(qū)別如果你有興趣的話自己去google吧。關(guān)于Hibernate的緩存機(jī)制,如果你只是一般用用Hibernate的話沒有必要深入研究,就當(dāng)它不存在好了,get和load到底用哪個(gè)也不用非常講究,如果你用工具生成DAO的話,    生成的代碼用什么就用什么吧。

    四、關(guān)鍵概念的理解
    在使用Hibernate進(jìn)行開發(fā)的時(shí)候有幾個(gè)概念在我看來是必須理解的,否則在開發(fā)的時(shí)候會(huì)遇到很多問題而摸不著頭腦。
    1.Lazy loading,懶加載(延遲加載)
    這個(gè)技術(shù)在很多地方被應(yīng)用,比如Eclipse的插件管理也是用的延遲加載。在Hibernate中,所謂延遲加載就是返回一個(gè)POJO但是某些數(shù)據(jù)(往往是實(shí)體類型或者Set類型)并沒有被真正的被填充,直到POJO的某個(gè)字段真正被引用的時(shí)候才從數(shù)據(jù)庫中讀取相應(yīng)的數(shù)據(jù)來填充POJO中的字段。這樣就能有效的避免很多不必要的數(shù)據(jù)庫操作,因?yàn)镻OJO的有些數(shù)據(jù)我們并不需要,而且數(shù)據(jù)庫操作是很費(fèi)時(shí)間的。在Hibernate2中,默認(rèn)是非延遲加載的,而在Hibernate3中,默認(rèn)就是延遲加載了。
    如果使用了延遲加載,那么在讀取數(shù)據(jù)的時(shí)候有一個(gè)問題必須注意,那就是在數(shù)據(jù)真正被加載之前,Session不能被關(guān)閉。你可以回頭看一下代碼片段5,我在構(gòu)造函數(shù)里面open了session以后就沒有關(guān)閉這個(gè)session,所以我在使用的時(shí)候沒有什么問題,但這樣總占著數(shù)據(jù)庫連接也不好,用好了應(yīng)該及時(shí)關(guān)閉給別人用。我上文給的例子中沒有關(guān)閉Session的代碼,要加的話給DAO加一個(gè)方法調(diào)用Session.close(),然后在代碼片段1中的return之前調(diào)用這個(gè)方法就行了。
    Hibernate的延遲加載機(jī)制遠(yuǎn)不是這么簡單,但是普通的應(yīng)用沒有必要去深究這些東西,了解這么多就夠了。

    2. Object lifecycle,對象生命周期
    在Hibernate中,對象分為三種狀態(tài),Transient(自由狀態(tài))、Persistent(持久狀態(tài)),Detached(游離狀態(tài)),下面我分別解釋一下。
    1、自由狀態(tài):所謂自由狀態(tài)就是說這個(gè)對象是自由的,與Hibernate無關(guān),比       如:
    Student student = new Student();
        student.setId("0361095");
                    這里的student就是一個(gè)普通的對象與hibernate無關(guān),稱為自由狀態(tài)。

    2、持久狀態(tài):所謂持久狀態(tài)就是指對象和數(shù)據(jù)庫中的數(shù)據(jù)(持久狀態(tài)的數(shù)據(jù))       有關(guān)聯(lián),也就是說對象被Hibernate所管理了,比如:
    session.save(student);
       這樣student對象就從自由狀態(tài)變?yōu)槌志脿顟B(tài)了。持久狀態(tài)的對象在Session       與數(shù)據(jù)庫中的數(shù)據(jù)進(jìn)行同步時(shí)(比如commit)會(huì)把數(shù)據(jù)更新到數(shù)據(jù)庫。而         其他狀態(tài)的則不會(huì)。我覺得可以這樣來理解持久狀態(tài),可以看成Hibernate       也擁有一份對象的引用,那么如果你對持久狀態(tài)對象的屬性進(jìn)行更改的話,       Hibernate看到的對象的狀態(tài)也更改了,而Hibernate所看到的對象和數(shù)據(jù)       庫中的數(shù)據(jù)是等價(jià)的。也正是這樣,Hibernate才實(shí)現(xiàn)了Object/Relation       的映射。類似的,load和get方法也一樣會(huì)獲取Persistent狀態(tài)的對象。

    3、游離狀態(tài):每次調(diào)用Session.close以后,所有跟這個(gè)session有關(guān)的處于
    Persistant的對象就變成了游離狀態(tài)。也許你要問Detached和Transient有什么區(qū)別。其實(shí)從我給的例子來說看不出什么區(qū)別,因?yàn)槲疫@里ID是給定的,而真正開發(fā)的時(shí)候ID往往是自增的,那么Transient的對象是沒有ID    的,當(dāng)save了以后就有了,顯而易見Detached的對象也是有ID,只不過這個(gè)對象已經(jīng)和Hibernate脫離了關(guān)系。但是游離狀態(tài)的對象仍然和數(shù)據(jù)庫    中的記錄有一定聯(lián)系,至少游離狀態(tài)的對象知道數(shù)據(jù)庫中有條記錄的ID為xxx。從這一點(diǎn)上來講,游離狀態(tài)是可以自己創(chuàng)造出來的,只要你知道數(shù)據(jù)庫中的主鍵信息。
    在使用Hibernate開發(fā)的時(shí)候要分清楚這三種狀態(tài),否則很容易出錯(cuò)。比如不能去save一個(gè)游離狀態(tài)的對象,不能去update一個(gè)自由狀態(tài)的對象等等。

    五、結(jié)束語
    我要講的就這么多,這篇文章不是詳細(xì)介紹Hibernate,我只是總結(jié)了自己學(xué)習(xí)和使用Hibernate的一些感受和經(jīng)驗(yàn),希望能給沒有用過Hibernate的開發(fā)者一個(gè)上手的指引。如果你看完了這篇文章仍然滿頭霧水,無從下手的話,我只能向你表示歉意,浪費(fèi)你的時(shí)間了。不管怎樣,我強(qiáng)烈推薦一本書《深入淺出Hibernate》,這本書既能作為學(xué)習(xí)的教程,也能作為日后的查詢用書,相當(dāng)實(shí)用。

    posted on 2008-01-10 11:39 靈! 閱讀(305) 評論(0)  編輯  收藏 所屬分類: Java技術(shù)-hibernate

    <2008年1月>
    303112345
    6789101112
    13141516171819
    20212223242526
    272829303112
    3456789

    導(dǎo)航

    統(tǒng)計(jì)

    隨筆分類

    隨筆檔案

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 一级毛片不卡片免费观看| 亚洲欧洲成人精品香蕉网| 无码av免费网站| 一级看片免费视频| 亚洲欧洲无码一区二区三区| 久久99国产亚洲精品观看| 免费在线观看理论片| 久久久久国色AV免费看图片| 免费A级毛片无码A∨| 91老湿机福利免费体验| 亚美影视免费在线观看| 久久精品国产亚洲AV| 亚洲免费综合色在线视频| 亚洲av无码片在线观看| 亚洲色图在线播放| 亚洲Av无码精品色午夜 | 日韩亚洲国产高清免费视频| 色婷婷亚洲十月十月色天| 亚洲成av人片天堂网| 亚洲中文久久精品无码| 国产亚洲精久久久久久无码AV| 亚洲福利中文字幕在线网址| 国产一级高清免费观看| 伊人久久亚洲综合影院| 亚洲av成人一区二区三区在线观看| 国产乱子伦片免费观看中字| 免费一级毛片正在播放| 亚洲成人国产精品| 亚洲欧洲中文日韩av乱码| 国产av无码专区亚洲国产精品| 亚洲国产精品无码久久青草| 亚洲精品国产V片在线观看| 免费在线观看视频a| 狠狠亚洲狠狠欧洲2019| 亚洲色偷拍另类无码专区| 亚洲成色www久久网站夜月| 亚洲av鲁丝一区二区三区| 亚洲美女视频网址| 67194在线午夜亚洲| 亚洲精品无码永久在线观看男男 | 在线观看av永久免费|