程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Hibernate —— Session,hibernatesession

Hibernate —— Session,hibernatesession

編輯:JAVA綜合教程

Hibernate —— Session,hibernatesession


一、概述

Session 是 Hibernate 向應用程序提供操縱數據的主要接口,它提供了基本的保存、更新、刪除和加載 Java 對象的方法。

二、Session 緩存

1.簡介

(1)Session 有一個緩存,稱為 Hibernate 一級緩存。位於緩存中的對象稱為持久化對象,每一個持久化對象與數據庫中的一條記錄對應。

(2)站在持久化的角度,Hibernate 將對象分為 4 種狀態:臨時狀態、持久化狀態、游離狀態、刪除狀態。

2.測試 Session 緩存

(1)准備

①hibernate.cfg.xml 文件請參看上一篇文章。

②SessionFactory、Session、Transaction

private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;

@Before
public void init() {
    Configuration configuration = new Configuration().configure();
    ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
    sessionFactory = configuration.buildSessionFactory(serviceRegistry);
    session = sessionFactory.openSession();
    transaction = session.beginTransaction();
}

@After
public void destroy() {
    transaction.commit();
    session.close();
    sessionFactory.close();
}

說明:使用單元測試類進行測試。因為是測試環境,不存在並發的情況,創建了一個 Session 對象。

(2)測試

@Test
public void testSession() {
    News news = (News) session.get(News.class, 1);
    System.out.println(news);

    News news2 = (News) session.get(News.class, 1);
    System.out.println(news2);

    System.out.println(news.equals(news2));
}

測試結果:

Hibernate: 
    select
        news0_.id as id1_0_0_,
        news0_.title as title2_0_0_,
        news0_.author as author3_0_0_,
        news0_.date as date4_0_0_ 
    from
        hibernate.news news0_ 
    where
        news0_.id=?
News{id=1, title='Title', author='tom', date=2016-09-28}
News{id=1, title='Title', author='tom', date=2016-09-28}
true

說明:

第一次查詢的時候,會將引用賦值給 news,同時向 Session 緩存中存入了一份。

第二次查詢的時候,並沒有發送 select 語句,而是從 Session 緩存中直接獲取的。

3.操縱 Session 緩存

(1)flush() :使數據表中的記錄和 Session 緩存中的對象的狀態保持一致。

① 在 Transaction 的 commit() 方法中,先調用 session 的 flush 方法,再提交事務。

org.hibernate.engine.transaction.spi.AbstractTransactionImpl#commit

@Override
public void commit() throws HibernateException {
    if ( localStatus != LocalStatus.ACTIVE ) {
        throw new TransactionException( "Transaction not successfully started" );
    }

    LOG.debug( "committing" );

    beforeTransactionCommit();

    try {
        doCommit();
        localStatus = LocalStatus.COMMITTED;
        afterTransactionCompletion( Status.STATUS_COMMITTED );
    }
    catch ( Exception e ) {
        localStatus = LocalStatus.FAILED_COMMIT;
        afterTransactionCompletion( Status.STATUS_UNKNOWN );
        throw new TransactionException( "commit failed", e );
    }
    finally {
        invalidate();
        afterAfterCompletion();
    }
}

org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction#beforeTransactionCommit

protected void beforeTransactionCommit() {
    this.transactionCoordinator().sendBeforeTransactionCompletionNotifications(this);
    if(this.isDriver && !this.transactionCoordinator().getTransactionContext().isFlushModeNever()) {
        this.transactionCoordinator().getTransactionContext().managedFlush();
    }

    if(this.isDriver) {
        this.transactionCoordinator().getTransactionContext().beforeTransactionCompletion(this);
    }

}

② 可能會打印 SQL 語句,但是不會提交事務。

③ 在未提交事務或顯式的調用 flush() 方法前,也可能會進行 flush() 操作。

  • 執行 HQL 或 QBC 查詢,會先進行 flush() 操作,以得到數據表的最新記錄。
  • 若記錄的 ID 是由數據庫使用的自增的方式生成的,則在調用 save() 方法時,就會立即發送 INSERT 語句,因為 save 方法後,必須保證對象的 ID 存在。

(2)refresh():會強制發送 SELECT 語句,以使 Session 緩存中對象的狀態和數據表中對應的記錄保持一致。

1 @Test
2 public void testRefresh() {
3     News news = (News) session.get(News.class, 1);
4     System.out.println(news);
5     session.refresh(news);
6     System.out.println(news);
7 }

我在第5行斷點,然後修改數據庫中 News 的 `author` 字段,改為 jerry。執行。

兩次打印結果相同。

Hibernate: select news0_.id as id1_0_0_, news0_.title as title2_0_0_, news0_.author as author3_0_0_, news0_.date as date4_0_0_ from hibernate.news news0_ where news0_.id=? News{id=1, title='Title', author='tom', date=2016-09-28} Hibernate: select news0_.id as id1_0_0_, news0_.title as title2_0_0_, news0_.author as author3_0_0_, news0_.date as date4_0_0_ from hibernate.news news0_ where news0_.id=? News{id=1, title='Title', author='tom', date=2016-09-28} View Code

原因:數據庫的隔離級別,Mysql 默認隔離級別為 REPEATABLE READ。

在 Hibernate 的配置文件中可以顯式的設置隔離級別. 每一個隔離級別都對應一個整數:

1. READ UNCOMMITED

2. READ COMMITED

4. REPEATABLE READ

8. SERIALIZEABLE

Hibernate 通過為 Hibernate 映射文件指定 hibernate.connection.isolation 屬性來設置事務的隔離級別。
修改後的打印結果:

