Hibernate的高級特性

實體對象生命週期
事務管理

數據緩存

實體對象生命週期
實體對象的3種狀態
自由狀態
即實體對象在內存中的自由存在,它與數據庫中的記錄無關。
持久狀態
即實體對象處於由Hibernate框架所管理的狀態。
遊離狀態
處於持久狀態的對象,其對應的Session實例關閉之後,那麼,此對象就處於遊離狀態。

事務管理
事務是一個邏輯工作單元,它包括一系列的操作。事務包含4個基本特性,也就是我們常說的ACID,其中包括:
Atomic(原子性):事務中包含的操作被看作一個邏輯單元,這個邏輯單元中的操作要麼全部成功,要麼全部失敗。
Consistency(一致性):一致性意味着,只有合法的數據可以被寫入數據庫,如果數據有任何違例(比如數據與字段類型不符),則事務應該將其回滾到最初狀態。
Isolation(隔離性):事務允許多個用戶對同一個數據的併發訪問,而不破壞數據的正確性和完整性。同時,並行事務的修改必須與其他並行事務的修改相互獨立。
Durability(持久性):事務結束後,事務處理的結果必須能夠得到固化(保存在可掉電存儲器上)。

Hibernate的事務管理
User7 u1 = new User7();
User7 u2 = new User7();
u1.setName("zs");
u1.setAccount(1000);
u2.setName("abcdefg");
u2.setAccount(2000);
Session s = HibernateSessionFactory.getSession();
Transaction t = s.beginTransaction();
try {
    s.save(u1);
    s.save(u2);
    t.commit();
} catch (Exception ex) {
    ex.printStackTrace();
    t.rollback();
}
HibernateSessionFactory.closeSession();

事務管理的隔離等級
事務隔離指的是,數據庫(或其他事務系統)通過某種機制,在並行的多個事務之間進行分隔,使每個事務在其執行過程中保持獨立(如同當前只有此事務單獨運行)。
Hibernate中的事務隔依賴於底層數據庫提供的事務隔機制,因此,對數據庫事務隔離機制的理解在基於Hibernate實現的持久層中同樣適用。
首先我們來看數據操作過程中可能出現的3種不確定情況:
髒讀取:一個事務讀取了另一個並行事務未提交的數據。
不可重複讀取:一個事務再次讀取之前曾讀取過的數據時,發現該數據已經被另一個已提交的事務修改。
虛讀:一個事務重新執行一個查詢,返回一套符合查詢條件的記錄,但這些記錄中包含了因爲其他最近提交的事務而產生的新記錄。

事務管理的隔離等級
爲了避免上面3種情況的出現,標準SQL規範中,定義瞭如下4個事務隔離等級:
隔離等級    髒讀取    不可重複讀取    虛讀
Read Uncommitted    可能    可能    可能
Read Committed    不可能    可能    可能
Repeatable Read    不可能    不可能    可能
Serializable    不可能    不可能    不可能
這4種事務隔離等級的嚴密程度由前往後依次遞增,同時,其性能也依次下降。因此,無論實際情況如何,都使用最高級事務隔離的做法並不可取。我們必須根據應用的具體情況進行取捨,以獲得數據合法性與系統性能上的最佳平衡。
在Hibernate中設置事務管理隔離等級:
<property name="connection.isolation">2</property>


業務邏輯的實現過程中,往往需要保證數據訪問的排他性。如在金融系統的日終結算處理中,我們希望針對某個截止點的數據進行處理,而不希望在結算進行過程中(可能是幾秒種,也可能是幾個小時),數據再發生變化。
此時,我們就需要一些機制來保證這些數據在某個操作過程中不會被外界修改,這樣的機制,在這裏,也就是所謂的“鎖”,即給我們選定的目標數據上鎖,使其無法被其他程序修改。
Hibernate支持兩種鎖機制:
悲觀鎖
樂觀鎖

悲觀鎖
悲觀鎖,正如其名,它指的是對數據被外界修改持保守態度,因此,在整個數據處理過程中,將數據處於鎖定狀態。悲觀鎖的實現,往往依靠數據庫提供的鎖機制。
一個典型的,依賴數據庫實現的悲觀鎖調用:
select * from user7 where name='zs' for update
通過for update子句,這條SQL鎖定了user7表中所有符合檢索條件的記錄(name='zs')。本次事務提交之前,外界無法修改這些記錄。
Hibernate的悲觀鎖,也是基於數據庫的鎖機制實現。
Hibernate的加鎖方法:
Criteria.setLockMode()
Query.setLockMode()
Session.lock()
Session.load()
Session.get()

Hibernate悲觀鎖示例
CREATE TABLE  `sample`.`user7` (
 `id` int(10) unsigned auto_increment,
 `name` varchar(6),
 `account` int(11),
 PRIMARY KEY  (`id`)
)

class Lock extends Thread {
    public void run() {
        Session s = HibernateSessionFactory.getSession();
        Transaction t = s.beginTransaction();
        User7 user = (User7) s.get(User7.class, 1, LockMode.UPGRADE);
        // User7 user = (User7) s.get(User7.class, 1);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(user.getAccount());
        user.setAccount(user.getAccount() - 100);
        s.update(user);
        System.out.println(user.getAccount());
        t.commit();
        HibernateSessionFactory.closeSession();
    }
}

