閱讀44 返回首頁    go 阿裏雲 go 技術社區[雲棲]


第五章 Hibernate核心API介紹與其使用

5.1 Configuration接口
5.1.1加載hibernate.cfg.xml並完成係統的配置
       Configuration接口的作用是對Hibernate進行配置、並啟動Hibernate和連接數據庫係統。
       在Hibernate的啟動過程中,Configuration類的實例首先定位缺省XML配置文件(hibernate.cfg.xml),並讀取關的配置項目,然後創建出一個SessionFactory對象。根5.1.2據Configuration對象創建一個SessionFactory對象。

Configuration cfg = new Configuration();
		cfg.configure();
		SessionFactory sessionFactory = cfg.buildSessionFactory();

5.1.3 也可以采用自定義的XML配置文件(少用)
        可以指定開發者自己的*.hbm.xml文件的位置,而不是使用默認的classpath下麵的hibernate.cfg.xml。但需要在代碼中指示開發者自定義的XML配置文件。
cfg.configure("hibernate2.cfg.xml");

5.2 SessionFactory接口

5.2.1 利用工廠類SessionFactory中取得Session的實例
Session session = sessionFactory.openSession();

5.2.2 SessionFactory並不是輕量級的
       但要注意的是SessionFactory並不是輕量級的!(占內存)
       實際上它的設計者的意圖是讓它能在整個應用中共享。

5.2.3 每個數據存儲源對應創建一個SessionFactory(單例) 

5.2.4 SessionFactory的緩存
       可分為兩類:內置緩存和外置緩存。

5.2.4.1 SessionFactory的內置緩存中存放了Hibernate配置信息和映射元數據信息、同時也緩存了Hibernate自動生成的SQL語句等;
 
5.2.4.2 SessionFactory的外置緩存是一個可配置的緩存插件
       在默認情況下,SessionFactory不會啟用這個緩存插件。
       外置緩存能存放大量數據庫數據的拷貝,外置緩存的物理介質可以是內存或者硬盤。


5.3 Session接口

5.3.1輕量級的類
        在Hibernate中,實例化的Session是一個輕量級的類,創建和銷毀它都不會占用很多資源。
        這在實際項目中確實很重要,因為在客戶程序中,可能會不斷地創建以及銷毀Session對象,如果Session的開銷太大,會給係統帶來不良影響。

5.3.2非線程安全的(一請求---一線程----一session---一事務)
        值得注意的是Session對象是非線程安全的,因此最好是一個線程隻創建一個Session對象(將它設計為局部對象)。
private static ThreadLocal threadSession = new ThreadLocal();
public static Session getThreadLocalSession(){
		Session s = (Session) threadSession.get();
		if(s == null){
			s = sessionFactory.openSession();
			threadSession.set(s);
		}
		return s;
	}

5.3.3 Session看作介於數據連接與事務管理一種中間接口
       我們可以將session想象成一個持久對象的緩衝區,Hibernate能檢測到這些持久對象的改變,並及時刷新數據庫。 

5.3.4 每一個Session實例和一個數據庫事務綁定 
       通常將每一個Session實例和一個數據庫事務綁定,也就是說,每執行一個數據庫事務(操作),都應該先創建一個新的Session實例。
       如果事務執行中出現異常,應該撤銷事務;同時不論事務執行成功與否,最後都應該調用Session的close()方法,從而釋放Session實例占用的資源。

5.3.5如何獲得Session對象
       首先創建SessionFactory對象,應用程序如果訪問多個數據源時,則應該產生多個SessionFactory;但是僅僅為了服務於某個請求時,不要創建一個新的     SessionFactory,因為創建SessionFactory 需要耗費大量的資源。
       然後根據SessionFactory再創建Session對象

5.3.6 Session的編程規則---需要隨時更新和釋放 
       注意:應該要session.close()語句放在finally語句塊中。

5.3.7 Sessin 接口中針對單條記錄的基本的CURD操作方法 
    1、save();  
session.save(Object);// session的save方法是向數據庫中保存一個對象
   
    2、delete()  
 session.delete(Object);//Object對象需要有ID。對象刪除後,對象狀態為Transistent狀態

    3、load()   
Session.load(Class arg0, Serializable arg1) throws HibernateException
* arg0:需要加載對象的類,例如:User.class
* arg1:查詢條件(實現了序列化接口的對象):例"4028818a245fdd0301245fdd06380001"字符串已經實現了序列化接口。如果是數值類類型,則hibernate會自動使用包裝類,例如 1
* 此方法返回類型為Object,但返回的是代理對象。
* 執行此方法時不會立即發出查詢SQL語句。隻有在使用對象時,它才發出查詢SQL語句,加載對象。
* 因為load方法實現了lazy(稱為延遲加載、賴加載)
* 延遲加載:隻有真正使用這個對象的時候,才加載(才發出SQL語句)
* hibernate延遲加載實現原理是代理方式。
* 采用load()方法加載數據,如果數據庫中沒有相應的記錄,則會拋出異常對象不找到(org.hibernate.ObjectNotFoundException)
try {
    session = sf.openSession();
    session.beginTransaction();
    User user = (User)session.load(User.class,1);
    //隻有在使用對象時,它才發出查詢SQL語句,加載對象。
    System.out.println("user.name=" + user.getName());

    //因為此的user為persistent狀態,所以數據庫進行同步為龍哥。
    user.setName("發哥");

    session.getTransaction().commit();
} catch (HibernateException e) {
    e.printStackTrace();
    session.getTransaction().rollback();
} finally{
    if (session != null){
    if (session.isOpen())
    session.close();
}
  
  4、Get()
   
 Session.get(Class arg0, Serializable arg1)方法
* arg0:需要加載對象的類,例如:User.class
* arg1:查詢條件(實現了序列化接口的對象):
        例"4028818a245fdd0301245fdd06380001"字符串已經實現了序列化接口。如果是基數類型,則hibernate會自動轉換成包裝類,如 1
        返回值: 此方法返回類型為Object,也就是對象,然後我們再強行轉換為需要加載的對象就可以了。
        如果數據不存在,則返回null;
        注:執行此方法時立即發出查詢SQL語句。加載User對象
        加載數據庫中存在的數據,代碼如下:
try {
     session = sf.openSession();
     session.beginTransaction();

     / * 此方法返回類型為Object,也就是對象,然後我們再強行轉換為需要加載 的對象就可以了。如果數據不存在,則返回null
       * 執行此方法時立即發出查詢SQL語句。加載User對象。
       */
     User user = (User)session.get(User.class, 1);

     //數據加載完後的狀態為persistent狀態。數據將與數據庫同步。
     System.out.println("user.name=" + user.getName());

     //因為此的user為persistent狀態,所以數據庫進行同步為龍哥。
     user.setName("龍哥");
  
     session.getTransaction().commit();
} catch (HibernateException e) {
     e.printStackTrace();
     session.getTransaction().rollback();
} finally{
     if (session != null){
     if (session.isOpen()){
         session.close();
     }
}

    5、load()與get()區別
    不存在對應記錄時表現不一樣;
    load返回的是代理對象,等到真正使用對象的內容時才發出sql語句,這樣就要求在第一次使用對象時,要求session處於open狀態,否則出錯
    get直接從數據庫加載,不會延遲加載
    get()和load()隻根據主鍵查詢,不能根據其它字段查詢,如果想根據非主鍵查詢,可以使用HQL

    6、update()
    用來更新detached對象,更新完成後轉為為persistent狀態(默認更新全部字段)
    更新transient對象會報錯(沒有ID)
    更新自己設定ID的transient對象可以(默認更新全部字段)
    persistent狀態的對象,隻要設定字段不同的值,在session提交時,會自動更新(默認更新全部字段)
    更新部分更新的字段(更改了哪個字段就更新哪個字段的內容)
    方法1:update/updatable屬性
     xml:設定<property>標簽的update屬性,設置在更新時是否參數更新
<property name="name" update="false"/>
     注意:update可取值為true(默認):參與更新;false:更新時不參與更新
     annotateon:設定@Column的updatable屬性值,true參與更新,false:不參與更新
@Column(updatable=false)
public String getTitle() {return title;}
    注意:此種方法很少用,因為它不靈活
    方法二:dynamic-update屬性
    注意:此方法目前隻適合xml方式,JAP1.0 annotation沒有對應的
    在實體類的映射文件中的<class>標簽中,使用dynamic-update屬性,true:表示修改了哪個字段就更新哪個字段,其它字段不更新,但要求是同一個session(不能跨session),如果跨了session同樣會更新所有的字段內容。
<class name="com.bjsxt.Student" dynamic-update="true">
 代碼:
@Test
public void testUpdate5() {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Student s = (Student)session.get(Student.class, 1);
s.setName("zhangsan5");
//提交時,會隻更新name字段,因為此時的s為persistent狀態
session.getTransaction().commit();
s.setName("z4");
Session session2 = sessionFactory.getCurrentSession();
session2.beginTransaction();
//更新時,會更新所有的字段,因為此時的s不是persistent狀態
session2.update(s);
session2.getTransaction().commit();}


            如果需要跨session實現更新修改的部分字段,需要使用session.merget()方法,合並字段內容
@Test
public void testUpdate6() {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Student s = (Student)session.get(Student.class, 1);
s.setName("zhangsan6");
session.getTransaction().commit();
s.setName("z4");
Session session2 = sessionFactory.getCurrentSession();
session2.beginTransaction();
session2.merge(s);
session2.getTransaction().commit()}
    這樣雖然可以實現部分字段更新,但這樣會多出一條select語句,因為在字段數據合並時,需要比較字段內容是否已變化,就需要從數據庫中取出這條記錄進行比較
使用HQL(EJBQL)麵向對象的查詢語言(建議)
@Test
public void testUpdate7() {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Query q = session.createQuery(
"update Student s set s.name='z5' where s.id = 1");
q.executeUpdate();
session.getTransaction().commit();
} 


    7、saveOrUpdate()
    在執行的時候hibernate會檢查,如果對象在數據庫中已經有對應的記錄(是指主鍵),則會更新update,否則會添加數據save
   
   8、clear()
    清除session緩存
    無論是load還是get,都會首先查找緩存(一級緩存,也叫session級緩存),如果沒有,才會去數據庫查找,調用clear()方法可以強製清除session緩存
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Teacher t = (Teacher)session.load(Teacher.class, 1);
System.out.println(t.getName());
session.clear();
Teacher t2 = (Teacher)session.load(Teacher.class, 1);
System.out.println(t2.getName());
session.getTransaction().commit();
     注意:這樣就會發出兩條SELECT語句,如果把session.clear()去除,則隻會發出一條SELECT語句,因為第二次load時,是使用session緩存中ID為1的對象,而這個對象已經在第一次load到緩存中 了。
    9、flush()
    在hibernate中也存在flush這個功能,在默認的情況下session.commit()之前時,其實執行了一個flush命令。
    Session.flush功能:
    清理緩存;
    執行sql(確定是執行SQL語句(確定生成update、insert、delete語句等),然後執行SQL語句。)


    Session在什麼情況下執行flush:
    默認在事務提交時執行;
    注意:flush時,可以自己設定,使用session.setFlushMode(FlushMode)來指定。
 
<pre name="code" session.setFlushMode(FlushMode);
   FlushMode的枚舉值:
    FlushMode.ALWAYS:任務一條SQL語句,都會flush一次
    FlushMode.AUTO :自動flush(默認)
    FlushMode.COMMIT: 隻有在commit時才flush
FlushMode.MANUAL:手動flush。
FlushMode.NEVER :永遠不flush  此選項在性能優化時可能用,比如session取數據為隻讀時用,這樣就不需要與數據庫同步了


注意:設置flush模式時,需要在session開啟事務之前設置。
可以顯示的調用flush;
在執行查詢前,如:iterate.
注:如果主鍵生成策略是uuid等不是由數據庫生成的,則session.save()時並不會發出SQL語句,隻有flush時才會發出SQL語句,但如果主鍵生成策略是native由數據庫生成的,則session.save的同時就發出SQL語句。


10、evict()
例如:session.evict(user)
作用:從session緩存(EntityEntries屬性)中逐出該對象
但是與commit同時使用,會拋出異常
session = HibernateUtils.getSession();
tx = session.beginTransaction();

User1 user = new User1();
user.setName("李四");
user.setPassword("123");
user.setCreateTime(new Date());
user.setExpireTime(new Date());

//利用Hibernate將實體類對象保存到數據庫中,因為user主鍵生成策略采用的是uuid,所以調用完成save後,隻是將user納入session的管理,不會發出insert語句,但是id已經生成,session中的existsInDatabase狀態為false
session.save(user);

session.evict(user);//從session緩存(EntityEntries屬性)中逐出該對象
//無法成功提交,因為hibernate在清理緩存時,在session的臨時集合(insertions)中取出user對象進行insert操作後需要更新entityEntries屬性中的existsInDatabase為true,而我們采用evict已經將user從session中逐出了,所以找不到相關數據,無法更新,拋出異常。

tx.commit();

解決在逐出session緩存中的對象不拋出異常的方法:
//在session.evict()之前進行顯示的調用session.flush()方法就可以了。
session.save(user);

//flush後hibernate會清理緩存,會將user對象保存到數據庫中,將session中的insertions中的user對象清除,並且會設置session中的existsInDatabase狀態為false
session.flush();

session.evict(user);//從session緩存(EntityEntries屬性)中逐出該對象

//可以成功提交,因為hibernate在清理緩存時,在Session的insertions中集合中無法找到user對象所以不會發出insert語句,也不會更新session中existsInDatabase的狀態。
tx.commit();

5.4 Transaction接口
 Transaction tx = null;
		try{
			tx = session.beginTransaction();
			session.save(obj);
			tx.commit();
		}catch(HibernateException e){
			if(tx != null){
				tx.rollback();
			}
			throw e;
		}finally{
			if(session != null){
				session.close();
			}
		}
	}




最後更新:2017-04-03 18:52:05

  上一篇:go Android adapter局部更新
  下一篇:go HDU 4305 無向連通圖的生成樹個數 矩陣行列式取模