一、持久對像生命週期
應用程序在使用Hibernate框架後,創建的持久對象會經歷一整套生命週期來完成數據庫的操作,其中主要的三個狀態分別是瞬態(Transient)、持久化(Persistent)、脫管(detached)。這三種狀態的轉換是能夠在應用程序中控制的,如下圖:
為了能清楚的了解這幾種狀態,這裡使用一個實例來查看下這幾種狀態下對象的不同,下面狀態內的代碼,具體步驟如下:
(1)創建Hibernate_session程序集,並添加像相應的jar包;
(2)配置Hibernate,添加相應的實體User類,及它的映射文件,並配置好相應的數據庫連接;
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/> </id> <property name="name"/> <property name="password"/> <property name="createTime"/> <property name="expireTime"/> </class> </hibernate-mapping>
Hibernate數據庫連接配置代碼:
<?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,將數據庫轉換為配置中的相應的語言--> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <mapping resource="com/hibernate/User.hbm.xml"/> </session-factory> </hibernate-configuration>
(3)添加靜態成員sessionfactory的公共類,用來創建一個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; //聲明靜態局部變量SessionFactory,數據庫鏡像static{ try{ //創建並獲取配置數據庫的配置文件,默認獲取hibernate.cfg.xml Configuration cfg=new Configuration().configure(); factory=cfg.buildSessionFactory(); //構建一個數據庫鏡像}catch(Exception e){ e.printStackTrace(); //打印錯誤信息} } public static Session getSession(){ return factory.openSession(); //返回創建的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,並在該文件夾內添加名稱為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 { }二、狀態轉化方法
1、對象直接進入Persistent狀態
1.1 get方法
從數據庫中獲取一行信息,並將該信息同步到創建的對像中,該方法返回一個Object對象,如果沒有查詢到內容則返回null。下面的實例通過採用Session的get方法來獲取一個對象,並將對象轉換為實例。
public void testGet1(){ Session session=null; Transaction tx = null; try{ session=HibernateUtils.getSession(); //開啟事務tx= session.beginTransaction(); //get加載上來的對象為持久對象//執行get會馬上發出查詢語句,如果不存在會返回null User user=(User)session.get(User.class,"ff80808145bc28cc0145bc28ce020002"); System.out.println(user.getName()); //persistent狀態//persistent狀態的對象,當對象的屬性發生改變的時候//Hibernate在清理緩存(臟數據檢查)的時候,會和數據庫同步user.setName("趙柳"); session.getTransaction().commit(); }catch(Exception e){ e.printStackTrace(); if(tx != null){ tx.rollback(); } }finally{ HibernateUtils.closeSession(session); } }設置斷點,獲取User對象。
獲取到了該對象,通過強制轉換後得到了一個user對象。程序中添加了setName方法,也就是說會更新數據庫中的名稱,執行完成後檢查數據庫,如下圖更新結果。
1.2 load方法
功能類似於get方法,也是從數據庫中獲取數據並同步到對像中,該方法支持lazy是一種懶漢操作,它返回的是一個持久化的Object對像或者一個代理,所以需要進行轉化。
public void testLoad1(){ Session session=null; try{ session=HibernateUtils.getSession(); //不會馬上查詢語句,因為load支持lazy(延遲加載/懶加載) //什麼教lazy?只有真正使用這個對象的時候,再創建,對於Hibernate來說//才真正發出查詢語句,主要為了提高性能,lazy是Hibernate中非常重要的特性//Hibernate的lazy是如何實現的?採用代理對象實現,代理對象主要採用的是CGLIB庫生成的//而不是JDK的動態代理,因為JDK的動態代理只能對實現了藉口的類生成代理,CGLIB可以對類生成//代理,它採用的是繼承方式User user=(User)session.load(User.class,"8a1b653745bcc7b50145bcc7b7140001"); System.out.println(user.getName()); //persistent狀態//persistent狀態的對象,當對象的屬性發生改變的時候//Hibernate在清理緩存(臟數據檢查)的時候,會和數據庫同步user.setName("zhaoliu"); session.getTransaction().commit(); }catch(Exception e){ e.printStackTrace(); }finally{ HibernateUtils.closeSession(session); } }查詢獲取該User對像如下圖:
分析上圖,獲取的User對象並不完整,或者說並沒有常見一個User對象,更是一種代理,它使用了CGLIB來預加載對象,只有在使用該對象時才真正創建。
1.3 Get Vs load
get和load方法很重要,在面試Hibernate時經常會考到,下面對比下兩者。
相同點:
(1)功能相同,將關係數據轉化為對象;
(2)使用方法相同,同樣需要製定兩個參數不同點:
(1)load方法支持lazy操作,預加載對象,在使用時才創建,get是直接將關係數據轉化為對象;
(2)load加載對像如果不存在會拋出objectNotFoundException異常,get如果沒有獲取數據會返回null。
2、手動構造detached對象
想要獲取對像還有另外一種方法,它區別於get與load方法,是一種手動獲取的方法,首先常見一個對象,然後通過制定id的方式獲取該對象的數據,方法如下:
public void testUer(){ Session session=null; try{ session=HibernateUtils.getSession(); session.beginTransaction(); //手動構造detached對象User user=new User(); user.setId("8a1b653745bcc7b50145bcc7b7140001"); //persistent狀態//persistent狀態的對象,當對象的屬性發生改變的時候//Hibernate在清理緩存(臟數據檢查)的時候,會和數據庫同步session.getTransaction().commit(); }catch(Exception e){ e.printStackTrace(); }finally{ HibernateUtils.closeSession(session); } }查看獲取的結果圖:
分析結果圖,代碼中使用了setId方法為該對象制定了id號,在製定id號後就能夠對該對象進行操作,在事務提交後同步到數據庫中,採用了手動指定,手動指定了對象的信息。
2.1 Delete方法
刪除數據庫中指定的對象,在刪除前必須將對象轉化到Persistent狀態,可以使用get、load或者手動的方法指定對象,使用方法如下代碼:
session=HibernateUtils.getSession(); session.beginTransaction(); User user=(User)session.load(User.class,"8a1b653745bcc6d50145bcc6d67a0001"); //建議採用此種方式刪除,先加載再刪除session.delete(user);
2.2 Update
更新數據,該方法會修改數據庫中的數據。在使用的時候會出現量中情況,更新數據庫某個字段值或者更新數據庫的整行值
2.2.1 更新某個字段值
如果只想要更新某個字段的值,在update前,需要使用load或者get方法使對象轉化為persistent狀態代碼如下:
//獲取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 更新整行<br />想要更新整行的數據,可以採用手動將狀態轉換到detached狀態,手動指定對象的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();查看更新結果:
分析更新結果,它其實更新了數據庫的整行數據,這種更新操作有太多的不確定因素,不建議使用。
2.3 save方法
插入數據。在執行save方法時會調用數據庫的insert語句,向數據庫中添加新的一行。 save後的對象會轉化為持久態,在此狀態下的對象能夠再次更新對象,在最後提交事務時會同更改更新到數據庫。如下:
public void testSave2(){ Session session=null; Transaction tx = null; try{ session=HibernateUtils.getSession(); //開啟事務tx= session.beginTransaction(); //Transient狀態User user=new User(); user.setName("zhangsi"); user.setPassword("123"); user.setCreateTime(new Date()); user.setExpireTime(new Date()); //persistent狀態//persistent狀態的對象,當對象的屬性發生改變的時候//Hibernate在清理緩存(臟數據檢查)的時候,會和數據庫同步session.save(user); user.setName("lisi"); tx.commit(); }catch(Exception e){ e.printStackTrace(); if(tx != null){ tx.rollback(); } }finally{ HibernateUtils.closeSession(session); } //detached狀態}查看上例運行結果視圖:
分析結果:session在提交事務的時候其實做了兩部的操作,結合代碼中的更新過程,首先是新增了一個User對象,之後執行了save操作,它會調用insert語句,然後在代碼中做了一個setName的操作,重新修改了名稱,但這時還沒有同步到數據庫中而是在內存中,這時就會有兩種狀態,我們稱此時的數據位臟數據,最後提交事務的時候更新到數據庫中。