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

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

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

    我思故我強(qiáng)

    Hibernate上手指南


    Hibernate上手指南

    關(guān)鍵字: ? Hibernate上手指南????

    Hibernate上手
    ????
    ?Hibernate,很久以前我就聽(tīng)說(shuō)過(guò)這個(gè)名詞,但是似乎沒(méi)什么動(dòng)力讓我去接近它,感覺(jué)它是一個(gè)很復(fù)雜的東西,一直沒(méi)搞明白它到底是用來(lái)做什么的。直到接手了一個(gè)項(xiàng)目在技術(shù)選型的時(shí)候我再一次的看到了Hibernate。我嘗試著去使用它,發(fā)現(xiàn)它并不是我想像中的那么深?yuàn)W,它很易用。你并不需要了解它的內(nèi)部結(jié)構(gòu),它一樣能為你工作的很好,如果你理解了它到底能為你做什么的話
    本文著重講述了為什么要使用Hibernate,此外也簡(jiǎn)單的介紹了如何使用Hibernate,以及Hibernate中的一些基本概念。我想借這篇文章來(lái)向還沒(méi)有接觸過(guò)Hibernate的開(kāi)發(fā)者推薦款優(yōu)秀的開(kāi)源ORM產(chǎn)品,如果你已經(jīng)實(shí)踐過(guò)Hibernate,那么我想你沒(méi)有必要再看下去。

    一、Why Hibernate?
    ????? 現(xiàn)在流行“測(cè)試驅(qū)動(dòng)開(kāi)發(fā)”,相似的我覺(jué)得“目的驅(qū)動(dòng)學(xué)習(xí)”是一種比較好的接受新技術(shù),新知識(shí)的途徑。在學(xué)習(xí)一樣新的技術(shù)之前,首先得明確到底有沒(méi)有必要學(xué)習(xí),已有的技術(shù)是否已經(jīng)工作的很好,學(xué)習(xí)這個(gè)新的技術(shù)是為了解決什么問(wèn)題。如果你明確了以上問(wèn)題,那么尋找并學(xué)習(xí)新的技術(shù)將會(huì)事半功倍,并且能快速應(yīng)用到實(shí)際的開(kāi)發(fā)當(dāng)中來(lái)提高效益。
    要說(shuō)Hibernate,就得先介紹一下Object/Relation Mapper(ORM),中文翻譯為對(duì)象關(guān)系映射。之所以會(huì)產(chǎn)生這樣的概念是源于目前軟件開(kāi)發(fā)中的一些不協(xié)調(diào)的思想。目前流行的編程模型是OOP(Object Oriented Programming),面向?qū)ο蟮木幊蹋壳傲餍械臄?shù)據(jù)庫(kù)模型是Relational Database,這兩者思考的方式不一樣,這必然產(chǎn)生了開(kāi)發(fā)過(guò)程中的不協(xié)調(diào)。ORM框架(也稱為持久層框架,)的出現(xiàn)就是為了解決這樣的問(wèn)題,屏蔽底層數(shù)據(jù)庫(kù)的操作,以面向?qū)ο蟮姆绞教峁┙o開(kāi)發(fā)者操作數(shù)據(jù)庫(kù)中數(shù)據(jù)的接口。目前流行的ORM框架有Apach OJB,Hibernate,iBatis等等,當(dāng)然最完善,最好用的是Hibernate,至少我這樣認(rèn)為。或許你對(duì)“持久層”感到迷惑,其實(shí)說(shuō)白了很簡(jiǎn)單,把數(shù)據(jù)放到數(shù)據(jù)庫(kù)中叫做持久化(內(nèi)存種的數(shù)據(jù)當(dāng)然不是持久的),那么負(fù)責(zé)這一操作的結(jié)構(gòu)層面就叫做持久層。你以前應(yīng)該聽(tīng)說(shuō)過(guò)表現(xiàn)層,業(yè)務(wù)層,數(shù)據(jù)層,那么持久層是在業(yè)務(wù)層和數(shù)據(jù)層之間的一層,或者說(shuō)持久層是數(shù)據(jù)層的一部分。
    接下來(lái),我想通過(guò)一個(gè)實(shí)際開(kāi)發(fā)中的例子來(lái)說(shuō)明ORM帶給我們的好處。先來(lái)講一下我們的需求,數(shù)據(jù)庫(kù)中有三張表,一張student,一張course,另外一張course_slection。其中student用來(lái)保存學(xué)生信息,course用來(lái)表示課程信息,course_selection用來(lái)表示學(xué)生的選課信息。(表的詳細(xì)結(jié)構(gòu)這里我就省略了,因?yàn)檫@并不重要)現(xiàn)在要求編寫(xiě)一個(gè)程序,用來(lái)選出指定學(xué)號(hào)學(xué)生所選的課程名字,那么可能會(huì)出現(xiàn)以下幾種程序編寫(xiě)的方式:
    1.??? 菜鳥(niǎo)級(jí)
    代碼片段1:


    public List selectCourses(String studentId)
    ??? {
    ??????? Connection con = null;
    ??????? Statement sta = null;
    ??????? try?
    ??????? {
    ??????????? Class.forName("oracle.jdbc.driver.OracleDriver");
    ??????????? con = DriverManager.getConnection(
    ???????????????????????????? "jdbc:oracle:thin:@10.85.33.199:1521:glee",
    ????????????????????????????????????????????????????????? "test", "test");
    ??????????? String sql = "select * from course_selection";
    ??????????? String sql2 = "select name from course where id='";
    ??????????? sta = con.createStatement();
    ??????????? ResultSet rs = sta.executeQuery(sql);
    ??????????? List list = new LinkedList();
    ??????????? while (rs.next())?
    ??????????? {
    ??????????????? ResultSet rs2 = sta.executeQuery(sql2 +????????????????????????????
    ?????????????????????????????????????????? rs.getString("course_id") + "'");
    ??????????????? if (rs2.next())?
    ??????????????? {
    ??????????????????? list.add(rs2.getString("name"));
    ??????????????? }
    ??????????? }
    ??????????? return list;
    ??????? }?
    ??????? catch (Exception e)?
    ??????? {
    ??????????? e.printStackTrace();
    ??????? }
    ??????? return null;
    }

    這段程序你一定看的很暈吧,什么亂七八糟的都搞在一起,那么接下來(lái)看一段改進(jìn)過(guò)的程序。
    2.??? 改進(jìn)后的代碼
    代碼片段2:


    class DBHelper
    {
    ??? public static Connection getConnection()
    ??? {
    ??????? try?
    ??????? {
    ??????????? Class.forName(Constants.DB_DRIVER);
    ??????????? return DriverManager.getConnection(Constants.DB_URL,??
    ?????????????????????????????????????????? Constants.DB_USER, Constants.DB_PWD);
    ??????? }?
    ??????? catch (Exception e)?
    ??????? {
    ??????????? e.printStackTrace();
    ??????? }
    ??????? return null;
    ??? }
    }
    public List selectCourses(String studentId)
    ??? {
    ??????? Connection con = null;
    ??????? Statement sta = null;
    ??????? try?
    ??????? {
    ??????????? con = DBHelper.getConnection();
    ??????????? String sql = "select * from course_selection";
    ??????????? String sql2 = "select name from course where id='";
    ??????????? sta = con.createStatement();
    ??????????? ResultSet rs = sta.executeQuery(sql);
    ??????????? List list = new LinkedList();
    ??????????? while (rs.next())?
    ??????????? {
    ??????????????? ResultSet rs2 = sta.executeQuery(sql2 +???????????????????????????????? rs.getString("course_id") + "'");
    ??????????????? if (rs2.next())?
    ??????????????? {
    ??????????????????? list.add(rs2.getString("name"));
    ??????????????? }
    ??????????? }
    ??????????? return list;
    ??????? }?
    ??????? catch (Exception e)?
    ??????? {
    ??????????? e.printStackTrace();
    ??????? }
    ??????? return null;
    }

    這段代碼的形式是一種被廣泛采用的形式,相對(duì)第一段代碼來(lái)說(shuō),應(yīng)該已經(jīng)有所進(jìn)步,分離了數(shù)據(jù)庫(kù)連接操作,并把數(shù)據(jù)庫(kù)連接信息交給單獨(dú)的類完成(一般放在配置文件里面),往往在開(kāi)發(fā)中還會(huì)引入數(shù)據(jù)庫(kù)連接池(Connection Pool)來(lái)提高性能,我這里都盡量簡(jiǎn)化了。但這些并不能從根本上改善程序的結(jié)構(gòu),在業(yè)務(wù)代碼中仍然混雜了很多數(shù)據(jù)庫(kù)操作,結(jié)構(gòu)不清晰。下面來(lái)看一段徹底分離數(shù)據(jù)庫(kù)操作的代碼:
    3.??? DAO模式
    代碼片段3:


    public List selectCourses(String studentId)
    ??? {
    ??????? StudentDAO sd = new StudentDAO();
    ??????? Student student = sd.findById(studentId);
    ??????? Set set = student.getCourseSelections();
    ??????? List courseNames = new LinkedList();
    ??????? for (Iterator iter = set.iterator(); iter.hasNext();)?
    ??????? {
    ??????????? CourseSelection element = (CourseSelection) iter.next();
    ??????????? courseNames.add(element.getCourse().getName());
    ??????? }
    ??? return courseNames;
    }

    ?????? 是不是感覺(jué)代碼少了很多?或許你對(duì)這段代碼有點(diǎn)迷惑,沒(méi)關(guān)系,后文會(huì)詳細(xì)解釋。我想先解釋一下DAO。其實(shí)DAO和Hibernate沒(méi)有必然聯(lián)系,只不過(guò)一般用Hibernate的程序都用DAO模式。DAO的全稱是Data Access Object,程序要訪問(wèn)數(shù)據(jù)庫(kù)中的數(shù)據(jù)(包括獲取,更新,刪除)都通過(guò)DAO來(lái)訪問(wèn),實(shí)際上DAO才是真正屏蔽了所有數(shù)據(jù)庫(kù)操作的東西,這樣在業(yè)務(wù)代碼中就可以完全隔離數(shù)據(jù)層的代碼。如果我告訴你,在真正用Hibernate開(kāi)發(fā)的時(shí)候,要完成上文提到的功能,需要手寫(xiě)的代碼就是“代碼片段3”這么多,甚至更少,你是不是有很大的動(dòng)力去學(xué)習(xí)Hibernate?那么好吧,讓我們開(kāi)始Hibernate之旅。


    二、持久層的組成
    ??? 這一節(jié)的名字應(yīng)該換成“基于Hibernate的持久層的組成”更合適一點(diǎn),可是它太長(zhǎng)了。既然Hibernate是用來(lái)開(kāi)發(fā)持久層,那么我先介紹一下這個(gè)持久層中的各個(gè)元素。
    1.??? POJO:Plain Old Java Object,你可以把它看作是簡(jiǎn)單的JavaBean。一般說(shuō)來(lái),一張數(shù)據(jù)庫(kù)表對(duì)應(yīng)一個(gè)POJO,也就是對(duì)象/關(guān)系的一一映射。
    2.??? DAO:對(duì)于每一個(gè)POJO,一般都有一個(gè)DAO與之對(duì)應(yīng),承擔(dān)所有關(guān)于該P(yáng)OJO的訪問(wèn)控制。實(shí)際上也就是控制了對(duì)數(shù)據(jù)庫(kù)中一張表的訪問(wèn)控制。
    3.??? *.hbm.xml文件:這個(gè)文件定義了POJO和數(shù)據(jù)庫(kù)中的表是如何映射的,比如POJO中的字段對(duì)應(yīng)數(shù)據(jù)庫(kù)表中的哪個(gè)字段等等。一般每個(gè)映射都用單獨(dú)的文件來(lái)描述,也就是有一個(gè)POJO就有一個(gè)*.hbm.xml文件。
    4.??? *.cfg.xml文件:這個(gè)文件定義了Hibernate的基本信息,比如數(shù)據(jù)庫(kù)驅(qū)動(dòng),用戶名,密碼等等連接信息,也包括了所有要用的*.hbm.xml文件,在初始化的時(shí)候,Hibernate會(huì)讀取這個(gè)文件來(lái)找相應(yīng)的映射文件完成對(duì)象/關(guān)系。
    我們還是以上文的例子來(lái)詳細(xì)描述一下這里提到的各個(gè)元素的內(nèi)容。
    1.??? Student.java:
    代碼片段4:


    ?
    public class Student? implements java.io.Serializable?
    {
    ??? private String id;
    ??? private String name;
    ??? private Set courseSelections = new HashSet(0);
    ?
    ??? public Student()?
    ??? {
    ??? }
    ?
    ??? public String getId()?
    ??? {
    ??????? return this.id;
    ??? }
    ????
    ??? public void setId(String id)?
    ??? {
    ??????? this.id = id;
    ??? }
    ?
    ??? public String getName()?
    ??? {
    ??????? return this.name;
    ??? }
    ????
    ??? public void setName(String name)?
    ??? {
    ??????? this.name = name;
    ??? }
    ?
    ??? public Set getCourseSelections()?
    ??? {
    ??????? return this.courseSelections;
    ??? }
    ????
    ??? public void setCourseSelections(Set courseSelections)?
    ??? {
    ??????? this.courseSelections = courseSelections;
    ??? }
    }

    這個(gè)類就是一個(gè)POJO,你可以很明顯的看出來(lái)它就是一個(gè)JavaBean。我想解釋它的courseSelection字段。很顯然,在數(shù)據(jù)庫(kù)表student中,沒(méi)有這個(gè)字段。這里的這個(gè)字段是因?yàn)橐粋€(gè)外鍵引用,course_selection的student_id是一個(gè)外鍵,引用了student表中的id字段。那么在Student類中courseSelection來(lái)記錄這樣的外鍵關(guān)系,也就是說(shuō),當(dāng)我們獲取了Student對(duì)象以后,就可以直接獲取他的選課記錄,這樣就為上層的調(diào)用提供了很大的方便。這里有點(diǎn)模糊沒(méi)關(guān)系,我在介紹映射定義文件(*.hbm.xml)的時(shí)候還會(huì)提到這個(gè)問(wèn)題。
    2.??? StudentDAO.java
    代碼片段5:


    ?
    public class StudentDAO?
    {
    ??? Session session;
    ??? public StudentDAO()
    ??? {
    ??????? Configuration cfg = new Configuration();
    ??????? cfg.configure("/hibernate.cfg.xml");
    ??????? SessionFactory sessionFactory = cfg.buildSessionFactory();
    ??????? session = sessionFactory.openSession();
    ??? }
    ????
    ??? public void save(Student transientInstance)?
    ??? {
    ??????? session.save(transientInstance);
    ??? }
    ????
    ??? public void delete(Student persistentInstance)?
    ??? {
    ??????? session.delete(persistentInstance);
    ??? }
    ????
    ??? public Student findById(java.lang.String id)?
    ??? {
    ??????? List list = session.createCriteria(Student.class).add(
    Expression.eq("id", id)).list();
    ??????? if (list.size() > 0)?
    ??????? {
    ??????????? return (Student)list.get(0);
    ??????? }
    ??????? return null;
    ??? }
    }

    這里的構(gòu)造函數(shù)是用來(lái)啟動(dòng)Hibernate,并獲取session。打開(kāi)一個(gè)session就相當(dāng)于打開(kāi)了一個(gè)數(shù)據(jù)庫(kù)連接,然后我們就可以對(duì)這個(gè)session進(jìn)行操作,完成數(shù)據(jù)庫(kù)操作,完全不用寫(xiě)SQL語(yǔ)句。我這里Hibernate的啟動(dòng)方式寫(xiě)的很不規(guī)范,系統(tǒng)應(yīng)該只需要完成一次Hibernate啟動(dòng)就可以在不同的DAO中使用,我把它寫(xiě)在構(gòu)造函數(shù)里面純粹是為了簡(jiǎn)化演示代碼。
    你可以看到save和delete方法都很簡(jiǎn)單直接對(duì)對(duì)象操作,而findById就有些麻煩,因?yàn)檫@里有一個(gè)查詢過(guò)程在里面。Hibernate里面查詢可以用Criteria這個(gè)類來(lái)完成,我們也常用Hibernate獨(dú)有的HQL(Hibernate Query Language)來(lái)完成查詢。當(dāng)然Hibernate也是支持原生SQL的。關(guān)于查詢的詳細(xì)信息請(qǐng)參考其他文章或書(shū)籍,我只是演示一個(gè)流程,介紹一些概念。
    3.??? Student.hbm.xml
    代碼片段6:


    <hibernate-mapping>
    ??? <class name="Student" table="STUDENT">
    ??????? <id name="id" type="string">
    ??????????? <column name="ID" length="10" />
    ??????????? <generator class="assigned" />
    ??????? </id>
    ??????? <property name="name" type="string">
    ??????????? <column name="NAME" not-null="true" />
    ??????? </property>
    ??????? <set name="courseSelections" inverse="true">
    ??????????? <key>
    ??????????????? <column name="STUDENT_ID" length="10"?
    not-null="true" />
    ??????????? </key>
    ??????????? <one-to-many class="CourseSelection" />
    ??????? </set>
    ??? </class>
    </hibernate-mapping>???

    這個(gè)文件定義了Student類和Student表是如何映射的。class元素定義了Sudent類和STUDENT表映射,然后就定義了各個(gè)屬性是如何映射的。如果一個(gè)屬性是數(shù)據(jù)庫(kù)的key,那么會(huì)用id標(biāo)簽來(lái)定義,column定義了當(dāng)前類的屬性和數(shù)據(jù)庫(kù)中的哪個(gè)字段對(duì)應(yīng),generator是id特有的。一般來(lái)說(shuō)id是自增的,由于我的數(shù)據(jù)庫(kù)是用的Oracle,它沒(méi)有自增字段,要實(shí)現(xiàn)自增必須用Sequence,這超出了本文的范圍,所以我就用assigned來(lái)簡(jiǎn)化示例代碼。assigned表示id是用戶給定的。
    有一個(gè)比較特別的標(biāo)簽是set,它對(duì)應(yīng)著數(shù)據(jù)庫(kù)中的外鍵關(guān)系,上文我提到的通過(guò)Student對(duì)象可以獲得所有相關(guān)的選課記錄就是通過(guò)這里的定義實(shí)現(xiàn)的。name屬性對(duì)應(yīng)了Student類中的字段名,key表示哪個(gè)字段是外鍵,one-to-many表示Student和CourseSelection是一對(duì)多關(guān)系,這和事實(shí)相符。類似的還有many-to-one,many-to-many,不過(guò)這些都不常用,我不介紹了。Hibernate根據(jù)這個(gè)映射定義文件,在實(shí)例化一個(gè)POJO(比如Student)的時(shí)候,會(huì)自動(dòng)的把定義過(guò)映射的屬性用數(shù)據(jù)庫(kù)中的數(shù)據(jù)填充,set也包括在內(nèi)。
    4.??? hibernate.cfg.xml
    代碼片段7:


    <hibernate-configuration>
    ??? <session-factory>
    ??????? <property name="connection.username">test</property>
    ??????? <property name="connection.url">
    jdbc:oracle:thin:@10.85.33.199:1521:glee</property>
    ??????? <property name="dialect">
    org.hibernate.dialect.Oracle9Dialect</property>
    ??????? <property name="connection.password">test</property>
    ??????? <property name="connection.driver_class">
    oracle.jdbc.OracleDriver</property>
    ??????? <mapping resource="Student.hbm.xml"></mapping>
    ??????? <mapping resource="CourseSelection.hbm.xml"></mapping>
    ??????? <mapping resource="Course.hbm.xml"></mapping>
    ??? </session-factory>
    </hibernate-configuration>????

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

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


    ?
    Configuration cfg = new Configuration();
    cfg.configure("/hibernate.cfg.xml");
    SessionFactory sessionFactory = cfg.buildSessionFactory();

    我們通過(guò)Configuration來(lái)讀取配置文件,然后就可以創(chuàng)建SessionFactory,這段代碼在??? 所有系統(tǒng)中都大同小異,一般就是xml配置文件的名字不一樣,所以也沒(méi)什么好說(shuō)的。
    當(dāng)我們有了SessionFactory以后就可以獲取Session了。調(diào)用SessionFactory.openSession()就會(huì)返回一個(gè)Session實(shí)例,然后我們操作這個(gè)Session來(lái)訪問(wèn)數(shù)據(jù)庫(kù)。值得一提的是Session并不是線程安全的,也就是每一個(gè)線程都必須有自己的Session。所以我們一般通過(guò)以下方法來(lái)獲取和關(guān)閉Session:
    代碼片段9:


    ?
    public static Session currentSession() throws HibernateException?
    ??? {
    ??????? Session session = (Session) threadLocal.get();
    ??????? if (session == null || !session.isOpen())?
    ??????? {
    ??????????? if (sessionFactory == null)?
    ??????????? {
    ??????????????? try?
    ??????????????? {
    ??????????????????? cfg.configure(CONFIG_FILE_LOCATION);
    ??????????????????? sessionFactory = cfg.buildSessionFactory();
    ??????????????? }?
    ??????????????? catch (Exception e)?
    ??????????????? {
    ??????????????????? e.printStackTrace();
    ??????????????? }
    ??????????? }
    ??????????? session = (sessionFactory != null) ?
    ???????????????? sessionFactory.openSession(): null;
    ??????????? threadLocal.set(session);
    ??????? }
    ??????? return session;
    }
    ?
    public static void closeSession() throws HibernateException?
    ??? {
    ??????? Session session = (Session) threadLocal.get();
    ??????? threadLocal.set(null);
    ?
    ??????? if (session != null)?
    ??????? {
    ??????????? session.close();
    ??????? }
    }

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

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

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

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

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

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

    posted on 2007-09-20 20:17 李云澤 閱讀(596) 評(píng)論(1)  編輯  收藏 所屬分類: Hibernate

    評(píng)論

    # re: Hibernate上手指南 2007-12-16 11:25 ocaction

    太好了,怎么沒(méi)人頂。對(duì)于入門(mén)來(lái)說(shuō),理清了許多概念以及hibernate對(duì)象關(guān)系的認(rèn)識(shí)。  回復(fù)  更多評(píng)論   


    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 四只虎免费永久观看| 国产99精品一区二区三区免费| 成人妇女免费播放久久久| 日日夜夜精品免费视频| 国产精品亚洲专区无码WEB| 国产大片免费观看中文字幕| 阿v免费在线观看| 亚洲国产精品成人| 一级做a毛片免费视频| 亚洲av永久无码精品网站| 久久国产精品免费看| 亚洲精品mv在线观看| 巨胸喷奶水视频www网免费| 亚洲色大成网站WWW国产| 麻豆国产精品入口免费观看| japanese色国产在线看免费| 亚洲AV无码成人精品区日韩| 伊人久久亚洲综合| 99爱在线精品视频免费观看9| 亚洲免费人成视频观看| 热99re久久精品精品免费| 四虎永久在线观看免费网站网址| 亚洲乱码无人区卡1卡2卡3| 成人亚洲性情网站WWW在线观看| 91香蕉国产线观看免费全集| 亚洲欧美日韩一区二区三区 | 亚洲av成人一区二区三区| 四虎影在线永久免费观看| 性感美女视频在线观看免费精品| 无人在线观看完整免费版视频| 精品一区二区三区免费毛片| 久久精品九九亚洲精品| 免费看大美女大黄大色| 天天拍拍天天爽免费视频| 免费无码不卡视频在线观看| 最近免费中文字幕大全| baoyu777永久免费视频| 亚洲精华国产精华精华液 | 久久免费国产精品| 亚洲免费综合色在线视频| 亚洲乱妇熟女爽到高潮的片|