兩次打印結果不同。

Hibernate: select news0_.id as id1_0_0_, news0_.title as title2_0_0_, news0_.author as author3_0_0_, news0_.date as date4_0_0_ from hibernate.news news0_ where news0_.id=? News{id=1, title='Title', author='jerry', date=2016-09-28} Hibernate: select news0_.id as id1_0_0_, news0_.title as title2_0_0_, news0_.author as author3_0_0_, news0_.date as date4_0_0_ from hibernate.news news0_ where news0_.id=? News{id=1, title='Title', author='tom', date=2016-09-28} View Code

(3)clear():清理緩存。

@Test
public void testClear() {
    session.get(News.class, 1);
    session.clear();
    session.get(News.class, 1);
}

輸出結果:

Hibernate: select news0_.id as id1_0_0_, news0_.title as title2_0_0_, news0_.author as author3_0_0_, news0_.date as date4_0_0_ from hibernate.news news0_ where news0_.id=? Hibernate: select news0_.id as id1_0_0_, news0_.title as title2_0_0_, news0_.author as author3_0_0_, news0_.date as date4_0_0_ from hibernate.news news0_ where news0_.id=? View Code

三、Session API

1.四種狀態的轉換圖

(1)臨時對象

  • 在使用代理主鍵的情況下,OID 通常為 null
  • 不處於 Session 的緩存中
  • 在數據庫中沒有對應的記錄

(2)持久化對象

  • OID 不為空
  • 位於 Session 緩存中
  • 在同一個 Session 實例的緩存中,數據庫表中的每條記錄只對應唯一的持久化對象

(3)游離對象

  • OID 不為空
  • 不處於 Session 緩存中

(4)刪除對象

  • 在數據庫中沒有和其 OID 對應的記錄
  • 不再處於 Session 緩存中

2.save()

(1)將一個臨時對象轉變為持久化對象

(2)為對象分配 ID

(3)在 flush 緩存的時候,計劃執行一條 INSERT 語句

(4)在 save() 方法前的 id 是無效的

(5)持久化對象的 ID 是不能被更改的。因為 Hibernate 通過持久化對象的 OID 來維持它與數據庫相關記錄的對應關系。

* persist() 和 save() 區別

對一個 OID 不為 Null 的對象執行 save() 方法時,會把該對象以一個新的 OID 保存到數據庫中,而 persist() 則會拋出一個異常。

3.get()/load()

(1)都可以根據 OID 從數據庫中加載一個持久化對象。

(2)執行 get() 時會立即加載對象。執行 load() ,若不使用該對象,則不會立即執行查詢操作,而是返回一個代理對象。

(3)get() 是立即檢索,而 load() 是延遲檢索。

(4)若數據表中沒有對應記錄,Session 也沒有被關閉。get() 返回 null,load() 使用返回對象時拋出異常。

(5)load() 可能會拋出 LozyInitizationException 異常:在需要初始化代理對象之前已經關閉了 Session。

@Test
public void testLoad() {
    News news = (News) session.load(News.class, 1);
    session.close();
    System.out.println(news);
}
org.hibernate.LazyInitializationException: could not initialize proxy - no Session

4.update()

(1)將一個游離對象轉變為持久化對象,並且計劃執行一條 update 語句。

(2)若更新一個持久化對象,不需要顯式的調用 update() 方法。因為在調用 Transaction 的 commit() 方法時,會先執行 session 的 flush() 方法。

(3)注意

  • 無論要更新的游離對象和數據表的記錄是否一致,都會發送 UPADATE 語句。如何讓只在不一致的情況下發送 UPDATE 語句?在 entity.hbm.xml 文件的 class 節點設置                             select-before-update=true(默認為 false)。通常不需要設置,與觸發器協同工作時需要注意。
  • 若數據表中沒有對應的記錄,但還是調用了 update() 方法,會拋出異常。
  • 當 update() 方法關聯一個游離對象時,如果在 Session 緩存中已經存在相同 OID 的持久化對象,會拋出異常。因為在 Session 緩存中不能有兩個 OID 相同的對象。

5.saveOrUpdate()

(1)同時包含了 save() 和 update() 方法的功能。

(2)判斷是否是游離對象還是臨時對象是根據 對象的 OID 來判定的。若為 null ,則執行 save() ,若不為 null,則判定為游離對象,執行 update() 。

(3)若 OID 不為 null,但數據中還沒有與之對應的記錄,則會拋出一個異常。

(4)了解:OID 值等於 id 的 unsaved-value 屬性值的對象,也被認為是一個游離對象。

6.delete()

(1)既可以刪除一個游離對象,也可以刪除一個持久化對象。

(2)只要 OID 和數據表中一條記錄對應,就會准備執行 delete 操作,若 OID 在數據表中沒有對應的記錄,則拋出異常。

(3)在執行 delete() 後,還是可以獲取到對象的 OID,防止對該對象的其他持久化操作,可以通過設置 hibernate 配置文件的 hibernate.use_identifier_rollback 為 true,

使刪除對象後,把其 OID 值為 null。

7.evict()

把指定持久化對象從 session 緩存中移除。

8.調用存儲過程

@Test
public void testWork() {
    session.doWork(new Work() {
        @Override
        public void execute(Connection connection) throws SQLException {
            System.out.println(connection);
            // 調用存儲過程
        }
    });
}

四、總結

介紹了 Hibernate 的一級緩存,包括如何操縱 Session 的緩存,以及四種狀態之間的轉換,以及建立在 Session 緩存和四種狀態基礎上的 Session API。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved