Hibernate的學習筆記

一、Hibernate說明:

首先說一下Hibernate吧,Hibernate是Java持久層框架之一,是一個開放源碼的ORM框架,它在ORM框架中級別最高,是完全面向對象的。

那麼什麼是ORM框架呢?O--Object(對象)、R--Relation(關係)、M--Mapping(映射),簡單的說就是在實體類和數據庫表建立對應關係,避免去和複雜的sql語句打交道,通過映射關係,可以直接操作對象型的數據來代替操作數據庫中的關係型數據,再着重說一下,Hibernate是完全面向對象的,在操作數據庫的層面上,我們只需要用對象來操作數據庫即可,與數據庫中的表、字段就完全脫離開來。

二、Hibernate對象的三種狀態

進入正題,Hibernate的對象共有三種狀態:瞬時態持久態遊離態(託管態)

瞬時態:新new出來的對象,就屬於瞬時態,此時session和數據庫中都不存在該對象。

持久態:對象做持久化操作的時候,比如調用session.save()方法時,對象由瞬時態變爲持久態,此時session和數據庫中都存在。

遊離態:對象與session脫離關聯,比如調用session.close()方法時,此時只有數據庫中存在該對象。

上個圖(來源於網絡--侵刪)

代碼:

package com.sinosoft.objectTest;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.sinosoft.model.User;

@RunWith(SpringJUnit4ClassRunner.class)  //spring整合了junit測試
@ContextConfiguration("classpath:applicationContext.xml")  //用於加載spring的配置文件,初始化容器
public class TransientTest {
	
	@Autowired
	//spring容器爲我們提供了工廠對象,這裏使用註解的方式去容器中拿到該對象
	private SessionFactory factory; 
	
	 @Test
	public void testMethod01(){
		 //從session工廠中拿到session對象
		 Session session = factory.openSession();
		 //開啓事物
		 Transaction tx = session.beginTransaction();
		 //新建一個對象,此時session還沒有對user對象管理,所以處於瞬時態
		 User user = new User();
		 user.setUserName("Transient");
		 user.setUserPassword("123");
		 //執行完save方法之後,該對象的狀態由瞬時態變爲持久態
		 session.save(user);
		 
		 //提交事務
		 tx.commit();  
		 //關閉session
		 session.close();   
	 }
}

提一句,關於Hibernate的sql打印,下面這段代碼,在調用完save()方法之後,user對象就變爲了持久態,並且被加入了一級緩存中,提交事物時,會發送一條sql語句:Hibernate: insert into user_test(user_name, user_password) values(?, ?)此時,如果我們更改了user對象的屬性,它會拿當前的user對象去和一級緩存中的user對象進行比較,如果相同,則無需發送新的sql語句,如果不同,則會發送一條更新的sql語句。這裏的測試用例會發送一條sql語句:Hibernate: update user_test setuser_name=?,user_password=? where user_id=?。這裏只是做了一點簡單的說明,關於具體sql打印的規則。略

@Test
public void testMethod02(){
	Session session = factory.openSession();
	Transaction tx = session.beginTransaction();
	User user = new User();
	user.setUserName("Transient01");
	user.setUserPassword("123");
	//此時user對象由瞬時態變爲持久態,同時對象被session管理,放入一級緩存
	session.save(user);
	//更改對象的屬性??
	user.setUserPassword("234");
	
	tx.commit();
	session.close();
}

三、HIbernate的一二級緩存

Hibernate的一級緩存也叫做session級別的緩存也叫做事物級別的緩存,它的生命週期和session的生命週期一樣,當session關閉,一級緩存也就消失了。支持的方法,get()、load()。session緩存是一塊內存空間,用來存放相互管理的java對象,Hibernate查詢對象的時候,首先會使用對象的屬性OID值在hibernate得一級緩存中進行查找,如果找到匹配OID值得對象,就直接將該對象從一級緩存中取出使用,不再查詢數據庫;如果沒有找到相同的OID的值得對象,則會去數據庫中查找相應數據。當從數據庫查詢數據的時候,該數據信息也會放置到一級緩存中。Hibernated的一級緩存的作用就是減少對數據庫的訪問次數。

Hibernate的二級緩存是SessionFactory級別的緩存,它的二級緩存策略的一般過程如下:

(1) 條件查詢的時候,總是發出一條select * from table_name where …. (選擇所有字段)這樣的SQL語句查詢數據庫,一次獲得所有的數據對象。

(2) 把獲得的所有數據對象根據ID放入到第二級緩存中。

(3) 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,如果配置了二級緩存,那麼從二級緩存中查;查不到,再查詢數據庫,把結果按照ID放入到緩存。

(4) 刪除、更新、增加數據的時候,同時更新緩存。

什麼樣的數據適合存放到第二級緩存中? 很少被修改的數據、不是很重要的數據

四、HIbernate的主鍵策略

代理主鍵(沒有任何業務意義的主鍵):

increment:先查最大值,加1,有線程安全問題,只是用測試

identity:數據庫逐漸自增

sequence:使用數據庫中的序列生成主鍵(oracle纔有)

hilo:由Hibernate自己封裝主鍵自增算法

native:自動三選一(increment,identity,hilo)

uuid:隨機生成字符串作爲uuid

自然主鍵: 

assigned:id值需要手動指定,一個個去設置。

五、Hibernate的Get和load方法的區別

(1)get()方法直接返回實體類,如果查不到數據則返回null。load()會返回一個實體代理對象(當前這個對象可以自動轉化爲實體對象),但當代理對象被調用時,如果沒有數據不存在,就會拋出個org.hibernate.ObjectNotFoundException異常

(2)load先到緩存(session緩存/二級緩存)中去查,如果沒有則返回一個代理對象(不馬上到DB中去找),等後面使用這個代理對象操作的時候,纔到DB中查詢,這就是我們常說的 load在默認情況下支持延遲加載(lazy);get先到緩存(session緩存/二級緩存)中去查,如果沒有就到DB中去查(即馬上發出sql)。總之,如果你確定DB中有這個對象就用load(),不確定就用get()(這樣效率高),具體看代碼?:

@Test
public void testMethod03(){
	Session session = factory.openSession();
	Transaction tx = session.beginTransaction();
	
	User user = new User();
	user.setUserName("Transient03");
	user.setUserPassword("sdsdf");
	
	//"7"這個userID對應的user對象還沒有加入庫中,緩存中也沒有
	//user = (User)session.load(User.class, 7);
	user = (User)session.get(User.class, 7);
	System.out.println(user);
	tx.commit();
	session.close();
}

問題:查詢不到的時候會返回什麼?

我們將新建一個user對象,該user對象即沒有入庫,也沒有加入緩存,當我們使用load()方法加載這個還沒有入庫的對象時,它在緩存和數據庫中都沒有找到該對象,此時會拋出一個ObjectNotFoundException的異常。把load()方法註釋掉以後,使用get方法加載並且打印對象,只是返回一個null.

異常信息:

org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.sinosoft.model.User#7]
	at org.hibernate.boot.internal.StandardEntityNotFoundDelegate.handleEntityNotFound(StandardEntityNotFoundDelegate.java:28)
	at org.hibernate.proxy.AbstractLazyInitializer.checkTargetState(AbstractLazyInitializer.java:242)
	at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:159)
	at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:266)
	at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:68)
	at com.sinosoft.model.User_$$_jvste44_0.setUserPassword(User_$$_jvste44_0.java)
	at com.sinosoft.objectTest.TransientTest.testMethod03(TransientTest.java:32)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:254)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章