Java的Hibernate框架中的基本映射用法講解
Hibernate進行了分類整合發(fā)現(xiàn)其實Hibernate分為三大部分:核心對象、映射、HQL,這三大部分開發(fā)過程中最常使用,前幾篇討論了核心對象及對象之間的轉(zhuǎn)換方法,接下來討論Hibernate的映射使用方法。
Hibernate一個重要的功能就是映射,它能夠在對象模型和關(guān)系模型之間轉(zhuǎn)換,是面向?qū)ο缶幊趟枷胩岢褂玫?,使用映射程序開發(fā)人員只需要關(guān)心對象模型中代碼的編寫。對象和關(guān)系數(shù)據(jù)庫之間的映射通常是由XML文檔來定義的。這個映射文檔被設(shè)計為易讀的,并且可以手動修改。這種映射關(guān)系我總結(jié)為下圖:
映射是通過XML來定義的,使用Hibernate創(chuàng)建的session來管理,最后由session使用JTA把更改提交到數(shù)據(jù)庫中。session可以理解為持久化管理器,管理持久層中的對象,它是由sessionFactory創(chuàng)建的。使用Hibernate編程時首先要連接數(shù)據(jù)庫,所以首先要去查看xml中有關(guān)數(shù)據(jù)庫連接的配置,根據(jù)文檔的配置創(chuàng)建sessionFactory(可以理解為數(shù)據(jù)庫鏡像),再由sessionFactory創(chuàng)建一個session,最后由session統(tǒng)一將更改提交到數(shù)據(jù)庫,這才完成了一部所有的操作。
使用過程
1.創(chuàng)建映射文件,映射文件以.hbm.xml為后綴名,標(biāo)明它是Hibernate的映射文件;
2.在映射文件中注冊實體類,并將實體類的屬性添加到映射類中,在添加屬性時要指定兩種值分別是id和property,id指明它是一個實體的唯一標(biāo)示,property指明它是表的字段列;
3.提交修改,同步數(shù)據(jù)。
Note:開發(fā)過xml數(shù)據(jù)轉(zhuǎn)到數(shù)據(jù)庫的開發(fā)人員很快就能理解這種映射其實是一種批量更新、批量創(chuàng)建的過程,映射也不例外,Hibernate規(guī)定了一套映射標(biāo)準(zhǔn),按照標(biāo)準(zhǔn)就能夠?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):
基礎(chǔ)數(shù)據(jù)庫中能夠設(shè)置的在Hibernate中同樣提供了設(shè)置的方法,可以使用標(biāo)簽屬性來設(shè)置具體的映射關(guān)系。
類-->表使用class標(biāo)簽,常用屬性:
(1)name:映射實體類,它的值需要設(shè)置為需要轉(zhuǎn)化成表的實體類的名稱,在同步時會根據(jù)該屬性查找相應(yīng)的實體類。
(2)table:映射數(shù)據(jù)庫表的名稱,如果要映射的表的名稱和實體類名稱不相同,使用該屬性來指定映射的表,如果不存在的話會根據(jù)該屬性值創(chuàng)建一個表。
查看上圖中配置生成的表結(jié)構(gòu),如下圖:
其中的表名更改為了t_user1;id字段更名為user_id,字段長度為32位;createTime屬性,映射為數(shù)據(jù)庫字段create_time,并修改為date類型。
屬性-->字段使用id或property標(biāo)簽,常用屬性:
(1)name:功能類似于class標(biāo)簽的name,值定實體類的映射屬性名;
(2)column:類似于實體類class標(biāo)簽的table,指定映射表的列名稱,如果不存在將會創(chuàng)建;
(3)type:指定映射到數(shù)據(jù)庫中字段的數(shù)據(jù)類型,根據(jù)需要查看文檔即可;
(4)generator,它是可選的,用來為一個持久類生成唯一的標(biāo)識。
<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接口。 這是一個非常簡單的接口;某些應(yīng)用程序可以選擇提供他們自己特定的實現(xiàn)。當(dāng)然, Hibernate提供了很多內(nèi)置的實現(xiàn)。下面介紹常用的幾種:
(1)identity:返回的標(biāo)識符是long, short 或者int類型的。類似于數(shù)據(jù)庫的自增字段。
(2)sequence:在DB2,PostgreSQL, Oracle, SAP DB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的標(biāo)識符是long, short或者 int類型的。在整個數(shù)據(jù)庫中自增,而不是單個的表中自增,需要指定單個的表中自增需要添加屬性。
(3)uuid:用一個128-bit的UUID算法生成字符串類型的標(biāo)識符, 這在一個網(wǎng)絡(luò)中是唯一的(使用了IP地址)。UUID被編碼為一個32位16進制數(shù)字的字符串。類似于.NET生成的序列號。
(4)native:根據(jù)底層數(shù)據(jù)庫的能力選擇identity, sequence 或者hilo中的一個。靈活的方式,會根據(jù)使用的數(shù)據(jù)庫來確定使用的標(biāo)識類型,MySQL會選擇identity,Oracle選擇sequence。
(5)assigned:手動為實體類制定一個標(biāo)識id。這是 <generator>元素沒有指定時的默認(rèn)生成策略。
(6)foreign:使用另外一個相關(guān)聯(lián)的對象的標(biāo)識符。通常和<one-to-one>聯(lián)合起來使用。
開發(fā)人員往往習(xí)慣了手動配置的方法根據(jù)文檔的說明來編寫配置屬性,這樣做很原始,初學(xué)者建議采用手動配置的方法,有助于思考。另外還有很多第三方工具,通過可視化的方法來配置然后生成xml配置文檔,提高了開發(fā)效率,類似的工具如:XDoclet、Middlegen和AndorMDA。
關(guān)聯(lián)映射之多對一
上面討論了Hibernate的基本映射,一個實體類對應(yīng)著一張表,在相應(yīng)的Hibernate Mapping文件中使用<class>標(biāo)簽映射。并且實體類中的普通屬性對應(yīng)著表字段,使用<property>標(biāo)簽映射。另外在構(gòu)造實體類時應(yīng)注意:在實體類中應(yīng)實現(xiàn)無參的默認(rèn)的構(gòu)造函數(shù),提供一個標(biāo)示,建議不要使用final修飾實體類,為實體類生成getter和setter方法,最后介紹了幾種主要的主鍵生成策略,接下來討論多對一映射。
這種多對一關(guān)聯(lián)映射反應(yīng)到對象模型中它是一種聚合關(guān)系,User是group的一部分,group中存在User,它們兩個的生命周期是不相同的,可反應(yīng)為下圖:
那么這種多對一關(guān)系映射在Hibernate中是如何設(shè)置的呢?下面將會介紹兩種方法,使用<many-to-one>標(biāo)簽直接映射,或者使用<many-to-one>的cascade級聯(lián)修改表。
1、Many-to-one直接映射
從字面意思上就能夠理解,它是指多對一的關(guān)系,many指的是多的一端,one指的是少的一端,在使用時往往在多的一端的hbm中使用該標(biāo)簽,并將<many-to-one>的name屬性設(shè)置為該映射文件對應(yīng)的類中的one一端的屬性,如:<many-to-one name="group" column="groupid"></many-to-one>,該標(biāo)簽添加在了User.hbm.xml中,它對應(yīng)many;標(biāo)簽中的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中的一個對應(yīng)的one中的一個屬性,它會自動在表中生成一列,所以使用column對列進行了重命名 --> <many-to-one name="group" column="groupid"></many-to-one> </class> </hibernate-mapping>
(3)測試上面的映射關(guān)系,向表中寫入兩個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對象,但是上面的代碼中g(shù)roup對象一直都是在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對象設(shè)置為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中可以找到關(guān)聯(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屬性,并復(fù)制為save-update,在group對象并非為Persistent狀態(tài)時即可寫入數(shù)據(jù)庫。這樣只需要將兩個user對象的Group屬性設(shè)置為同一個group對象即可實現(xiàn)多對一的映射關(guān)系,此時User.hbm.xml中對應(yīng)的內(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設(shè)置為save-update后即可實現(xiàn)向數(shù)據(jù)庫中級聯(lián)修改、添加和刪除,但是具體的級聯(lián)查詢操作卻不可以。
對應(yīng)的測試配置文件的方法為如下代碼:
//級聯(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的關(guān)聯(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)了多對一的映射方法,結(jié)果上是相同的,但在實現(xiàn)上很不相同。無論是第一種還是第二種采用的都是<many-to-one>在many一端的映射文件中添加該標(biāo)簽,并將標(biāo)簽的name屬性賦值為該映射文件注冊的類中的one一端的屬性值,這樣就完成了多對一的基本映射,這是相同點。不同點是直接映射關(guān)系沒有采用Hibernate字段的屬性,這樣在實現(xiàn)上較靈活,不但支持增刪改,而且可以查詢;第二種的cascade級聯(lián)修改則采用了Hibernate提供的方法,此種方法只支持增刪改,并不支持查詢。
相關(guān)文章
舉例講解Java中synchronized關(guān)鍵字的用法
這篇文章主要介紹了Java中synchronized關(guān)鍵字的用法,針對synchronized修飾方法的使用作出了簡單講解和演示,需要的朋友可以參考下2016-04-04Java實現(xiàn)圖片與二進制的互相轉(zhuǎn)換
這篇文章主要為大家詳細介紹了Java實現(xiàn)圖片與二進制的互相轉(zhuǎn)換,將圖片轉(zhuǎn)二進制再將二進制轉(zhuǎn)成圖片,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03基于SpringBoot + Redis實現(xiàn)密碼暴力破解防護
在現(xiàn)代應(yīng)用程序中,保護用戶密碼的安全性是至關(guān)重要的,密碼暴力破解是指通過嘗試多個密碼組合來非法獲取用戶賬戶的密碼,為了保護用戶密碼不被暴力破解,我們可以使用Spring Boot和Redis來實現(xiàn)一些防護措施,本文將介紹如何利用這些技術(shù)來防止密碼暴力破解攻擊2023-06-06Spring Boot整合mybatis使用注解實現(xiàn)動態(tài)Sql、參數(shù)傳遞等常用操作(實現(xiàn)方法)
這篇文章主要介紹了Spring Boot整合mybatis使用注解實現(xiàn)動態(tài)Sql、參數(shù)傳遞等常用操作(實現(xiàn)方法),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08java批量導(dǎo)入Excel數(shù)據(jù)超詳細實例
這篇文章主要給大家介紹了關(guān)于java批量導(dǎo)入Excel數(shù)據(jù)的相關(guān)資料,EXCEL導(dǎo)入就是文件導(dǎo)入,操作代碼是一樣的,文中給出了詳細的代碼示例,需要的朋友可以參考下2023-08-08