Java的Hibernate框架中的基本映射用法講解
Hibernate進行了分類整合發(fā)現(xiàn)其實Hibernate分為三大部分:核心對象、映射、HQL,這三大部分開發(fā)過程中最常使用,前幾篇討論了核心對象及對象之間的轉(zhuǎn)換方法,接下來討論Hibernate的映射使用方法。
Hibernate一個重要的功能就是映射,它能夠在對象模型和關系模型之間轉(zhuǎn)換,是面向?qū)ο缶幊趟枷胩岢褂玫?,使用映射程序開發(fā)人員只需要關心對象模型中代碼的編寫。對象和關系數(shù)據(jù)庫之間的映射通常是由XML文檔來定義的。這個映射文檔被設計為易讀的,并且可以手動修改。這種映射關系我總結為下圖:

映射是通過XML來定義的,使用Hibernate創(chuàng)建的session來管理,最后由session使用JTA把更改提交到數(shù)據(jù)庫中。session可以理解為持久化管理器,管理持久層中的對象,它是由sessionFactory創(chuàng)建的。使用Hibernate編程時首先要連接數(shù)據(jù)庫,所以首先要去查看xml中有關數(shù)據(jù)庫連接的配置,根據(jù)文檔的配置創(chuàng)建sessionFactory(可以理解為數(shù)據(jù)庫鏡像),再由sessionFactory創(chuàng)建一個session,最后由session統(tǒng)一將更改提交到數(shù)據(jù)庫,這才完成了一部所有的操作。
使用過程
1.創(chuàng)建映射文件,映射文件以.hbm.xml為后綴名,標明它是Hibernate的映射文件;
2.在映射文件中注冊實體類,并將實體類的屬性添加到映射類中,在添加屬性時要指定兩種值分別是id和property,id指明它是一個實體的唯一標示,property指明它是表的字段列;
3.提交修改,同步數(shù)據(jù)。
Note:開發(fā)過xml數(shù)據(jù)轉(zhuǎn)到數(shù)據(jù)庫的開發(fā)人員很快就能理解這種映射其實是一種批量更新、批量創(chuàng)建的過程,映射也不例外,Hibernate規(guī)定了一套映射標準,按照標準就能夠?qū)崿F(xiàn)轉(zhuǎn)換,它的內(nèi)部實現(xiàn)還是死的,所以它只是相對的靈活易用。
一個簡單的實體類映射過程:
1. 實體類User1的屬性代碼:
package com.hibernate;
import java.util.Date;
public class User1 {
private String id;
private String name;
private String password;
private Date createTime;
private Date expireTime;
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getExpireTime() {
return expireTime;
}
public void setExpireTime(Date expireTime) {
this.expireTime = expireTime;
}
}
2. User1.java的映射文件User1.hbm.xml的內(nèi)部代碼實現(xiàn):
基礎數(shù)據(jù)庫中能夠設置的在Hibernate中同樣提供了設置的方法,可以使用標簽屬性來設置具體的映射關系。
類-->表使用class標簽,常用屬性:
(1)name:映射實體類,它的值需要設置為需要轉(zhuǎn)化成表的實體類的名稱,在同步時會根據(jù)該屬性查找相應的實體類。
(2)table:映射數(shù)據(jù)庫表的名稱,如果要映射的表的名稱和實體類名稱不相同,使用該屬性來指定映射的表,如果不存在的話會根據(jù)該屬性值創(chuàng)建一個表。
查看上圖中配置生成的表結構,如下圖:

