深入解析Java的Hibernate框架中的持久對象
一、持久對象生命周期
應用程序在使用Hibernate框架后,創(chuàng)建的持久對象會經(jīng)歷一整套生命周期來完成數(shù)據(jù)庫的操作,其中主要的三個狀態(tài)分別是瞬態(tài)(Transient)、持久化(Persistent)、脫管(detached)。這三種狀態(tài)的轉(zhuǎn)換是能夠在應用程序中控制的,如下圖:

為了能清楚的了解這幾種狀態(tài),這里使用一個實例來查看下這幾種狀態(tài)下對象的不同,下面狀態(tài)內(nèi)的代碼,具體步驟如下:
(1)創(chuàng)建Hibernate_session程序集,并添加像相應的jar包;
(2)配置Hibernate,添加相應的實體User類,及它的映射文件,并配置好相應的數(shù)據(jù)庫連接;
User類文件的映射文件User.hbm.xml代碼:
<?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-4-30 15:39:33 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping> <class name="com.hibernate.User"> <id name="id"> <generator class="uuid"/> </id> <property name="name"/> <property name="password"/> <property name="createTime"/> <property name="expireTime"/> </class> </hibernate-mapping>
Hibernate數(shù)據(jù)庫連接配置代碼:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_session</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">ab12</property> <!-- dialect:方言,封裝的底層API,類似于Runtime,將數(shù)據(jù)庫轉(zhuǎn)換為配置中的相應的語言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <mapping resource="com/hibernate/User.hbm.xml"/> </session-factory> </hibernate-configuration>
(3)添加靜態(tài)成員sessionfactory的公共類,用來創(chuàng)建一個SessionFactory及其Session對象;
package com.hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class session {
private static SessionFactory factory; //聲明靜態(tài)局部變量SessionFactory,數(shù)據(jù)庫鏡像
static{
try{
//創(chuàng)建并獲取配置數(shù)據(jù)庫的配置文件,默認獲取hibernate.cfg.xml
Configuration cfg=new Configuration().configure();
factory=cfg.buildSessionFactory(); //構(gòu)建一個數(shù)據(jù)庫鏡像
}catch(Exception e){
e.printStackTrace(); //打印錯誤信息
}
}
public static Session getSession(){
return factory.openSession(); //返回創(chuàng)建的session對象
}
public static SessionFactory getSessionFactory(){
return factory; //返回相應的SessionFactory
}
//關閉session對象
public static void closeSession(Session session){
if(session != null){
if(session.isOpen()){
session.close();
}
}
}
}
(4)添加一個Source Folder,并在該文件夾內(nèi)添加名稱為com.hibernate的package包,并在包中添加一個名稱為SessionTest的類文件。
package com.hibernate;
import java.util.Date;
import junit.framework.TestCase;
import org.hibernate.Session;
import org.hibernate.Transaction;
public class SessionTest extends TestCase {
}
二、狀態(tài)轉(zhuǎn)化方法
1、對象直接進入Persistent狀態(tài)
1.1 get方法
從數(shù)據(jù)庫中獲取一行信息,并將該信息同步到創(chuàng)建的對象中,該方法返回一個Object對象,如果沒有查詢到內(nèi)容則返回null。下面的實例通過采用Session的get方法來獲取一個對象,并將對象轉(zhuǎn)換為實例。
public void testGet1(){
Session session=null;
Transaction tx = null;
try{
session=HibernateUtils.getSession();
//開啟事務
tx= session.beginTransaction();
//get加載上來的對象為持久對象
//執(zhí)行get會馬上發(fā)出查詢語句,如果不存在會返回null
User user=(User)session.get(User.class,"ff80808145bc28cc0145bc28ce020002");
System.out.println(user.getName());
//persistent狀態(tài)
//persistent狀態(tài)的對象,當對象的屬性發(fā)生改變的時候
//Hibernate在清理緩存(臟數(shù)據(jù)檢查)的時候,會和數(shù)據(jù)庫同步
user.setName("趙柳");
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
if(tx != null){
tx.rollback();
}
}finally{
HibernateUtils.closeSession(session);
}
}
設置斷點,獲取User對象。

