JavaEE學習筆記——2、Hibernate基礎

==============================ORM的基本知識==============================

目前主流數據庫是關係型數據庫(RDB),而Java則是面向對象的編程語言(OOPL),要結合兩者相當麻煩。

ORM是爲了解決這一問題而產生的概念。


OOPL的優勢:

面向對象的建模、操作

多態、繼承

摒棄難以理解的過程

建安易用,易理解


RDB的優勢:

大量數據查找、排序

集合數據連接操作、映射

數據庫訪問的併發、事務

數據庫的約束、隔離


ORM,Object/Relation Mapping可理解爲一種規範,它概述了這類框架的特徵:完成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);
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章