其中的表名更改為了t_user1;id字段更名為user_id,字段長度為32位;createTime屬性,映射為數(shù)據(jù)庫字段create_time,并修改為date類型。
屬性-->字段使用id或property標簽,常用屬性:
(1)name:功能類似于class標簽的name,值定實體類的映射屬性名;
(2)column:類似于實體類class標簽的table,指定映射表的列名稱,如果不存在將會創(chuàng)建;
(3)type:指定映射到數(shù)據(jù)庫中字段的數(shù)據(jù)類型,根據(jù)需要查看文檔即可;
(4)generator,它是可選的,用來為一個持久類生成唯一的標識。
<id name="id" type="long" column="cat_id">
<generator class="org.hibernate.id.TableHiLoGenerator">
<param name="table">uid_table</param>
<param name="column">next_hi_value_column</param>
</generator>
</id>
所有的生成器都實現(xiàn)org.hibernate.id.IdentifierGenerator接口。 這是一個非常簡單的接口;某些應用程序可以選擇提供他們自己特定的實現(xiàn)。當然, Hibernate提供了很多內(nèi)置的實現(xiàn)。下面介紹常用的幾種:
(1)identity:返回的標識符是long, short 或者int類型的。類似于數(shù)據(jù)庫的自增字段。
(2)sequence:在DB2,PostgreSQL, Oracle, SAP DB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的標識符是long, short或者 int類型的。在整個數(shù)據(jù)庫中自增,而不是單個的表中自增,需要指定單個的表中自增需要添加屬性。
(3)uuid:用一個128-bit的UUID算法生成字符串類型的標識符, 這在一個網(wǎng)絡中是唯一的(使用了IP地址)。UUID被編碼為一個32位16進制數(shù)字的字符串。類似于.NET生成的序列號。
(4)native:根據(jù)底層數(shù)據(jù)庫的能力選擇identity, sequence 或者hilo中的一個。靈活的方式,會根據(jù)使用的數(shù)據(jù)庫來確定使用的標識類型,MySQL會選擇identity,Oracle選擇sequence。
(5)assigned:手動為實體類制定一個標識id。這是 <generator>元素沒有指定時的默認生成策略。
(6)foreign:使用另外一個相關聯(lián)的對象的標識符。通常和<one-to-one>聯(lián)合起來使用。
開發(fā)人員往往習慣了手動配置的方法根據(jù)文檔的說明來編寫配置屬性,這樣做很原始,初學者建議采用手動配置的方法,有助于思考。另外還有很多第三方工具,通過可視化的方法來配置然后生成xml配置文檔,提高了開發(fā)效率,類似的工具如:XDoclet、Middlegen和AndorMDA。
關聯(lián)映射之多對一
上面討論了Hibernate的基本映射,一個實體類對應著一張表,在相應的Hibernate Mapping文件中使用<class>標簽映射。并且實體類中的普通屬性對應著表字段,使用<property>標簽映射。另外在構造實體類時應注意:在實體類中應實現(xiàn)無參的默認的構造函數(shù),提供一個標示,建議不要使用final修飾實體類,為實體類生成getter和setter方法,最后介紹了幾種主要的主鍵生成策略,接下來討論多對一映射。

這種多對一關聯(lián)映射反應到對象模型中它是一種聚合關系,User是group的一部分,group中存在User,它們兩個的生命周期是不相同的,可反應為下圖:

那么這種多對一關系映射在Hibernate中是如何設置的呢?下面將會介紹兩種方法,使用<many-to-one>標簽直接映射,或者使用<many-to-one>的cascade級聯(lián)修改表。
1、Many-to-one直接映射
從字面意思上就能夠理解,它是指多對一的關系,many指的是多的一端,one指的是少的一端,在使用時往往在多的一端的hbm中使用該標簽,并將<many-to-one>的name屬性設置為該映射文件對應的類中的one一端的屬性,如:<many-to-one name="group" column="groupid"></many-to-one>,該標簽添加在了User.hbm.xml中,它對應many;標簽中的name值為group對映射one,并且在User.java中會有一個名為group的屬性。接下來看下具體實現(xiàn)實現(xiàn)的代碼類。
(1)User.java類代碼,其中有一個名為group的屬性,它將會作為<many-to-one>的one一端的name值。
public class User {
private String name;
public String GetName(){
return name;
}
public void SetName(String name){
this.name=name;
}
private Group group;
public Group GetGroup(){
return group;
}
public void SetGroup(Group group){
this.group=group;
}
}
(2)User.hbm.xml中的<many-to-one>,name的值為User.java中one端的屬性值,它會在數(shù)據(jù)庫中生成一個新列,可以將該新列理解為User表的外鍵。
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2014-5-14 23:39:25 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping> <class name="com.hibernate.User" table="USER"> <id name="id" type="java.lang.Long"> <column name="ID" /> <generator class="assigned" /> </id> <!-- name的值group為User.java中的一個對應的one中的一個屬性,它會自動在表中生成一列,所以使用column對列進行了重命名 --> <many-to-one name="group" column="groupid"></many-to-one> </class> </hibernate-mapping>
(3)測試上面的映射關系,向表中寫入兩個User對象分別為user1和user2,命名為張三和李四,使用session保存對象,向數(shù)據(jù)庫中寫入數(shù)據(jù),代碼如下:
public void testSave1(){
Session session=null;
try{
session=GetSession.getSession();
session.beginTransaction();
Group group=new Group();
group.SetName("動力節(jié)點");
User user1=new User();
user1.SetName("張三");
user1.SetGroup(group);
User user2=new User();
user2.SetName("李四");
user2.SetGroup(group);
session.save(user1);
session.save(user2);
//會報TransientObjectException錯誤
//在清理緩存時發(fā)生錯誤TransientObjectException
//因為Group為Transient狀態(tài),沒有被Session,在數(shù)據(jù)庫中沒有匹配的數(shù)據(jù)
//而User為Persistent狀態(tài),在清理緩存時Hibernate在緩存中無法找到Group對象
//揭露:Persistent狀態(tài)的對象不能引用Transient狀態(tài)的對象
//該問題在testSave2方法中更改
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
GetSession.CloseSession(session);
}
}
但是使用上面的代碼在執(zhí)行寫入時會報錯TransientObjectException,這是因為在保存User對象時它會按照<many-to-one>中添加的group去內(nèi)存中查找group對象,但是上面的代碼中group對象一直都是在Transient狀態(tài)中,并沒有被session管理,也就是說查找不到session對象,而User對象進入了Persistent狀態(tài),于是會報此錯誤。正確的代碼如下:
public void testSave2(){
Session session=null;
try{
session=GetSession.getSession();
session.beginTransaction();
Group group=new Group();
group.SetName("動力節(jié)點");
session.save(group); //此處將group對象設置為Persistent對象
User user1=new User();
user1.SetName("張三");
user1.SetGroup(group);
User user2=new User();
user2.SetName("李四");
user2.SetGroup(group);
session.save(user1);
session.save(user2);
//可以正確的保存數(shù)據(jù)
//因為Group和User都是Persistent狀態(tài)的對象
//所以在Hibernate清理緩存時在session中可以找到關聯(lián)對象
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
GetSession.CloseSession(session);
}
}
2、級聯(lián)映射
除了上面所說的將group對象和user對象都轉(zhuǎn)化到Persistent對象外,還可以使用cascade級聯(lián)映射屬性,在<many-to-one>屬性中添加cascade屬性,并復制為save-update,在group對象并非為Persistent狀態(tài)時即可寫入數(shù)據(jù)庫。這樣只需要將兩個user對象的Group屬性設置為同一個group對象即可實現(xiàn)多對一的映射關系,此時User.hbm.xml中對應的內(nèi)容為如下代碼:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2014-5-14 23:39:25 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping> <class name="com.hibernate.User" table="USER"> <id name="id" type="java.lang.Long"> <column name="ID" /> <generator class="assigned" /> </id> <!-- 級聯(lián)修改表 --> <many-to-one name="group" column="groupid" cascade="save-update"></many-to-one> </class> </hibernate-mapping>
Note:cascade設置為save-update后即可實現(xiàn)向數(shù)據(jù)庫中級聯(lián)修改、添加和刪除,但是具體的級聯(lián)查詢操作卻不可以。
對應的測試配置文件的方法為如下代碼:
//級聯(lián)cascade
public void testSave3(){
Session session=null;
try{
session=GetSession.getSession();
session.beginTransaction();
Group group=new Group();
group.SetName("動力節(jié)點");
User user1=new User();
user1.SetName("張三");
user1.SetGroup(group);
User user2=new User();
user2.SetName("李四");
user2.SetGroup(group);
session.save(user1);
session.save(user2);
//沒有拋出TransientObjectException異常
//因為使用了級聯(lián)
//Hibernate會首先保存User的關聯(lián)對象Group
//Group和User就都是Persistent狀態(tài)的對象了
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
GetSession.CloseSession(session);
}
}
3、對比升華
兩種方法同樣實現(xiàn)了多對一的映射方法,結果上是相同的,但在實現(xiàn)上很不相同。無論是第一種還是第二種采用的都是<many-to-one>在many一端的映射文件中添加該標簽,并將標簽的name屬性賦值為該映射文件注冊的類中的one一端的屬性值,這樣就完成了多對一的基本映射,這是相同點。不同點是直接映射關系沒有采用Hibernate字段的屬性,這樣在實現(xiàn)上較靈活,不但支持增刪改,而且可以查詢;第二種的cascade級聯(lián)修改則采用了Hibernate提供的方法,此種方法只支持增刪改,并不支持查詢。
相關文章
一文掌握Spring?中?@Component?和?@Bean?區(qū)別(最新推薦)
?@Component?用于標識一個普通的類,@Bean用于配置類里面,在方法上面聲明和配置?Bean?對象,這篇文章主要介紹了Spring?中?@Component?和?@Bean?區(qū)別(最新推薦),需要的朋友可以參考下2024-04-04
springboot的控制反轉(zhuǎn)和自動裝配示例代碼
這篇文章主要介紹了springboot的控制反轉(zhuǎn)和自動裝配的相關知識,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-06-06
Java多線程CountDownLatch的實現(xiàn)
本文主要介紹了Java多線程CountDownLatch的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02
SpringBoot使用Caffeine實現(xiàn)緩存的示例代碼
本文主要介紹了SpringBoot使用Caffeine實現(xiàn)緩存的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07

