java框架之Hibernate(2)-持久化類&主鍵生成策略&緩存&事務&查詢

持久化類

概述

持久化:將內存中的對象持久化到數據庫中的過程就是持久化。Hibernate 就是用來進行持久化的框架。html

持久化類:一個 Java 對象與數據庫的表創建了映射關係,那麼這個類在 Hibernate 中稱爲是持久化類。java

也能夠看做:持久化類 = JavaBean + 映射文件

編寫規則

一、對持久化類提供一個無參構造方法。sql

Hibernate 底層使用反射生成實例。

二、屬性須要私有,對私有屬性提供 public 的 get 和 set 方法。數據庫

Hibernate 底層獲取和設置對象的值就是經過 get 和 set 方法。

三、對持久化類提供一個惟一標識 OID 與數據庫表主鍵對應。數組

Java 中經過對象的地址區分是不是同一個對象,數據庫中經過主鍵區分是不是同一條記錄,而在 Hibernate 中是經過持久化類的 OID 屬性區分是不是同一個對象。緩存

四、持久化類中屬性儘可能使用包裝類類型。session

由於包裝類類型默認值爲 null ,基本數據類型有默認值,如 int 默認爲 0 。假若有一個 int 類型的 score 字段用來記錄分數,爲 null 時表示缺考,此時 int 就不支持了。

五、持久化類不要用 final 修飾。框架

延遲加載是 Hibernate 的一個優化手段,而它的原理是經過 javassist 返回一個代理對象,而 javassist 是經過底層字節碼繼承持久化類對持久化類實現產生代理,使用 final 描述的持久化類就不能繼承,延遲加載也就會失效。

對象的三種狀態

Hibernate 是持久層的框架,經過持久化類完成 ORM 操做。爲了更好的管理,Hibernate 將持久化類對象分爲了三種狀態。ide

  • 瞬時態

    沒有惟一標識 OID,且沒有被 session 管理。工具

  • 遊離(託管)態

    有惟一標示 OID,且沒有被 session 管理。

  • 持久態

    有惟一標識 OID,且被 session 管理。

Session session1 = HibernateUtil.openSession();
Transaction transaction1 = session1.beginTransaction();
Customer customer = new Customer(); // 瞬時態:無惟一標識,未被 session1 管理。
customer.setCust_id(1L); // 未被 session1 管理,若是數據庫數據有與之對應的惟一標示(即數據庫表中有一行 cust_id=1 的數據),則爲遊離態,不然依舊是瞬時態。
customer.setCust_name("李四");
session1.saveOrUpdate(customer); // 交由 session1 管理,且數據庫數據有對應惟一標示的數據,轉爲持久態。
transaction1.commit();
session1.close();

Session session2 = HibernateUtil.openSession();
Transaction transaction2 = session2.beginTransaction();
System.out.println(customer); // 以前託管到的 session1 已關閉,即未被 session1 也未被 session2 管理,但有與數據庫數據對應的惟一標識,此時爲遊離態。
session2.delete(customer); // session2 主動放棄管理,且從數據庫中刪除與惟一標示對應的記錄,轉爲瞬時態。
transaction2.commit();
session2.close();

/*
總結:
    瞬時態對象
        得到:
            Customer customer = new Customer();
        狀態轉換:
            ->持久態:
                session.save(customer); 、 session.saveOrUpdate(customer);
            ->遊離態:
                customer.setCust_id(1L);
    遊離態對象
        得到:
            Customer customer = new Customer();customer.setCust_id(1L);
        狀態轉換:
            ->持久態
                session.update(customer); 、 session.saveOrUpdate(customer);
            ->瞬時態:
                customer.setCust_id(null);
    持久態對象
        得到:
            session.get(Customer.class,1L); 、 session.load(Customer.class,1L);
        狀態轉換:
            ->瞬時態:
                session.delete(customer);
            ->遊離態:
                session.close(); 、 session.clear(); 、 session1.evict(customer);
*/
代碼演示狀態轉換:瞬時->遊離->持久->遊離->瞬時

主鍵生成策略

主鍵的分類

  • 天然主鍵

    主鍵自己就是表中有意義的字段。

    好比有一我的員表,每一個人都會有一個身份證號(惟一不可重複),此時使用身份證號做爲主鍵,這個主鍵就稱爲是天然主鍵。
  • 代理主鍵

    主鍵自己不是表中必須的字段。

    若有一我的員表,每一個人都會有一個身份證號(惟一不可重複),但此時使用一個與這張表邏輯不相關的 PID 字段做爲主鍵,這種主鍵就稱爲是代理主鍵。
在實際開發中,儘可能使用代理主鍵:

一旦天然主鍵參與到業務邏輯中,後期就有可能修改源代碼。

好的程序設計知足 OCP 原則,對程序的擴展是 open 的,對修改源碼是 close 的。