獲取到了該對象,通過強制轉(zhuǎn)換后得到了一個user對象。程序中添加了setName方法,也就是說會更新數(shù)據(jù)庫中的名稱,執(zhí)行完成后檢查數(shù)據(jù)庫,如下圖更新結(jié)果。

1.2 load方法
功能類似于get方法,也是從數(shù)據(jù)庫中獲取數(shù)據(jù)并同步到對象中,該方法支持lazy是一種懶漢操作,它返回的是一個持久化的Object對象或者一個代理,所以需要進行轉(zhuǎn)化。
public void testLoad1(){
Session session=null;
try{
session=HibernateUtils.getSession();
//不會馬上查詢語句,因為load支持lazy(延遲加載/懶加載)
//什么教lazy?只有真正使用這個對象的時候,再創(chuàng)建,對于Hibernate來說
//才真正發(fā)出查詢語句,主要為了提高性能,lazy是Hibernate中非常重要的特性
//Hibernate的lazy是如何實現(xiàn)的?采用代理對象實現(xiàn),代理對象主要采用的是CGLIB庫生成的
//而不是JDK的動態(tài)代理,因為JDK的動態(tài)代理只能對實現(xiàn)了借口的類生成代理,CGLIB可以對類生成
//代理,它采用的是繼承方式
User user=(User)session.load(User.class,"8a1b653745bcc7b50145bcc7b7140001");
System.out.println(user.getName());
//persistent狀態(tài)
//persistent狀態(tài)的對象,當對象的屬性發(fā)生改變的時候
//Hibernate在清理緩存(臟數(shù)據(jù)檢查)的時候,會和數(shù)據(jù)庫同步
user.setName("zhaoliu");
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
}finally{
HibernateUtils.closeSession(session);
}
}
查詢獲取該User對象如下圖:

分析上圖,獲取的User對象并不完整,或者說并沒有常見一個User對象,更是一種代理,它使用了CGLIB來預加載對象,只有在使用該對象時才真正創(chuàng)建。
1.3 Get Vs load
get和load方法很重要,在面試Hibernate時經(jīng)常會考到,下面對比下兩者。
相同點:
(1)功能相同,將關系數(shù)據(jù)轉(zhuǎn)化為對象;
(2)使用方法相同,同樣需要制定兩個參數(shù)
不同點:
(1)load方法支持lazy操作,預加載對象,在使用時才創(chuàng)建,get是直接將關系數(shù)據(jù)轉(zhuǎn)化為對象;
(2)load加載對象如果不存在會拋出objectNotFoundException異常,get如果沒有獲取數(shù)據(jù)會返回null。
2、手動構(gòu)造detached對象
想要獲取對象還有另外一種方法,它區(qū)別于get與load方法,是一種手動獲取的方法,首先常見一個對象,然后通過制定id的方式獲取該對象的數(shù)據(jù),方法如下:
public void testUer(){
Session session=null;
try{
session=HibernateUtils.getSession();
session.beginTransaction();
//手動構(gòu)造detached對象
User user=new User();
user.setId("8a1b653745bcc7b50145bcc7b7140001");
//persistent狀態(tài)
//persistent狀態(tài)的對象,當對象的屬性發(fā)生改變的時候
//Hibernate在清理緩存(臟數(shù)據(jù)檢查)的時候,會和數(shù)據(jù)庫同步
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
}finally{
HibernateUtils.closeSession(session);
}
}
查看獲取的結(jié)果圖:
分析結(jié)果圖,代碼中使用了setId方法為該對象制定了id號,在制定id號后就能夠?qū)υ搶ο筮M行操作,在事務提交后同步到數(shù)據(jù)庫中,采用了手動指定,手動指定了對象的信息。
2.1 Delete方法
刪除數(shù)據(jù)庫中指定的對象,在刪除前必須將對象轉(zhuǎn)化到Persistent狀態(tài),可以使用get、load或者手動的方法指定對象,使用方法如下代碼:
session=HibernateUtils.getSession(); session.beginTransaction(); User user=(User)session.load(User.class,"8a1b653745bcc6d50145bcc6d67a0001"); //建議采用此種方式刪除,先加載再刪除 session.delete(user);
2.2 Update
更新數(shù)據(jù),該方法會修改數(shù)據(jù)庫中的數(shù)據(jù)。在使用的時候會出現(xiàn)量中情況,更新數(shù)據(jù)庫某個字段值或者更新數(shù)據(jù)庫的整行值
2.2.1 更新某個字段值
如果只想要更新某個字段的值,在update前,需要使用load或者get方法使對象轉(zhuǎn)化為persistent狀態(tài)代碼如下:
//獲取session對象
session=HibernateUtils.getSession();
//開啟事務
session.beginTransaction();
//或者可以使用另外的方法開啟
//session.getTransaction().begin();
//加載獲取User對象
//方法一:使用load方法
//User user=(User)session.load(User.class, "8a1b653745bcc7b50145bcc7b7140001");
//方法二:手動獲取
User user=new User();
user.setId("8a1b653745bcc7b50145bcc7b7140001");
//更新姓名
user.setName("zhangsan");
session.update(user);
session.getTransaction().commit();
2.2.2 更新整行
想要更新整行的數(shù)據(jù),可以采用手動將狀態(tài)轉(zhuǎn)換到detached狀態(tài),手動指定對象的id值,代碼如下:
//獲取session對象
session=HibernateUtils.getSession();
//開啟事務
session.beginTransaction();
//或者可以使用另外的方法開啟
//session.getTransaction().begin();
//手動獲取
User user=new User();
user.setId("8a1b653745bcc7b50145bcc7b7140001");
//更新姓名
user.setName("zhangsan");
session.update(user);
session.getTransaction().commit();
查看更新結(jié)果:

分析更新結(jié)果,它其實更新了數(shù)據(jù)庫的整行數(shù)據(jù),這種更新操作有太多的不確定因素,不建議使用。
2.3 save方法
插入數(shù)據(jù)。在執(zhí)行save方法時會調(diào)用數(shù)據(jù)庫的insert語句,向數(shù)據(jù)庫中添加新的一行。save后的對象會轉(zhuǎn)化為持久態(tài),在此狀態(tài)下的對象能夠再次更新對象,在最后提交事務時會同更改更新到數(shù)據(jù)庫。如下:
public void testSave2(){
Session session=null;
Transaction tx = null;
try{
session=HibernateUtils.getSession();
//開啟事務
tx= session.beginTransaction();
//Transient狀態(tài)
User user=new User();
user.setName("zhangsi");
user.setPassword("123");
user.setCreateTime(new Date());
user.setExpireTime(new Date());
//persistent狀態(tài)
//persistent狀態(tài)的對象,當對象的屬性發(fā)生改變的時候
//Hibernate在清理緩存(臟數(shù)據(jù)檢查)的時候,會和數(shù)據(jù)庫同步
session.save(user);
user.setName("lisi");
tx.commit();
}catch(Exception e){
e.printStackTrace();
if(tx != null){
tx.rollback();
}
}finally{
HibernateUtils.closeSession(session);
}
//detached狀態(tài)
}
查看上例運行結(jié)果視圖:

分析結(jié)果:session在提交事務的時候其實做了兩部的操作,結(jié)合代碼中的更新過程,首先是新增了一個User對象,之后執(zhí)行了save操作,它會調(diào)用insert語句,然后在代碼中做了一個setName的操作,重新修改了名稱,但這時還沒有同步到數(shù)據(jù)庫中而是在內(nèi)存中,這時就會有兩種狀態(tài),我們稱此時的數(shù)據(jù)位臟數(shù)據(jù),最后提交事務的時候更新到數(shù)據(jù)庫中。
相關文章
使用SpringMVC 重寫、擴展HttpServletRequest請求參數(shù)
這篇文章主要介紹了使用SpringMVC 重寫、擴展HttpServletRequest請求參數(shù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
Java List接口與Iterator接口及foreach循環(huán)使用解析
這篇文章主要介紹了Java List接口與Iterator接口及foreach循環(huán),主要包括List接口與Iterator接口及foreach循環(huán)具體的使用方法和代碼,需要的朋友可以參考下2022-04-04
SpringCloud基于Feign實現(xiàn)遠程調(diào)用的問題小結(jié)
這篇文章主要介紹了SpringCloud基于Feign遠程調(diào)用,通過使用 Feign 的方式,我們可以更加優(yōu)雅地進行多參數(shù)的遠程調(diào)用,避免了手動拼接URL或構(gòu)建復雜的請求體,需要的朋友可以參考下2024-02-02

