Hibernate上手指南(轉(zhuǎn)載)
Hibernate上手
Hibernate,很久以前我就聽說過這個名詞,但是似乎沒什么動力讓我去接近它,感覺它是一個很復雜的東西,一直沒搞明白它到底是用來做什么的。直到接手了一個項目在技術(shù)選型的時候我再一次的看到了Hibernate。我嘗試著去使用它,發(fā)現(xiàn)它并不是我想像中的那么深奧,它很易用。你并不需要了解它的內(nèi)部結(jié)構(gòu),它一樣能為你工作的很好,如果你理解了它到底能為你做什么的話
本文著重講述了為什么要使用Hibernate,此外也簡單的介紹了如何使用Hibernate,以及Hibernate中的一些基本概念。我想借這篇文章來向還沒有接觸過Hibernate的開發(fā)者推薦款優(yōu)秀的開源ORM產(chǎn)品,如果你已經(jīng)實踐過Hibernate,那么我想你沒有必要再看下去。
一、Why Hibernate?
現(xiàn)在流行“測試驅(qū)動開發(fā)”,相似的我覺得“目的驅(qū)動學習”是一種比較好的接受新技術(shù),新知識的途徑。在學習一樣新的技術(shù)之前,首先得明確到底有沒有必要學習,已有的技術(shù)是否已經(jīng)工作的很好,學習這個新的技術(shù)是為了解決什么問題。如果你明確了以上問題,那么尋找并學習新的技術(shù)將會事半功倍,并且能快速應用到實際的開發(fā)當中來提高效益。
要說Hibernate,就得先介紹一下Object/Relation Mapper(ORM),中文翻譯為對象關系映射。之所以會產(chǎn)生這樣的概念是源于目前軟件開發(fā)中的一些不協(xié)調(diào)的思想。目前流行的編程模型是OOP(Object Oriented Programming),面向?qū)ο蟮木幊蹋壳傲餍械臄?shù)據(jù)庫模型是Relational Database,這兩者思考的方式不一樣,這必然產(chǎn)生了開發(fā)過程中的不協(xié)調(diào)。ORM框架(也稱為持久層框架,)的出現(xiàn)就是為了解決這樣的問題,屏蔽底層數(shù)據(jù)庫的操作,以面向?qū)ο蟮姆绞教峁┙o開發(fā)者操作數(shù)據(jù)庫中數(shù)據(jù)的接口。目前流行的ORM框架有Apach OJB,Hibernate,iBatis等等,當然最完善,最好用的是Hibernate,至少我這樣認為。或許你對“持久層”感到迷惑,其實說白了很簡單,把數(shù)據(jù)放到數(shù)據(jù)庫中叫做持久化(內(nèi)存種的數(shù)據(jù)當然不是持久的),那么負責這一操作的結(jié)構(gòu)層面就叫做持久層。你以前應該聽說過表現(xiàn)層,業(yè)務層,數(shù)據(jù)層,那么持久層是在業(yè)務層和數(shù)據(jù)層之間的一層,或者說持久層是數(shù)據(jù)層的一部分。
接下來,我想通過一個實際開發(fā)中的例子來說明ORM帶給我們的好處。先來講一下我們的需求,數(shù)據(jù)庫中有三張表,一張student,一張course,另外一張course_slection。其中student用來保存學生信息,course用來表示課程信息,course_selection用來表示學生的選課信息。(表的詳細結(jié)構(gòu)這里我就省略了,因為這并不重要)現(xiàn)在要求編寫一個程序,用來選出指定學號學生所選的課程名字,那么可能會出現(xiàn)以下幾種程序編寫的方式:
1. 菜鳥級
代碼片段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;
}
這段程序你一定看的很暈吧,什么亂七八糟的都搞在一起,那么接下來看一段改進過的程序。
2. 改進后的代碼
代碼片段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;
}
感謝原作者的詳細解說,文章來源:http://java.chinaitlab.com/tools/845695.html
Hibernate,很久以前我就聽說過這個名詞,但是似乎沒什么動力讓我去接近它,感覺它是一個很復雜的東西,一直沒搞明白它到底是用來做什么的。直到接手了一個項目在技術(shù)選型的時候我再一次的看到了Hibernate。我嘗試著去使用它,發(fā)現(xiàn)它并不是我想像中的那么深奧,它很易用。你并不需要了解它的內(nèi)部結(jié)構(gòu),它一樣能為你工作的很好,如果你理解了它到底能為你做什么的話
本文著重講述了為什么要使用Hibernate,此外也簡單的介紹了如何使用Hibernate,以及Hibernate中的一些基本概念。我想借這篇文章來向還沒有接觸過Hibernate的開發(fā)者推薦款優(yōu)秀的開源ORM產(chǎn)品,如果你已經(jīng)實踐過Hibernate,那么我想你沒有必要再看下去。
一、Why Hibernate?
現(xiàn)在流行“測試驅(qū)動開發(fā)”,相似的我覺得“目的驅(qū)動學習”是一種比較好的接受新技術(shù),新知識的途徑。在學習一樣新的技術(shù)之前,首先得明確到底有沒有必要學習,已有的技術(shù)是否已經(jīng)工作的很好,學習這個新的技術(shù)是為了解決什么問題。如果你明確了以上問題,那么尋找并學習新的技術(shù)將會事半功倍,并且能快速應用到實際的開發(fā)當中來提高效益。
要說Hibernate,就得先介紹一下Object/Relation Mapper(ORM),中文翻譯為對象關系映射。之所以會產(chǎn)生這樣的概念是源于目前軟件開發(fā)中的一些不協(xié)調(diào)的思想。目前流行的編程模型是OOP(Object Oriented Programming),面向?qū)ο蟮木幊蹋壳傲餍械臄?shù)據(jù)庫模型是Relational Database,這兩者思考的方式不一樣,這必然產(chǎn)生了開發(fā)過程中的不協(xié)調(diào)。ORM框架(也稱為持久層框架,)的出現(xiàn)就是為了解決這樣的問題,屏蔽底層數(shù)據(jù)庫的操作,以面向?qū)ο蟮姆绞教峁┙o開發(fā)者操作數(shù)據(jù)庫中數(shù)據(jù)的接口。目前流行的ORM框架有Apach OJB,Hibernate,iBatis等等,當然最完善,最好用的是Hibernate,至少我這樣認為。或許你對“持久層”感到迷惑,其實說白了很簡單,把數(shù)據(jù)放到數(shù)據(jù)庫中叫做持久化(內(nèi)存種的數(shù)據(jù)當然不是持久的),那么負責這一操作的結(jié)構(gòu)層面就叫做持久層。你以前應該聽說過表現(xiàn)層,業(yè)務層,數(shù)據(jù)層,那么持久層是在業(yè)務層和數(shù)據(jù)層之間的一層,或者說持久層是數(shù)據(jù)層的一部分。
接下來,我想通過一個實際開發(fā)中的例子來說明ORM帶給我們的好處。先來講一下我們的需求,數(shù)據(jù)庫中有三張表,一張student,一張course,另外一張course_slection。其中student用來保存學生信息,course用來表示課程信息,course_selection用來表示學生的選課信息。(表的詳細結(jié)構(gòu)這里我就省略了,因為這并不重要)現(xiàn)在要求編寫一個程序,用來選出指定學號學生所選的課程名字,那么可能會出現(xiàn)以下幾種程序編寫的方式:
1. 菜鳥級
代碼片段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;
}
這段程序你一定看的很暈吧,什么亂七八糟的都搞在一起,那么接下來看一段改進過的程序。
2. 改進后的代碼
代碼片段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;
}
這段代碼的形式是一種被廣泛采用的形式,相對第一段代碼來說,應該已經(jīng)有所進步,分離了數(shù)據(jù)庫連接操作,并把數(shù)據(jù)庫連接信息交給單獨的類完成(一般放在配置文件里面),往往在開發(fā)中還會引入數(shù)據(jù)庫連接池(Connection Pool)來提高性能,我這里都盡量簡化了。但這些并不能從根本上改善程序的結(jié)構(gòu),在業(yè)務代碼中仍然混雜了很多數(shù)據(jù)庫操作,結(jié)構(gòu)不清晰。下面來看一段徹底分離數(shù)據(jù)庫操作的代碼:
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;
}
是不是感覺代碼少了很多?或許你對這段代碼有點迷惑,沒關系,后文會詳細解釋。我想先解釋一下DAO。其實DAO和Hibernate沒有必然聯(lián)系,只不過一般用Hibernate的程序都用DAO模式。DAO的全稱是Data Access Object,程序要訪問數(shù)據(jù)庫中的數(shù)據(jù)(包括獲取,更新,刪除)都通過DAO來訪問,實際上DAO才是真正屏蔽了所有數(shù)據(jù)庫操作的東西,這樣在業(yè)務代碼中就可以完全隔離數(shù)據(jù)層的代碼。如果我告訴你,在真正用Hibernate開發(fā)的時候,要完成上文提到的功能,需要手寫的代碼就是“代碼片段3”這么多,甚至更少,你是不是有很大的動力去學習Hibernate?那么好吧,讓我們開始Hibernate之旅。
二、持久層的組成
這一節(jié)的名字應該換成“基于Hibernate的持久層的組成”更合適一點,可是它太長了。既然Hibernate是用來開發(fā)持久層,那么我先介紹一下這個持久層中的各個元素。
1. POJO:Plain Old Java Object,你可以把它看作是簡單的JavaBean。一般說來,一張數(shù)據(jù)庫表對應一個POJO,也就是對象/關系的一一映射。
2. DAO:對于每一個POJO,一般都有一個DAO與之對應,承擔所有關于該POJO的訪問控制。實際上也就是控制了對數(shù)據(jù)庫中一張表的訪問控制。
3. *.hbm.xml文件:這個文件定義了POJO和數(shù)據(jù)庫中的表是如何映射的,比如POJO中的字段對應數(shù)據(jù)庫表中的哪個字段等等。一般每個映射都用單獨的文件來描述,也就是有一個POJO就有一個*.hbm.xml文件。
4. *.cfg.xml文件:這個文件定義了Hibernate的基本信息,比如數(shù)據(jù)庫驅(qū)動,用戶名,密碼等等連接信息,也包括了所有要用的*.hbm.xml文件,在初始化的時候,Hibernate會讀取這個文件來找相應的映射文件完成對象/關系。
我們還是以上文的例子來詳細描述一下這里提到的各個元素的內(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;
}
}
這個類就是一個POJO,你可以很明顯的看出來它就是一個JavaBean。我想解釋它的courseSelection字段。很顯然,在數(shù)據(jù)庫表student中,沒有這個字段。這里的這個字段是因為一個外鍵引用,course_selection的student_id是一個外鍵,引用了student表中的id字段。那么在Student類中courseSelection來記錄這樣的外鍵關系,也就是說,當我們獲取了Student對象以后,就可以直接獲取他的選課記錄,這樣就為上層的調(diào)用提供了很大的方便。這里有點模糊沒關系,我在介紹映射定義文件(*.hbm.xml)的時候還會提到這個問題。
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;
}
}
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;
}
是不是感覺代碼少了很多?或許你對這段代碼有點迷惑,沒關系,后文會詳細解釋。我想先解釋一下DAO。其實DAO和Hibernate沒有必然聯(lián)系,只不過一般用Hibernate的程序都用DAO模式。DAO的全稱是Data Access Object,程序要訪問數(shù)據(jù)庫中的數(shù)據(jù)(包括獲取,更新,刪除)都通過DAO來訪問,實際上DAO才是真正屏蔽了所有數(shù)據(jù)庫操作的東西,這樣在業(yè)務代碼中就可以完全隔離數(shù)據(jù)層的代碼。如果我告訴你,在真正用Hibernate開發(fā)的時候,要完成上文提到的功能,需要手寫的代碼就是“代碼片段3”這么多,甚至更少,你是不是有很大的動力去學習Hibernate?那么好吧,讓我們開始Hibernate之旅。
二、持久層的組成
這一節(jié)的名字應該換成“基于Hibernate的持久層的組成”更合適一點,可是它太長了。既然Hibernate是用來開發(fā)持久層,那么我先介紹一下這個持久層中的各個元素。
1. POJO:Plain Old Java Object,你可以把它看作是簡單的JavaBean。一般說來,一張數(shù)據(jù)庫表對應一個POJO,也就是對象/關系的一一映射。
2. DAO:對于每一個POJO,一般都有一個DAO與之對應,承擔所有關于該POJO的訪問控制。實際上也就是控制了對數(shù)據(jù)庫中一張表的訪問控制。
3. *.hbm.xml文件:這個文件定義了POJO和數(shù)據(jù)庫中的表是如何映射的,比如POJO中的字段對應數(shù)據(jù)庫表中的哪個字段等等。一般每個映射都用單獨的文件來描述,也就是有一個POJO就有一個*.hbm.xml文件。
4. *.cfg.xml文件:這個文件定義了Hibernate的基本信息,比如數(shù)據(jù)庫驅(qū)動,用戶名,密碼等等連接信息,也包括了所有要用的*.hbm.xml文件,在初始化的時候,Hibernate會讀取這個文件來找相應的映射文件完成對象/關系。
我們還是以上文的例子來詳細描述一下這里提到的各個元素的內(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;
}
}
這個類就是一個POJO,你可以很明顯的看出來它就是一個JavaBean。我想解釋它的courseSelection字段。很顯然,在數(shù)據(jù)庫表student中,沒有這個字段。這里的這個字段是因為一個外鍵引用,course_selection的student_id是一個外鍵,引用了student表中的id字段。那么在Student類中courseSelection來記錄這樣的外鍵關系,也就是說,當我們獲取了Student對象以后,就可以直接獲取他的選課記錄,這樣就為上層的調(diào)用提供了很大的方便。這里有點模糊沒關系,我在介紹映射定義文件(*.hbm.xml)的時候還會提到這個問題。
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ù)是用來啟動Hibernate,并獲取session。打開一個session就相當于打開了一個數(shù)據(jù)庫連接,然后我們就可以對這個session進行操作,完成數(shù)據(jù)庫操作,完全不用寫SQL語句。我這里Hibernate的啟動方式寫的很不規(guī)范,系統(tǒng)應該只需要完成一次Hibernate啟動就可以在不同的DAO中使用,我把它寫在構(gòu)造函數(shù)里面純粹是為了簡化演示代碼。
你可以看到save和delete方法都很簡單直接對對象操作,而findById就有些麻煩,因為這里有一個查詢過程在里面。Hibernate里面查詢可以用Criteria這個類來完成,我們也常用Hibernate獨有的HQL(Hibernate Query Language)來完成查詢。當然Hibernate也是支持原生SQL的。關于查詢的詳細信息請參考其他文章或書籍,我只是演示一個流程,介紹一些概念。
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>
這個文件定義了Student類和Student表是如何映射的。class元素定義了Sudent類和STUDENT表映射,然后就定義了各個屬性是如何映射的。如果一個屬性是數(shù)據(jù)庫的key,那么會用id標簽來定義,column定義了當前類的屬性和數(shù)據(jù)庫中的哪個字段對應,generator是id特有的。一般來說id是自增的,由于我的數(shù)據(jù)庫是用的Oracle,它沒有自增字段,要實現(xiàn)自增必須用Sequence,這超出了本文的范圍,所以我就用assigned來簡化示例代碼。assigned表示id是用戶給定的。
有一個比較特別的標簽是set,它對應著數(shù)據(jù)庫中的外鍵關系,上文我提到的通過Student對象可以獲得所有相關的選課記錄就是通過這里的定義實現(xiàn)的。name屬性對應了Student類中的字段名,key表示哪個字段是外鍵,one-to-many表示Student和CourseSelection是一對多關系,這和事實相符。類似的還有many-to-one,many-to-many,不過這些都不常用,我不介紹了。Hibernate根據(jù)這個映射定義文件,在實例化一個POJO(比如Student)的時候,會自動的把定義過映射的屬性用數(shù)據(jù)庫中的數(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>
這個文件我不解釋了,自己看吧。結(jié)合上文StudentDAO的例子,我想你應該能看明白。
看了這么多,或許你會有點頭皮發(fā)麻,POJO,DAO,配置文件…好像要寫的東西還是很多。值得慶幸的是現(xiàn)在Hibernate已經(jīng)發(fā)展的比較成熟了,有很多工具來幫助我們完成這些工作,比如MiddleGen,Hibernate Synchronizer等等。我使用的開發(fā)工具是Eclipse+MyEclipse,我所要做的只是把數(shù)據(jù)庫表建好,然后MyEclipse提供的工具會自動根據(jù)數(shù)據(jù)庫表生成POJO,DAO,*.hbm.xml,甚至hibernate.cfg.xml都是自動完成的(前提是MyEclipse知道你的數(shù)據(jù)庫連接信息)。我并不打算介紹如何用IDE來開發(fā)Hibernate,你可以參考IDE的幫助文檔。
到這里為止,使用Hibernate進行開發(fā)的基本組成元素我都介紹好了,強烈建議你馬上實踐一遍,即使有些不理解,也先依葫蘆畫瓢一個。對了,別忘了把Hibernate的包down下來放到classpath里面。
三、Session與SessionFactory
Session可以說是Hibernate的核心,Hibernate對外暴露的接口就是Session。所以我這里講一下有關Session的常用函數(shù)和特性。
在講Session之前,我想先提一下SessionFactory,這個東西不復雜,只要配置好就行了。顧名思義,SessionFactory就是用來創(chuàng)建Session的。SessionFactory是線程安全的,也就是說對于同一個數(shù)據(jù)庫的所有操作共享一個SessionFactory就行了。回頭看代碼片段5,我們可以看到SessionFactory的常用配置方式。
代碼片段8:
Configuration cfg = new Configuration();
cfg.configure("/hibernate.cfg.xml");
SessionFactory sessionFactory = cfg.buildSessionFactory();
我們通過Configuration來讀取配置文件,然后就可以創(chuàng)建SessionFactory,這段代碼在 所有系統(tǒng)中都大同小異,一般就是xml配置文件的名字不一樣,所以也沒什么好說的。
當我們有了SessionFactory以后就可以獲取Session了。調(diào)用SessionFactory.openSession()就會返回一個Session實例,然后我們操作這個Session來訪問數(shù)據(jù)庫。值得一提的是Session并不是線程安全的,也就是每一個線程都必須有自己的Session。所以我們一般通過以下方法來獲取和關閉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();
}
}
可以看到,我們通過threadLocal來保存每個線程的session,這樣就保證了各個線程之 間的互不干擾,也保證了系統(tǒng)只有一個SessionFactory實例(對于大多數(shù)應用來說已經(jīng) 足夠了)。如果你使用MyEclipse進行開發(fā)的話,它會自動生成一個 HibernateSessionFactory.java,其中就包含了以上代碼。
好了,現(xiàn)在我們已經(jīng)獲得了Session,下面我來介紹以下Session的常用函數(shù),這些函數(shù)都有很多重載函數(shù),我只介紹以下大概是干嘛的,不一一解釋,詳細信息你可以查看Hibernate的API。
1.Session.get(),獲取某個類的實例,一般都是通過id來獲取比如
Session.get(Student.class, "0361095");
這句話的意思就是獲取id(primary key)為“0361095”的Student對象。這里要注 意的是第二個參數(shù)必須是Object,也就是說,如果是long類型的1,那么必須轉(zhuǎn)換 成new Long(1)再傳入。
2.Session.load(),用法和意義都和get一樣,不過它們還是有點區(qū)別,我稍后解釋。
3.Session.save(),將某個實例保存到數(shù)據(jù)庫中去(往往在數(shù)據(jù)庫中形成一條新的記錄)。
4.Session.update(),更新某個實例,這個實例必須和數(shù)據(jù)庫中有對應,否則會報錯。
5.Session.delete(),刪除某個實例,也就是刪除這個實例對應的數(shù)據(jù)表中的數(shù)據(jù)。
6.Session.saveOrUpdate(),保存或者更新某個實例,調(diào)用這個方法你就不用去關心到 底是save還是update了,它會自己判斷,然后調(diào)用相應的函數(shù)。其實save和update 涉及到實體對象生命周期中的三種狀態(tài),這個比較重要,我在后面會單獨講的。
對于get和load的區(qū)別,很難講清楚,這里涉及到Hibernate的緩存機制,是一個非常 復雜的話題,我不打算深入討論這個內(nèi)容。簡單的來講,Session實現(xiàn)了Hibernate的一 級緩存,SessionFactory實現(xiàn)了Hibernate的二級緩存。load方法會先查找一級緩存再查 找二級緩存,最后再去數(shù)據(jù)庫中找,而get只會查找一級緩存,然后就去數(shù)據(jù)庫中找了。 這只是是get和load的一個區(qū)別,另外的區(qū)別如果你有興趣的話自己去google吧。關于Hibernate的緩存機制,如果你只是一般用用Hibernate的話沒有必要深入研究,就當它不存在好了,get和load到底用哪個也不用非常講究,如果你用工具生成DAO的話, 生成的代碼用什么就用什么吧。
四、關鍵概念的理解
在使用Hibernate進行開發(fā)的時候有幾個概念在我看來是必須理解的,否則在開發(fā)的時候會遇到很多問題而摸不著頭腦。
1.Lazy loading,懶加載(延遲加載)
這個技術(shù)在很多地方被應用,比如Eclipse的插件管理也是用的延遲加載。在Hibernate中,所謂延遲加載就是返回一個POJO但是某些數(shù)據(jù)(往往是實體類型或者Set類型)并沒有被真正的被填充,直到POJO的某個字段真正被引用的時候才從數(shù)據(jù)庫中讀取相應的數(shù)據(jù)來填充POJO中的字段。這樣就能有效的避免很多不必要的數(shù)據(jù)庫操作,因為POJO的有些數(shù)據(jù)我們并不需要,而且數(shù)據(jù)庫操作是很費時間的。在Hibernate2中,默認是非延遲加載的,而在Hibernate3中,默認就是延遲加載了。
如果使用了延遲加載,那么在讀取數(shù)據(jù)的時候有一個問題必須注意,那就是在數(shù)據(jù)真正被加載之前,Session不能被關閉。你可以回頭看一下代碼片段5,我在構(gòu)造函數(shù)里面open了session以后就沒有關閉這個session,所以我在使用的時候沒有什么問題,但這樣總占著數(shù)據(jù)庫連接也不好,用好了應該及時關閉給別人用。我上文給的例子中沒有關閉Session的代碼,要加的話給DAO加一個方法調(diào)用Session.close(),然后在代碼片段1中的return之前調(diào)用這個方法就行了。
Hibernate的延遲加載機制遠不是這么簡單,但是普通的應用沒有必要去深究這些東西,了解這么多就夠了。
2. Object lifecycle,對象生命周期
在Hibernate中,對象分為三種狀態(tài),Transient(自由狀態(tài))、Persistent(持久狀態(tài)),Detached(游離狀態(tài)),下面我分別解釋一下。
1、自由狀態(tài):所謂自由狀態(tài)就是說這個對象是自由的,與Hibernate無關,比 如:
Student student = new Student();
student.setId("0361095");
這里的student就是一個普通的對象與hibernate無關,稱為自由狀態(tài)。
2、持久狀態(tài):所謂持久狀態(tài)就是指對象和數(shù)據(jù)庫中的數(shù)據(jù)(持久狀態(tài)的數(shù)據(jù)) 有關聯(lián),也就是說對象被Hibernate所管理了,比如:
session.save(student);
這樣student對象就從自由狀態(tài)變?yōu)槌志脿顟B(tài)了。持久狀態(tài)的對象在Session 與數(shù)據(jù)庫中的數(shù)據(jù)進行同步時(比如commit)會把數(shù)據(jù)更新到數(shù)據(jù)庫。而 其他狀態(tài)的則不會。我覺得可以這樣來理解持久狀態(tài),可以看成Hibernate 也擁有一份對象的引用,那么如果你對持久狀態(tài)對象的屬性進行更改的話, Hibernate看到的對象的狀態(tài)也更改了,而Hibernate所看到的對象和數(shù)據(jù) 庫中的數(shù)據(jù)是等價的。也正是這樣,Hibernate才實現(xiàn)了Object/Relation 的映射。類似的,load和get方法也一樣會獲取Persistent狀態(tài)的對象。
3、游離狀態(tài):每次調(diào)用Session.close以后,所有跟這個session有關的處于
Persistant的對象就變成了游離狀態(tài)。也許你要問Detached和Transient有什么區(qū)別。其實從我給的例子來說看不出什么區(qū)別,因為我這里ID是給定的,而真正開發(fā)的時候ID往往是自增的,那么Transient的對象是沒有ID 的,當save了以后就有了,顯而易見Detached的對象也是有ID,只不過這個對象已經(jīng)和Hibernate脫離了關系。但是游離狀態(tài)的對象仍然和數(shù)據(jù)庫 中的記錄有一定聯(lián)系,至少游離狀態(tài)的對象知道數(shù)據(jù)庫中有條記錄的ID為xxx。從這一點上來講,游離狀態(tài)是可以自己創(chuàng)造出來的,只要你知道數(shù)據(jù)庫中的主鍵信息。
在使用Hibernate開發(fā)的時候要分清楚這三種狀態(tài),否則很容易出錯。比如不能去save一個游離狀態(tài)的對象,不能去update一個自由狀態(tài)的對象等等。
五、結(jié)束語
我要講的就這么多,這篇文章不是詳細介紹Hibernate,我只是總結(jié)了自己學習和使用Hibernate的一些感受和經(jīng)驗,希望能給沒有用過Hibernate的開發(fā)者一個上手的指引。如果你看完了這篇文章仍然滿頭霧水,無從下手的話,我只能向你表示歉意,浪費你的時間了。不管怎樣,我強烈推薦一本書《深入淺出Hibernate》,這本書既能作為學習的教程,也能作為日后的查詢用書,相當實用。
你可以看到save和delete方法都很簡單直接對對象操作,而findById就有些麻煩,因為這里有一個查詢過程在里面。Hibernate里面查詢可以用Criteria這個類來完成,我們也常用Hibernate獨有的HQL(Hibernate Query Language)來完成查詢。當然Hibernate也是支持原生SQL的。關于查詢的詳細信息請參考其他文章或書籍,我只是演示一個流程,介紹一些概念。
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>
這個文件定義了Student類和Student表是如何映射的。class元素定義了Sudent類和STUDENT表映射,然后就定義了各個屬性是如何映射的。如果一個屬性是數(shù)據(jù)庫的key,那么會用id標簽來定義,column定義了當前類的屬性和數(shù)據(jù)庫中的哪個字段對應,generator是id特有的。一般來說id是自增的,由于我的數(shù)據(jù)庫是用的Oracle,它沒有自增字段,要實現(xiàn)自增必須用Sequence,這超出了本文的范圍,所以我就用assigned來簡化示例代碼。assigned表示id是用戶給定的。
有一個比較特別的標簽是set,它對應著數(shù)據(jù)庫中的外鍵關系,上文我提到的通過Student對象可以獲得所有相關的選課記錄就是通過這里的定義實現(xiàn)的。name屬性對應了Student類中的字段名,key表示哪個字段是外鍵,one-to-many表示Student和CourseSelection是一對多關系,這和事實相符。類似的還有many-to-one,many-to-many,不過這些都不常用,我不介紹了。Hibernate根據(jù)這個映射定義文件,在實例化一個POJO(比如Student)的時候,會自動的把定義過映射的屬性用數(shù)據(jù)庫中的數(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>
這個文件我不解釋了,自己看吧。結(jié)合上文StudentDAO的例子,我想你應該能看明白。
看了這么多,或許你會有點頭皮發(fā)麻,POJO,DAO,配置文件…好像要寫的東西還是很多。值得慶幸的是現(xiàn)在Hibernate已經(jīng)發(fā)展的比較成熟了,有很多工具來幫助我們完成這些工作,比如MiddleGen,Hibernate Synchronizer等等。我使用的開發(fā)工具是Eclipse+MyEclipse,我所要做的只是把數(shù)據(jù)庫表建好,然后MyEclipse提供的工具會自動根據(jù)數(shù)據(jù)庫表生成POJO,DAO,*.hbm.xml,甚至hibernate.cfg.xml都是自動完成的(前提是MyEclipse知道你的數(shù)據(jù)庫連接信息)。我并不打算介紹如何用IDE來開發(fā)Hibernate,你可以參考IDE的幫助文檔。
到這里為止,使用Hibernate進行開發(fā)的基本組成元素我都介紹好了,強烈建議你馬上實踐一遍,即使有些不理解,也先依葫蘆畫瓢一個。對了,別忘了把Hibernate的包down下來放到classpath里面。
三、Session與SessionFactory
Session可以說是Hibernate的核心,Hibernate對外暴露的接口就是Session。所以我這里講一下有關Session的常用函數(shù)和特性。
在講Session之前,我想先提一下SessionFactory,這個東西不復雜,只要配置好就行了。顧名思義,SessionFactory就是用來創(chuàng)建Session的。SessionFactory是線程安全的,也就是說對于同一個數(shù)據(jù)庫的所有操作共享一個SessionFactory就行了。回頭看代碼片段5,我們可以看到SessionFactory的常用配置方式。
代碼片段8:
Configuration cfg = new Configuration();
cfg.configure("/hibernate.cfg.xml");
SessionFactory sessionFactory = cfg.buildSessionFactory();
我們通過Configuration來讀取配置文件,然后就可以創(chuàng)建SessionFactory,這段代碼在 所有系統(tǒng)中都大同小異,一般就是xml配置文件的名字不一樣,所以也沒什么好說的。
當我們有了SessionFactory以后就可以獲取Session了。調(diào)用SessionFactory.openSession()就會返回一個Session實例,然后我們操作這個Session來訪問數(shù)據(jù)庫。值得一提的是Session并不是線程安全的,也就是每一個線程都必須有自己的Session。所以我們一般通過以下方法來獲取和關閉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();
}
}
可以看到,我們通過threadLocal來保存每個線程的session,這樣就保證了各個線程之 間的互不干擾,也保證了系統(tǒng)只有一個SessionFactory實例(對于大多數(shù)應用來說已經(jīng) 足夠了)。如果你使用MyEclipse進行開發(fā)的話,它會自動生成一個 HibernateSessionFactory.java,其中就包含了以上代碼。
好了,現(xiàn)在我們已經(jīng)獲得了Session,下面我來介紹以下Session的常用函數(shù),這些函數(shù)都有很多重載函數(shù),我只介紹以下大概是干嘛的,不一一解釋,詳細信息你可以查看Hibernate的API。
1.Session.get(),獲取某個類的實例,一般都是通過id來獲取比如
Session.get(Student.class, "0361095");
這句話的意思就是獲取id(primary key)為“0361095”的Student對象。這里要注 意的是第二個參數(shù)必須是Object,也就是說,如果是long類型的1,那么必須轉(zhuǎn)換 成new Long(1)再傳入。
2.Session.load(),用法和意義都和get一樣,不過它們還是有點區(qū)別,我稍后解釋。
3.Session.save(),將某個實例保存到數(shù)據(jù)庫中去(往往在數(shù)據(jù)庫中形成一條新的記錄)。
4.Session.update(),更新某個實例,這個實例必須和數(shù)據(jù)庫中有對應,否則會報錯。
5.Session.delete(),刪除某個實例,也就是刪除這個實例對應的數(shù)據(jù)表中的數(shù)據(jù)。
6.Session.saveOrUpdate(),保存或者更新某個實例,調(diào)用這個方法你就不用去關心到 底是save還是update了,它會自己判斷,然后調(diào)用相應的函數(shù)。其實save和update 涉及到實體對象生命周期中的三種狀態(tài),這個比較重要,我在后面會單獨講的。
對于get和load的區(qū)別,很難講清楚,這里涉及到Hibernate的緩存機制,是一個非常 復雜的話題,我不打算深入討論這個內(nèi)容。簡單的來講,Session實現(xiàn)了Hibernate的一 級緩存,SessionFactory實現(xiàn)了Hibernate的二級緩存。load方法會先查找一級緩存再查 找二級緩存,最后再去數(shù)據(jù)庫中找,而get只會查找一級緩存,然后就去數(shù)據(jù)庫中找了。 這只是是get和load的一個區(qū)別,另外的區(qū)別如果你有興趣的話自己去google吧。關于Hibernate的緩存機制,如果你只是一般用用Hibernate的話沒有必要深入研究,就當它不存在好了,get和load到底用哪個也不用非常講究,如果你用工具生成DAO的話, 生成的代碼用什么就用什么吧。
四、關鍵概念的理解
在使用Hibernate進行開發(fā)的時候有幾個概念在我看來是必須理解的,否則在開發(fā)的時候會遇到很多問題而摸不著頭腦。
1.Lazy loading,懶加載(延遲加載)
這個技術(shù)在很多地方被應用,比如Eclipse的插件管理也是用的延遲加載。在Hibernate中,所謂延遲加載就是返回一個POJO但是某些數(shù)據(jù)(往往是實體類型或者Set類型)并沒有被真正的被填充,直到POJO的某個字段真正被引用的時候才從數(shù)據(jù)庫中讀取相應的數(shù)據(jù)來填充POJO中的字段。這樣就能有效的避免很多不必要的數(shù)據(jù)庫操作,因為POJO的有些數(shù)據(jù)我們并不需要,而且數(shù)據(jù)庫操作是很費時間的。在Hibernate2中,默認是非延遲加載的,而在Hibernate3中,默認就是延遲加載了。
如果使用了延遲加載,那么在讀取數(shù)據(jù)的時候有一個問題必須注意,那就是在數(shù)據(jù)真正被加載之前,Session不能被關閉。你可以回頭看一下代碼片段5,我在構(gòu)造函數(shù)里面open了session以后就沒有關閉這個session,所以我在使用的時候沒有什么問題,但這樣總占著數(shù)據(jù)庫連接也不好,用好了應該及時關閉給別人用。我上文給的例子中沒有關閉Session的代碼,要加的話給DAO加一個方法調(diào)用Session.close(),然后在代碼片段1中的return之前調(diào)用這個方法就行了。
Hibernate的延遲加載機制遠不是這么簡單,但是普通的應用沒有必要去深究這些東西,了解這么多就夠了。
2. Object lifecycle,對象生命周期
在Hibernate中,對象分為三種狀態(tài),Transient(自由狀態(tài))、Persistent(持久狀態(tài)),Detached(游離狀態(tài)),下面我分別解釋一下。
1、自由狀態(tài):所謂自由狀態(tài)就是說這個對象是自由的,與Hibernate無關,比 如:
Student student = new Student();
student.setId("0361095");
這里的student就是一個普通的對象與hibernate無關,稱為自由狀態(tài)。
2、持久狀態(tài):所謂持久狀態(tài)就是指對象和數(shù)據(jù)庫中的數(shù)據(jù)(持久狀態(tài)的數(shù)據(jù)) 有關聯(lián),也就是說對象被Hibernate所管理了,比如:
session.save(student);
這樣student對象就從自由狀態(tài)變?yōu)槌志脿顟B(tài)了。持久狀態(tài)的對象在Session 與數(shù)據(jù)庫中的數(shù)據(jù)進行同步時(比如commit)會把數(shù)據(jù)更新到數(shù)據(jù)庫。而 其他狀態(tài)的則不會。我覺得可以這樣來理解持久狀態(tài),可以看成Hibernate 也擁有一份對象的引用,那么如果你對持久狀態(tài)對象的屬性進行更改的話, Hibernate看到的對象的狀態(tài)也更改了,而Hibernate所看到的對象和數(shù)據(jù) 庫中的數(shù)據(jù)是等價的。也正是這樣,Hibernate才實現(xiàn)了Object/Relation 的映射。類似的,load和get方法也一樣會獲取Persistent狀態(tài)的對象。
3、游離狀態(tài):每次調(diào)用Session.close以后,所有跟這個session有關的處于
Persistant的對象就變成了游離狀態(tài)。也許你要問Detached和Transient有什么區(qū)別。其實從我給的例子來說看不出什么區(qū)別,因為我這里ID是給定的,而真正開發(fā)的時候ID往往是自增的,那么Transient的對象是沒有ID 的,當save了以后就有了,顯而易見Detached的對象也是有ID,只不過這個對象已經(jīng)和Hibernate脫離了關系。但是游離狀態(tài)的對象仍然和數(shù)據(jù)庫 中的記錄有一定聯(lián)系,至少游離狀態(tài)的對象知道數(shù)據(jù)庫中有條記錄的ID為xxx。從這一點上來講,游離狀態(tài)是可以自己創(chuàng)造出來的,只要你知道數(shù)據(jù)庫中的主鍵信息。
在使用Hibernate開發(fā)的時候要分清楚這三種狀態(tài),否則很容易出錯。比如不能去save一個游離狀態(tài)的對象,不能去update一個自由狀態(tài)的對象等等。
五、結(jié)束語
我要講的就這么多,這篇文章不是詳細介紹Hibernate,我只是總結(jié)了自己學習和使用Hibernate的一些感受和經(jīng)驗,希望能給沒有用過Hibernate的開發(fā)者一個上手的指引。如果你看完了這篇文章仍然滿頭霧水,無從下手的話,我只能向你表示歉意,浪費你的時間了。不管怎樣,我強烈推薦一本書《深入淺出Hibernate》,這本書既能作為學習的教程,也能作為日后的查詢用書,相當實用。
感謝原作者的詳細解說,文章來源:http://java.chinaitlab.com/tools/845695.html
posted on 2011-07-15 14:00 七孑 閱讀(254) 評論(0) 編輯 收藏 所屬分類: SSH構(gòu)架精要