主鍵的生成策略

  • increment

    hibernate 提供的自動增加機制,使用 short 、int 、long 類型的主鍵,在單線程程序中使用。

    發送一條 sql :  select max(id) from 查詢最大 id,而後用 id+1 做爲下一條記錄的主鍵。
  • identity

    適用 short 、int 、long 類型的主鍵,使用的是數據庫底層的自增機制,適用於有自增機制的數據庫(MySQL、MSSQL)。

  • sequence

    適用 short 、int 、long 類型的主鍵,採用的是序列的方式,適用於支持序列機制的數據庫(Oracle)。

  • uuid

    適用於字符串類型主鍵,由 Hibernate 隨機生成字符串主鍵。

  • native

    本地策略,能夠在 identity 和 sequence 間自動切換。

  • assigned

    Hibernate 放棄主鍵的管理,經過手動編碼給主鍵賦值。

  • foreign

    依賴外部主鍵,適用於一對一關聯映射狀況下使用。

緩存

緩存概述

一種優化的方式,將數據放入內存中,使用的時候直接從內存中取,不用經過存儲源。

Hibernate 提供了兩種緩存機制:一級緩存、二級緩存。

Hibernate的一級緩存

是 Session 級別的緩存,一級緩存生命週期與 Session 一致,由 Session 中一系列 Java 集合構成,自帶不可卸載。

Hibernate 的一級緩存就是指 Session 緩存,Session 緩存是一塊內存空間,用來存放管理的持久化類對象,在使用 Hibernate 查詢對象時,首先會使用對象屬性的惟一標示 OID 值在一級緩存中進行查找,若是找到匹配的 OID 值的對象,就直接將該對象從一級緩存中取出使用,不會再查詢數據庫;若是沒有找到相同 OID 值的對象,則會去數據庫中查找相應數據。當從數據庫中查詢到所需數據時,該數據信息也會放置到一級緩存中。

在 Session 接口的實現中包含一系列 Java 集合,這些 Java 集合構成了 Session 緩存。只要 Session 實例未結束生命週期,存放在它緩存中的持久化類對象也不會結束生命週期。因此一級緩存也被稱爲 Session 級別的緩存。

Hibernate 的一級緩存的做用就是減小對數據庫訪問的次數。
  • 證實一級緩存的存在

    Session session = HibernateUtil.openSession();
    Customer customer1 = session.get(Customer.class, 1L);// 發出 select 的 SQL 語句
    Customer customer2 = session.get(Customer.class, 1L);// 無 SQL 發出,只是取出上一行查詢出的放到一級緩存的 customer1 賦值給 customer2
    System.out.println(customer1 == customer2);// true :即 customer1 和 customer2 是同一個對象
    session.close();
    查詢時會將持久化類對象放入一級緩存
    Session session = HibernateUtil.openSession();
    Transaction transaction = session.beginTransaction();
    Customer customer1 = new Customer();
    customer1.setCust_name("郭德綱");
    Serializable id = session.save(customer1); // 在事務提交時發出 insert 的 SQL 語句,且將保存的對象放入一份到一級緩存中
    Customer customer2 = session.get(Customer.class, id); // 無 SQL 語句發出,只是取上一行新增時保存到一級緩存中的 customer1 賦值給 customer2
    System.out.println(customer1 == customer2);// true :即 customer1 和 customer2 是同一個對象
    transaction.commit();
    session.close();
    新增時會將持久化類對象放入一級緩存
    Session session = HibernateUtil.openSession();
    Transaction transaction = session.beginTransaction();
    Customer customer1 = new Customer();
    customer1.setCust_id(1L);
    customer1.setCust_name("郭德綱");
    session.update(customer1); // 在事務提交時發出 update 的 SQL 語句,且將要更新的對象放入一份到一級緩存中
    Customer customer2 = session.get(Customer.class, 1L); // 無 SQL 語句發出,只是取上一行更新時保存到一級緩存中的 customer1 賦值給 customer2
    System.out.println(customer1 == customer2);// true :即 customer1 和 customer2 是同一個對象
    transaction.commit();
    session.close();
    更新時會將持久化類對象放入一級緩存
  • 特殊區域:快照區

    當 Hibernate 將持久化類對象放入一級緩存時,並不只僅只是放入一份,還拷貝了一份放入了一級緩存中的一個特殊區域-快照區。而 Hibernate 利用這個快照區實現了持久化類對象的一個特性,看以下示例:

    Session session = HibernateUtil.openSession();
    Customer customer = session.get(Customer.class, 1L);
    Transaction transaction = session.beginTransaction();
    customer.setCust_name("郭德綱");
    // session.update(customer); // 即使省略這個更新操做,在事務提交時 Hibernate 仍然會發出更新的 SQL 語句。
    transaction.commit();
    session.close();
    例:

    當咱們使持久化類對象的屬性發生改變時,一級緩存中對應的持久化對象也會隨之發生改變,而快照區對應的持久化對象不變。而當事務提交時,Hibernate 會對比一級緩存中存放的持久化類對象和它對應快照中存放的持久化類對象。若是有差別,Hibernate 則幫咱們執行更新操做;若是沒有差別,則不會對數據庫進行操做。

