目前主流數據庫是關係型數據庫(RDB),而Java則是面向對象的編程語言(OOPL),要結合兩者相當麻煩。
ORM是爲了解決這一問題而產生的概念。
OOPL的優勢:
面向對象的建模、操作
多態、繼承
摒棄難以理解的過程建安易用,易理解
RDB的優勢:
大量數據查找、排序
集合數據連接操作、映射
數據庫訪問的併發、事務數據庫的約束、隔離
當ORM框架完成映射後,即可以利用OOPL的簡單易用,又能使用RDB的技術優勢。
其實JavaEE規範裏的JPA規範就是一種ORM規範,只是JPA並不提供任何ORM實現,只提供了系列的編程接口。
==============================ORM和Hibernate的關係==============================
Hibernate是一個ORM框架,管理Java類到數據庫表的映射,還提供查詢和獲取數據的方法,大幅度縮短使用JDBC處理數據持久化的時間。
==============================Hibernate的基本映射思想==============================
所有ORM工具大致上都遵循相同的映射思路:數據表映射類、數據表的行映射對象(即實例)、數據表的列(字段)映射對象的屬性。
==============================Hibernate入門知識==============================
Hibernate被稱爲低侵入式設計,因爲它採用POJO作爲PO,不要求持久化類繼承任何父類,或者實現任何接口。
Hibernate底層依然是基於JDBC的,因此應用程序使用它少不了JDBC驅動。
所有ORM框架中有一個非常重要的媒介:PO,通過該對象可對數據執行CRUD操作,以OO的方式操作DB。
數據源是一種提高數據庫連接性能的常規手段,hibernate推薦採用c3p0數據源管理數據庫連接。
在官網上下載Hibernate的壓縮包後,裏面的hibernate.jar和lib下的required、jpa子目錄下所有jar包是必須的。
一個簡單的示例:
//domain包下的POJO
public class News {
private Integer id;
private String title;
private String content;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- 指定Hibernate3映射文件的DTD信息 -->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- hibernate mapping是映射文件的根元素 -->
<hibernate-mapping package="basicusage.chapter1">
<!-- 每個class元素對應一個持久化對象 -->
<class name="News" table="t_newss">
<!-- id元素定義持久化類的標識屬性 -->
<id name="id">
<!-- 指定主鍵生成策略 -->
<generator class="identity" />
</id>
<!-- property元素定義常規屬性 -->
<property name="title" />
<property name="content" />
</class>
</hibernate-mapping>
PO = POJO + hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 指定Hibernate配置文件的DTD信息 -->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- hibernate-configuration是連接配置文件的根元素 -->
<hibernate-configuration>
<session-factory>
<!-- 指定連接數據庫所用的驅動 -->
<property name="contention.driver_class">com.mysql.jdbc.Driver</property>
<!-- 指定連接數據庫的url,hibernate連接的數據庫名 -->
<property name="connection.url">jdbc:mysql://192.168.56.111:3306/hibernateexercise</property>
<!-- 指定連接數據庫的用戶名 -->
<property name="connection.username">root</property>
<!-- 指定連接數據庫的密碼 -->
<property name="connection.password">123321</property>
<!-- 指定連接池的最大連接數 -->
<property name="hibernate.c3p0.max_size">20</property>
<!-- 指定連接池的最小連接數 -->
<property name="hibernate.c3p0.max_size">1</property>
<!-- 指定連接池的超時時間 -->
<property name="hibernate.c3p0.timeout">5000</property>
<!-- 指定連接池的最大緩存多少個Statement對象 -->
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<property name="hibernate.c3p0.validate">true</property>
<!-- 指定數據庫方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- 根據需要自動創建數據庫表 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 顯示SQL -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化良好地顯示SQL -->
<property name="hibernate.format_sql">true</property>
<!-- 在SQL中添加有助於調試的註釋 -->
<property name="hibernate.use_sql_comments">false</property>
<!-- 羅列所有的映射文件 -->
<mapping resource="basicusage/chapter1/News.hbm.xml" />
</session-factory>
</hibernate-configuration>
持久化操作代碼:
public class NewsManager
{
public static void main(String[] args) throws Exception
{
Configuration conf = new Configuration()
// 下面的方法默認加載hibernate.cfg.xml
.configure();
SessionFactory sf = conf.buildSessionFactory();
Session sess = sf.openSession();
// 開始事務
Transaction tx = sess.beginTransaction();
News n = new News();
n.setTitle("create title");
n.setContent("create content");
sess.save(n);
// 提交事務
tx.commit();
sess.close();
sf.close();
}
}
hibernate持久化通常操組步驟:
1、開發持久化類,由POJO加映射文件組成。
2、獲取Configuration。
3、獲取SessionFactory。
4、獲取Session,打開事務。
5、用面向對象的方式操作數據庫。
6、關閉事務,關閉Session。
對Hibernate的數據訪問進行優化時,需要了解Hibernate底層SQL操作,這是因爲數據訪問對絕大部分應用來說,是一個巨大、耗時的操作。
==============================使用Eclipse開發Hibernate應用==============================
Eclipse.hibernate工具http://www.jboss.org/tools/download/stable
該工具可以通過數據庫現存表的結構反向生成對應的實體類。
==============================Hibernate的體系和核心API==============================
現在我們知道了一個概念Hibernate Session,只有處於Session管理下的POJO才具有持久化操作能力。當應用程序對於處於Session管理下的POJO實例執行操作時,Hibernate將這種面向對象的操作轉換成了持久化操作能力。
通過上圖能夠發現HIbernate需要一個hibernate.properties文件,該文件用於配置Hibernate和數據庫連接的信息。還需要一個XML文件,該映射文件確定了持久化類和數據表、數據列之間的想對應關係。
除了使用hibernate.properties文件,還可以採用另一種形式的配置文件: *.cfg.xml文件。在實際應用中,採用XML配置文件的方式更加廣泛,兩種配置文件的實質是一樣的。
Hibernate的持久化解決方案將用戶從赤裸裸的JDBC訪問中釋放出來,用戶無需關注底層的JDBC操作,而是以面向對象的方式進行持久層操作。底層數據連接的獲得、數據訪問的實現、事務控制都無需用戶關心。這是一種“全面解決”的體系結構方案,將應用層從底層的JDBC/JTA API中抽象出來。通過配置文件來管理底層的JDBC連接,讓Hibernate解決持久化訪問的實現。
(1)SessionFactory:這是Hibernate的關鍵對象,它是單個數據庫映射關係經過編譯後的內存鏡像,它也是線程安全的。它是生成Session的工廠,本身要應用到ConnectionProvider,該對象可以在進程和集羣的級別上,爲那些事務之間可以重用的數據提供可選的二級緩存。
(2)Session:它是應用程序和持久存儲層之間交互操作的一個單線程對象。它也是Hibernate持久化操作的關鍵對象,所有的持久化對象必須在Session的管理下才能夠進行持久化操作。此對象的生存週期很短,其隱藏了JDBC連接,也是Transaction 的工廠。Session對象有一個一級緩存,現實執行Flush之前,所有的持久化操作的數據都在緩存中Session對象處。
(3)持久化對象:系統創建的POJO實例一旦與特定Session關聯,並對應數據表的指定記錄,那該對象就處於持久化狀態,這一系列的對象都被稱爲持久化對象。程序中對持久化對象的修改,都將自動轉換爲持久層的修改。持久化對象完全可以是普通的Java Beans/POJO,唯一的特殊性是它們正與Session關聯着。
(4)瞬態對象和脫管對象:系統進行new關鍵字進行創建的Java 實例,沒有Session 相關聯,此時處於瞬態。瞬態實例可能是在被應用程序實例化後,尚未進行持久化的對象。如果一個曾今持久化過的實例,但因爲Session的關閉而轉換爲脫管狀態。
(5)事務(Transaction):代表一次原子操作,它具有數據庫事務的概念。但它通過抽象,將應用程序從底層的具體的JDBC、JTA和CORBA事務中隔離開。在某些情況下,一個Session 之內可能包含多個Transaction對象。雖然事務操作是可選的,但是所有的持久化操作都應該在事務管理下進行,即使是隻讀操作。
(6)連接提供者(ConnectionProvider):它是生成JDBC的連接的工廠,同時具備連接池的作用。他通過抽象將底層的DataSource和DriverManager隔離開。這個對象無需應用程序直接訪問,僅在應用程序需要擴展時使用。
(7)事務工廠(TransactionFactory):他是生成Transaction對象實例的工廠。該對象也無需應用程序的直接訪問。
==============================Hibernate的配置文件==============================
除了通過xml配置外,還可以創建Configuration對象,通過屬性設置的方式配置,不過實際開發中並不使用。
public class NewsManager
{
public static void main(String[] args) throws Exception
{
//實例化Configuration,不加載任何配置文件
Configuration conf = new Configuration()
//通過addClass方法添加持久化類
.addClass(domain.News.class)
//通過setProperty設置Hibernate的連接屬性。
.setProperty("hibernate.connection.driver_class" , "com.mysql.jdbc.Driver")
.setProperty("hibernate.connection.url" , "jdbc:mysql://localhost/hibernate")
.setProperty("hibernate.connection.username" , "root")
.setProperty("hibernate.connection.password" , "32147")
.setProperty("hibernate.c3p0.max_size" , "20")
.setProperty("hibernate.c3p0.min_size" , "1")
.setProperty("hibernate.c3p0.timeout" , "5000")
.setProperty("hibernate.c3p0.max_statements" , "100")
.setProperty("hibernate.c3p0.idle_test_period" , "3000")
.setProperty("hibernate.c3p0.acquire_increment" , "2")
.setProperty("hibernate.c3p0.validate" , "true")
.setProperty("hibernate.dialect" , "org.hibernate.dialect.MySQLInnoDBDialect")
.setProperty("hibernate.hbm2ddl.auto" , "create");
//以Configuration創建SessionFactory
SessionFactory sf = conf.buildSessionFactory();
//實例化Session
Session sess = sf.openSession();
//開始事務
Transaction tx = sess.beginTransaction();
//創建消息實例
News n = new News();
//設置消息標題和消息內容
n.setTitle("it is title");
n.setContent("it is content");
//保存消息
sess.save(n);
//提交事務
tx.commit();
//關閉Session
sess.close();
}
}
Hibernate\hibernate-distribution-3.6.10.Final\project\etc目錄下有配置文件模板
hibernate提供了連接池配置hibernate.connection.pool_size,但自帶的連接池僅有測試價值,實際項目中使用c3p0或proxool。
==============================持久化類的基本要求==============================
基本規則:
1、一個無參構造器,這是爲了讓Hibernate能使用Constructor.newInstance()來創建持久化類的實例。
2、提供一個標識屬性,通常是主鍵字段,或者聯合主鍵。
3、爲持久化類的每個屬性提供setter和getter方法,使之具有JavaBean風格。
4、使用非final的類,如果PO沒有實現任何藉口的話,Hibernate使用CGLIB生成代理,該代理對象是PO子類的實例。如果使用了final類,則無法生成CGLIB代理,將無法進行性能優化。
5、重寫equals()和hashCode()方法。
==============================持久化對象的狀態==============================
Transient(瞬態):對象由new創建,尚未與Session關聯。
Persistent(持久化):持久化實例在DB中有對應的記錄,擁有一個持久化標識(identifier)。
Detached(脫管):某個實例曾處於持久化狀態,但隨後與之關聯的Session被關閉。
改變PO狀態的方法:
save()和persist(),Hibernate之所以提供與save功能幾乎類似的persist方法,一方面是爲了照顧JPA的用法習慣,另一方面,它們之間有一個區別:save會返回PO的標識屬性值,persist則沒有返回值,因爲save會立即將PO對應的數據插入DB,而persist則保證當它在一個事務外部被調用時,並不立即轉換成insert語句,這個功能很有用,尤其當我們封裝一個長會話流程的時候。
load()和get(),這兩方法主要區別在於是否延遲加載,load具有延遲加載功能,它不會立即訪問DB,當試圖加載的記錄不存在時,load可能返回一個未初始化的代理對象;而get總是立即訪問DB,當試圖加載的記錄不存在時,返回null。
update()和updateOrSave(),前者保存應用對PO所做的修改,若不清楚該PO是否被持久化過,則使用後者進行自動判斷。
merge()也可將應用對脫管對象所做的修改保存到DB,區別於update的是,merge不會持久化給定對象,舉例來說:update(a)後,a對象將變爲持久化狀態,但merge(a)後,a對象不會變爲持久化狀態,a對象依然不被關聯到session。
當使用merge保存應用對脫管對象所做的修改時,如果Session中存在相同持久化標識的PO,merge方法裏提供對象的狀態將覆蓋原有PO的狀態,如果Session中沒有相應的PO,則嘗試從DB中加載,或創建型額PO,最後返回該PO。
當使用load或get方法加載PO時,還可指定一個“鎖模式”(LockMode),通過lock()方法。
Notice:Hibernate本身不提供直接執行update或delete語句的API,Hibernate提供的是一種OO的狀態管理。
==============================Hibernate的基本映射==============================
映射文件以.hbm.xml結尾,每個Hibernate映射文件的基本結構都是相同的。
==============================List、Set、Map等級和屬性映射==============================
List:
public class Person
{
//標識屬性
private Integer id;
//普通屬性name
private String name;
//普通屬性age
private int age;
//集合屬性,保留該對象關聯的學校
private List<String> schools = new ArrayList<String>();
//id屬性的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
//name屬性的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
//age屬性的setter和getter方法
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
//schools屬性的setter和getter方法
public void setSchools(List<String> schools)
{
this.schools = schools;
}
public List<String> getSchools()
{
return this.schools;
}
}
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
<class name="Person" table="person_inf">
<!-- 映射標識屬性 -->
<id name="id" column="person_id">
<!-- 指定主鍵生成器策略 -->
<generator class="identity"/>
</id>
<!-- 映射普通屬性 -->
<property name="name" type="string"/>
<property name="age" type="int"/>
<!-- 映射List集合屬性 -->
<list name="schools" table="school">
<!-- 映射集合屬性數據表的外鍵列 -->
<key column="person_id" not-null="true"/>
<!-- 映射集合屬性數據表的集合索引列 -->
<list-index column="list_order"/>
<!-- 映射保存集合元素的數據列 -->
<element type="string" column="school_name"/>
</list>
</class>
</hibernate-mapping>
public class PersonManager
{
public static void main(String[] args)
{
PersonManager mgr = new PersonManager();
mgr.createAndStorePerson();
HibernateUtil.sessionFactory.close();
}
//創建並保存Person對象
private void createAndStorePerson()
{
//打開線程安全的session對象
Session session = HibernateUtil.currentSession();
//打開事務
Transaction tx = session.beginTransaction();
//創建Person對象
Person yeeku = new Person();
//爲Person對象設置屬性
yeeku.setAge(29);
yeeku.setName("crazyit.org");
//創建List集合
List<String> schools = new ArrayList<String>();
schools.add("小學");
schools.add("中學");
//設置List集合屬性
yeeku.setSchools(schools);
session.save(yeeku);
tx.commit();
HibernateUtil.closeSession();
}
}
Array:
public class Person
{
//標識屬性
private Integer id;
//普通屬性name
private String name;
//普通屬性age
private int age;
//數組屬性,保留該對象關聯的學校
private String[] schools;
//id屬性的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
//name屬性的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
//age屬性的setter和getter方法
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
//schools屬性的setter和getter方法
public void setSchools(String[] schools)
{
this.schools = schools;
}
public String[] getSchools()
{
return this.schools;
}
}
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
<class name="Person" table="person_inf">
<!-- 映射標識屬性 -->
<id name="id" column="person_id">
<!-- 指定主鍵生成器策略 -->
<generator class="identity"/>
</id>
<!-- 映射普通屬性 -->
<property name="name" type="string"/>
<property name="age" type="int"/>
<!-- 映射數組屬性 -->
<array name="schools" table="school">
<!-- 映射數組屬性數據表的外鍵列 -->
<key column="person_id" not-null="true"/>
<!-- 映射數組屬性數據表的數組索引列 -->
<list-index column="list_order"/>
<!-- 映射保存數組元素的數據列 -->
<element type="string" column="school_name"/>
</array>
</class>
</hibernate-mapping>
public class PersonManager
{
public static void main(String[] args)
{
PersonManager mgr = new PersonManager();
mgr.createAndStorePerson();
HibernateUtil.sessionFactory.close();
}
//創建並保存Person對象
private void createAndStorePerson()
{
//打開線程安全的session對象
Session session = HibernateUtil.currentSession();
//打開事務
Transaction tx = session.beginTransaction();
//創建Person對象
Person yeeku = new Person();
//爲Person對象設置屬性
yeeku.setAge(29);
yeeku.setName("crazyit.org");
//創建數組對象
String[] schools = new String[]
{"小學" , "中學"};
//設置數組屬性
yeeku.setSchools(schools);
session.save(yeeku);
tx.commit();
HibernateUtil.closeSession();
}
}
Set:
public class Person
{
//標識屬性
private Integer id;
//普通屬性name
private String name;
//普通屬性age
private int age;
//集合屬性,保留該對象關聯的學校
private Set<String> schools =
new HashSet<String>();
//id屬性的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
//name屬性的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
//age屬性的setter和getter方法
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
//schools屬性的setter和getter方法
public void setSchools(Set<String> schools)
{
this.schools = schools;
}
public Set<String> getSchools()
{
return this.schools;
}
}
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
<class name="Person" table="person_inf">
<!-- 映射標識屬性 -->
<id name="id" column="person_id">
<!-- 指定主鍵生成器策略 -->
<generator class="identity"/>
</id>
<!-- 映射普通屬性 -->
<property name="name" type="string"/>
<property name="age" type="int"/>
<!-- 映射Set集合屬性 -->
<set name="schools" table="school">
<!-- 映射集合屬性數據表的外鍵列 -->
<key column="person_id" not-null="true"/>
<!-- 映射保存集合元素的數據列,增加非空約束 -->
<element type="string" column="school_name"
not-null="true"/>
</set>
</class>
</hibernate-mapping>
public class PersonManager
{
public static void main(String[] args)
{
PersonManager mgr = new PersonManager();
mgr.createAndStorePerson();
HibernateUtil.sessionFactory.close();
}
private void createAndStorePerson()
{
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
//創建Person對象
Person yeeku = new Person();
//爲Person對象設置屬性
yeeku.setAge(29);
yeeku.setName("crazyit.org");
//創建Set集合
Set<String> s = new HashSet<String>();
s.add("小學");
s.add("中學");
//設置Set集合屬性
yeeku.setSchools(s);
session.save(yeeku);
tx.commit();
HibernateUtil.closeSession();
}
}
bag:
public class Person
{
//標識屬性
private Integer id;
//普通屬性name
private String name;
//普通屬性age
private int age;
//集合屬性,保留該對象關聯的學校
private Collection<String> schools =
new ArrayList<String>();
//id屬性的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
//name屬性的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
//age屬性的setter和getter方法
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
//schools屬性的setter和getter方法
public void setSchools(Collection<String> schools)
{
this.schools = schools;
}
public Collection<String> getSchools()
{
return this.schools;
}
}
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
<class name="Person" table="person_inf">
<!-- 映射標識屬性 -->
<id name="id" column="person_id">
<!-- 指定主鍵生成器策略 -->
<generator class="identity"/>
</id>
<!-- 映射普通屬性 -->
<property name="name" type="string"/>
<property name="age" type="int"/>
<!-- 使用bag元素映射集合屬性 -->
<bag name="schools" table="school">
<!-- 映射集合屬性數據表的外鍵列 -->
<key column="person_id" not-null="true"/>
<!-- 映射保存集合元素的數據列 -->
<element type="string" column="school_name"
not-null="true"/>
</bag>
</class>
</hibernate-mapping>
public class PersonManager
{
public static void main(String[] args)
{
PersonManager mgr = new PersonManager();
mgr.createAndStorePerson();
HibernateUtil.sessionFactory.close();
}
//創建並保存Person對象
private void createAndStorePerson()
{
//打開線程安全的session對象
Session session = HibernateUtil.currentSession();
//打開事務
Transaction tx = session.beginTransaction();
//創建Person對象
Person yeeku = new Person();
//爲Person對象設置屬性
yeeku.setAge(29);
yeeku.setName("crazyit.org");
//創建List集合
Collection<String> schools =
new ArrayList<String>();
schools.add("小學");
schools.add("中學");
//設置Collection集合屬性
yeeku.setSchools(schools);
session.save(yeeku);
tx.commit();
HibernateUtil.closeSession();
}
}
Map:
public class Person
{
//標識屬性
private Integer id;
//普通屬性name
private String name;
//普通屬性age
private int age;
//集合屬性,保留該對象關聯的考試成績
private Map<String ,Float> scores
= new HashMap<String ,Float>();
//id屬性的setter和getter方法
public void setId(Integer id)
{
this.id = id;
}
public Integer getId()
{
return this.id;
}
//name屬性的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
//age屬性的setter和getter方法
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
//scores屬性的setter和getter方法
public void setScores(Map<String ,Float> scores)
{
this.scores = scores;
}
public Map<String ,Float> getScores()
{
return this.scores;
}
}
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
<class name="Person" table="person_inf">
<!-- 映射標識屬性 -->
<id name="id" column="person_id">
<!-- 指定主鍵生成器策略 -->
<generator class="identity"/>
</id>
<!-- 映射普通屬性 -->
<property name="name" type="string"/>
<property name="age" type="int"/>
<!-- 映射Map集合屬性 -->
<map name="scores" table="score">
<!-- 映射集合屬性數據表的外鍵列 -->
<key column="person_id" not-null="true"/>
<!-- 映射集合屬性數據表的Map key列 -->
<map-key column="subject" type="string"/>
<!-- 映射保存集合元素的數據列 -->
<element column="grade" type="float"/>
</map>
</class>
</hibernate-mapping>
public class PersonManager
{
public static void main(String[] args)
{
PersonManager mgr = new PersonManager();
mgr.createAndStorePerson();
HibernateUtil.sessionFactory.close();
}
private void createAndStorePerson()
{
//打開線程安全的session對象
Session session = HibernateUtil.currentSession();
//打開事務
Transaction tx = session.beginTransaction();
//創建Person對象
Person yeeku = new Person();
//爲Person對象設置屬性
yeeku.setAge(29);
yeeku.setName("crazyit.org");
//創建Map集合
Map<String ,Float> m = new HashMap<String ,Float>();
m.put("語文" , 67f);
m.put("英文" , 45f);
//設置Map集合屬性
yeeku.setScores(m);
session.save(yeeku);
tx.commit();
HibernateUtil.closeSession();
}
}
性能分析:
對於集合屬性,通常推薦使用延遲加載。
集合可分成如下兩類:有序(根據key或index訪問)、無序(只能遍歷元素)。
有序集合都擁有一個由外鍵列和集合元素索引列組成的聯合主鍵,因此,集合屬性的更新或刪除是非常高效的——主鍵已被有效索引。
無序集合的主鍵由<key.../>和其他元素字段構成,或者根本沒有主鍵。DB很難對複雜主鍵進行索引,即使可以建立索引,性能也非常差。
在這種情況下,<bag.../>映射是最差的,因爲它允許有重複的元素值,也沒有索引字段,如果試圖修改這種集合屬性,Hibernate會先刪除全部,再重新創建。
對於多對多關聯、值數據的集合而言,有序集合類比Set多一個好處:因爲Set集合內部結構的原因,所以如果“改變”Set集合的某個元素,Hibernate不會立即update該元素對應的數據行,因此,只有insert或delete時“改變”纔有效。
在Hibernate中,Set應該是最通用的集合類型,這是因爲“Set集合”的語義最貼近關係模型的聯繫關係,因此Hibernate的關聯映射都是採用<set.../>元素(或<bag../>元素)映射的。而且,在設計良好的Hibernate領域中,1-N關聯的1的一端通常帶有inverse="true",對於這種關聯映射,1的一端不再控制關聯關係,所有更新操作將會在N的一端進行處理,對於這種情況,無須考慮其集合的更新性能。
一旦指定inverse="true"屬性,使用<list.../>和<bag.../>映射屬性將有較好的性能,因爲Collection.add()和Collection.addAll()方法總是返回true。而Set集合需要保證元素不能重複,這就會進行插入前比較。
==============================數據庫對象映射==============================
==============================組合屬性映射==============================
==============================集合元素爲複合類型的映射==============================
==============================複合主鍵映射==============================
==============================使用JPA Annotation管理映射信息==============================
早期Hibernate使用XML映射文件管理實體類與表之間的映射關係,而JPA規範則推薦使用更簡單、易用的Annotation來管理實體類與數據表之間的映射關係,這樣就避免了一個實體需要同時維護兩份文件(類與xml配置)。
@Entity
@Table(name="person_table")
public class Person
{
/* 指定使用複合主鍵類是Name */
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name="first"
, column=@Column(name="person_first")),
@AttributeOverride(name="last"
, column=@Column(name="person_last" , length=20))
})
private Name name;
//普通屬性
@Column(name="person_email")
private String email;
@Embedded
@AttributeOverrides({
@AttributeOverride(name="name"
, column=@Column(name="cat_name" , length=35)),
@AttributeOverride(name="color"
, column=@Column(name="cat_color"))
})
//組件屬性,代表此人擁有的寵物
private Cat pet;
//name屬性的setter和getter方法
public void setName(Name name)
{
this.name = name;
}
public Name getName()
{
return this.name;
}
//email屬性的setter和getter方法
public void setEmail(String email)
{
this.email = email;
}
public String getEmail()
{
return this.email;
}
//pet屬性的setter和getter方法
public void setPet(Cat pet)
{
this.pet = pet;
}
public Cat getPet()
{
return this.pet;
}
}
@Entity用於標註該類是一個PO,@EmbeddedId用於標註符合類型的標識屬性,而@Embedded用於標註一個組件屬性,也就是說Person的name屬性就是一個複合類型的標識屬性;pet屬性是一個組件屬性。
//修飾組件屬性類
@Embeddable
public class Name
implements java.io.Serializable
{
private String first;
private String last;
//無參數的構造器
public Name()
{
}
//初始化全部屬性的構造器
public Name(String first , String last)
{
this.first = first;
this.last = last;
}
//first屬性的setter和getter方法
public void setFirst(String first)
{
this.first = first;
}
public String getFirst()
{
return this.first;
}
//last屬性的setter和getter方法
public void setLast(String last)
{
this.last = last;
}
public String getLast()
{
return this.last;
}
//提供重寫的equals方法
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj.getClass() == Name.class)
{
Name target = (Name)obj;
if (target.getFirst().equals(first)
&& target.getLast().equals(last))
{
return true;
}
}
return false;
}
//提供重寫的hashCode方法
public int hashCode()
{
return first.hashCode() + last.hashCode() * 17;
}
}
上面的Name類需要作爲標識屬性的類型,因此一樣需要實現Serializable接口,並重寫hashCode和equals方法。
至於Person類所包含的組件屬性pet,它所屬的Cat類也只要簡單地使用@Embeddable修飾即可。
//修飾組件屬性類
@Embeddable
public class Cat
{
private String name;
private String color;
//無參數的構造器
public Cat()
{
}
//初始化全部屬性的構造器
public Cat(String name , String color)
{
this.name = name;
this.color = color;
}
//name屬性的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
//color屬性的setter和getter方法
public void setColor(String color)
{
this.color = color;
}
public String getColor()
{
return this.color;
}
}
一旦在實體類中通過上面的Annotation進行標註之後,Hibernate已經能夠理解實體類與表之間的映射關係了,也就不再需要.hbm.xml的映射文件了。
<!-- 原有的Person.hbm.xml需作更改 -->
<mapping class="org.crazyit.app.domain.Person"/>
public class PersonManager
{
public static void main(String[] args)
{
PersonManager mgr = new PersonManager();
mgr.createAndStorePerson();
HibernateUtil.sessionFactory.close();
}
private void createAndStorePerson()
{
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
//創建Person對象
Person yeeku = new Person();
yeeku.setName(new Name("crazyit.org", "瘋狂Java聯盟"));
yeeku.setEmail("[email protected]");
yeeku.setPet(new Cat("Garfield", "黃色"));
session.save(yeeku);
tx.commit();
HibernateUtil.closeSession();
}
}
public class HibernateUtil
{
public static final SessionFactory sessionFactory;
static
{
try
{
//採用默認的hibernate.cfg.xml來啓動一個Configuration的實例
Configuration configuration = new Configuration()
.configure();
//由Configuration的實例來創建一個SessionFactory實例
sessionFactory = configuration.buildSessionFactory();
}
catch (Throwable ex)
{
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
//ThreadLocal可以隔離多個線程的數據共享,因此不再需要對線程同步
public static final ThreadLocal<Session> session
= new ThreadLocal<Session>();
public static Session currentSession()
throws HibernateException
{
Session s = session.get();
//如果該線程還沒有Session,則創建一個新的Session
if (s == null)
{
s = sessionFactory.openSession();
//將獲得的Session變量存儲在ThreadLocal變量session裏
session.set(s);
}
return s;
}
public static void closeSession()
throws HibernateException
{
Session s = session.get();
if (s != null)
s.close();
session.set(null);
}
}