樂觀鎖
相對悲觀鎖而言,樂觀鎖機制採取了更加寬鬆的加載機制。悲觀鎖大多數情況下依靠數據庫的鎖機制實現,以保證操作最大程度的獨佔性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。
樂觀鎖,大多是基於數據版本(version)記錄機制實現。何謂數據版本?即爲數據增加一個版本標識,在基於數據庫表的版本解決方案中,一般是通過爲數據庫表增加一個“version”字段來實現。
讀取出數據時,將此版本號一同讀出,之後更新時,對此版本號加1。此時,將提交數據的版本數據與數據表對應記錄的當前版本信息進行比對,如果提交際的數據版本號大於數據庫表當前版本號,則予以更新,否則認爲是過期數據。
Hibernate在其數據訪問引擎中內置了樂觀鎖實現。如果不用考慮外部系統對數據的更新操作,利用Hibernate提供的透明化樂觀鎖實現,將大大提升我們的生產力。

Hibernate樂觀鎖示例
創建數據表
CREATE TABLE  `sample`.`user8` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `name` varchar(45) NOT NULL default '',
  `account` int(11) NOT NULL default '0',
  `version` int(11) NOT NULL default '0',
  PRIMARY KEY  (`id`)
)

實體類
package fire;

public class User8 implements java.io.Serializable {
    private Integer id;
    private Integer version;
    private String name;
    private Integer account;
    public User8() {
    }
    public User8(Integer id, String name, Integer account) {
        this.id = id;
        this.name = name;
        this.account = account;
    }
    ……
}

Hibernate映射文件
<hibernate-mapping>
  <class name="fire.User8" table="user8" catalog="sample"
            optimistic-lock="version">
    <id name="id" type="java.lang.Integer">
      <column name="id" />
      <generator class="assigned" />
    </id>
    <version name="version" type="java.lang.Integer">
      <column name="version" not-null="true" />
    </version>
    <property name="name" type="java.lang.String">
      <column name="name" length="45" not-null="true" />
    </property>
    <property name="account" type="java.lang.Integer">
      <column name="account" not-null="true" />
    </property>
  </class>
</hibernate-mapping>

Hibernate配置文件
<hibernate-configuration>
     <session-factory>
    <property name="connection.username">root</property>
    <property name="connection.url">
        jdbc:mysql://localhost:3306/sample
    </property>
    <property name="dialect">
        org.hibernate.dialect.MySQLDialect
    </property>
    <property name="connection.password">root</property>
    <property name="connection.driver_class">
        com.mysql.jdbc.Driver
    </property>
    <property name="show_sql">true</property>
    <mapping resource="fire/User8.hbm.xml" />
     </session-factory>
</hibernate-configuration>

測試類
class Lock extends Thread{
    public void run(){
        Session s = HibernateSessionFactory.getSession();
        Transaction t = s.beginTransaction();
        User8 user = (User8) s.get(User8.class, 1);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(user.getAccount());
        user.setAccount(user.getAccount() - 100);
        s.update(user);
        System.out.println(user.getAccount());
        t.commit();
        HibernateSessionFactory.closeSession();
    }
}

數據緩存
Hibernate數據緩存分爲兩個層次,以Hibernate語義加以區分,可分爲:
1.一級緩存(Session)
一級緩存正常情況下由Hibernate自動維護,如果需要手動干預,我們可以通過以下方法完成:
Session.evict()           Session.clear()
2.二級緩存(SessionFactory)
二級緩存是全局緩存,使用時需要對其進行配置:
<property name="cache.provider_class">
    org.hibernate.cache.HashtableCacheProvider
</property>
<property name="cache.use_query_cache">true</property>
<cache usage="read-write"/>
Query.setCacheable(true)
SessionFactory.evictQueries()

一級緩存示例
Session s=HibernateSessionFactory.getSession();
User7 u1=(User7) s.load(User7.class, Integer.valueOf(1));
System.out.println(u1.getName());
// s.evict(u1);
// s.clear();
User7 u2=(User7) s.load(User7.class, Integer.valueOf(1));
System.out.println(u2.getName());
HibernateSessionFactory.closeSession();

二級緩存示例
Session s1 = HibernateSessionFactory.getSession();
Query q1 = s1.createQuery("from User7");
q1.setCacheable(true);
Iterator it1 = q1.list().iterator();
while (it1.hasNext()) {
    User7 user = (User7) it1.next();
    System.out.println(user.getName());
}
HibernateSessionFactory.closeSession();

Session s2 = HibernateSessionFactory.getSession();
// s2.getSessionFactory().evictQueries();
Query q2 = s2.createQuery("from User7");
q2.setCacheable(true);
Iterator it2 = q2.list().iterator();
while (it2.hasNext()) {
    User7 user = (User7) it2.next();
    System.out.println(user.getName());
}
HibernateSessionFactory.closeSession();



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