事務

事務回顧

點擊查看

Hibernate中設置事務隔離級別

在覈心配置文件 hibernate.cfg.xml 中添加以下屬性便可:

<!--
配置事務隔離級別,有以下四個值:
    1 :讀未提交 (Read uncommitted)
    2 :讀已提交 (Read committed)
    4 :可重複讀 (Repeatable read) 默認級別
    8 :串行化 (Serializable)
-->
<property name="hibernate.connection.isolation">4</property>

綁定Session到當前線程

在覈心配置文件 hibernate.cfg.xml 中添加以下屬性:

<!--
thread : Session 對象的生命週期與本地線程綁定。
jta : Session 對象的生命週期與 JTA 事務綁定。
managed : Hibernate 委託程序來管理 Session 對象的生命週期。
-->
<property name="hibernate.current_session_context_class">thread</property>

而後就能夠保證在當前線程中經過 org.hibernate.SessionFactory.getCurrentSession 方法獲取到的 Session 是同一個實例。抽取工具類:

package com.zze.util;


import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    public static final Configuration cfg;
    public static final SessionFactory sf;

    static {
        cfg = new Configuration().configure();
        sf = cfg.buildSessionFactory();
    }

    public static Session openSession() {
        return sf.openSession();
    }

    public static Session getCurrentSession() {
        return sf.getCurrentSession();
    }
}
com.zze.util.HibernateUtil
Hibernate 內部是經過 ThreadLocal 來實現線程綁定 Session 的。

幾種查詢方式

HQL

HQL (Hibernate Query Language) ,相對 sql 來講,sql 中的表名在 hql 中用類名替代,sql 中的列名在 hql 中用屬性名替代。

Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
String hql = "from Customer"; // 簡單查詢
Query query = currentSession.createQuery(hql);
List<Customer> customerList = query.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
transaction.commit();
簡單查詢
Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
String hql = "from Customer where cust_name like ?";
Query query = currentSession.createQuery(hql);
query.setParameter(0, "張%");
List<Customer> customerList = query.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
transaction.commit();
條件查詢
Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
String hql = "from Customer";
Query query = currentSession.createQuery(hql);
query.setFirstResult(2); // 起始索引,從 0 開始
query.setMaxResults(2); // 每頁條數
List<Customer> customerList = query.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
transaction.commit();
分頁查詢

QBC

QBC (Query By Criteria) API 提供了檢索對象的另外一種方式,它主要由 Criteria 接口、Criterion 接口和 Expresson 類組成,它支持在運行時動態生成查詢語句,是一種更面向對象的查詢方式。

Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
Criteria criteria = currentSession.createCriteria(Customer.class);
List<Customer> customerList = criteria.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
transaction.commit();
簡單查詢
Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
Criteria criteria = currentSession.createCriteria(Customer.class);
criteria.add(Restrictions.like("cust_name", "張", MatchMode.END));
List<Customer> customerList = criteria.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
transaction.commit();
條件查詢
Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
Criteria criteria = currentSession.createCriteria(Customer.class);
criteria.setFirstResult(2);
criteria.setMaxResults(2);
List<Customer> customerList = criteria.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
transaction.commit();
分頁查詢

SQL

Hibernate 也支持咱們使用原生 SQL 查詢。

Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
SQLQuery sqlQuery = currentSession.createSQLQuery("select * from customer");
// 默認返回一個 Object 數組對象的列表,數組的每一項對應數據庫中每一行數據的一列
List<Object[]> customerList = sqlQuery.list();
for (Object[] customer : customerList) {
    System.out.println(String.format("id:%s,cust_name:%s", customer[0], customer[1]));
}
transaction.commit();
簡單查詢:返回 List<Object[]>
Session session = HibernateUtil.getCurrentSession();
session.beginTransaction();
String sql = "select * from customer where cust_name like ?";
SQLQuery sqlQuery = session.createSQLQuery(sql);
sqlQuery.addEntity(Customer.class);
sqlQuery.setParameter(0,"張%");
List<Customer> customerList = sqlQuery.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
session.getTransaction().commit();
條件查詢:返回 List<持久化類>
Session session = HibernateUtil.getCurrentSession();
session.beginTransaction();
String sql = "select cust_id id,cust_name name from customer";
SQLQuery sqlQuery = session.createSQLQuery(sql);
sqlQuery.addScalar("id", new LongType());
sqlQuery.addScalar("name");
sqlQuery.setResultTransformer(Transformers.aliasToBean(Customer2.class));
List<Customer2> userList = (List<Customer2>)sqlQuery.list();
for (Customer2 customer2 : userList) {
    System.out.println(customer2);
}
session.getTransaction().commit();
簡單查詢:返回 List<指定類型>
相關文章
相關標籤/搜索