一、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)