Hibernate核心類和接口
Configuration類
- 負責管理Hibernate的配置信息
- 讀取hibernate.cfg.xml文件
- 加載hibernate.cfg.xml配置文件中配置的驅動,url,用戶名,密碼,連接池
- 管理 *.hbm.xml對象關係文件
hibernate.cfg.xml文件
- 該文件用於指定各個參數,是hibernate的核心文件
- 默認放在src目錄下,也可以放在別的目錄下
- 指定連接數據庫的驅動、用戶名、密碼、url、連接池
- 指定對象關係映射文件的位置
- 也可以使用hibernate.properties文件來代替該文件(推薦使用hibernate.cfg.xml)
對象關係映射文件(*.hbm.xml)
- 該文件主要作用是建立表和類的映射關係,是不可或缺的重要文件
- 一般放在其映射的類的同一個目錄下,但不是必須的
- 命名方式一般是:類名.hbm.xml,但不是必須的
- 示意圖如下:
SessionFactory(會話工廠)接口
- 緩存SQL語句和某些數據(Session級緩存)
- 在應用程序初始化的時候創建,是一個重量級的類(吃內存),一般用單例模式保證一個應用中只需要一個SessionFactory實例
- 如果某個應用訪問多個數據庫,則要創建多個會話工廠實例,一般是一個數據庫對應一個會話工廠實例
- 通過SessionFactory接口可以獲得Session(會話)實例
Session(會話)接口
- Session一個實例代表與數據庫的一次操作(當然一次操作可以使CRUD組合)
- Session實例通過SessionFactory獲取,用完需要關閉
- Session是線程不同步的(不安全),因此要保證在同一個線程中使用,可以用getCurrentSession()
- Session可以看做是持久化管理器,它是與持久化操作相關的接口
這裏討論一下通過SessionFactory獲取Session的兩個方法,openSession()
和getCurrentSession()
的區別:
(1) 採用getCurrentSession()
創建的Session會綁定到當前線程中,而採用openSession()
創建的Session則不會
(2) 採用getCurrentSession()
創建的Session在commit或rollback時會自動關閉,而採用openSession()
創建的Session必須手動關閉。
(3) 如果是通過getCurrentSession()
獲取Session,進行查詢也需要通過事務提交
(4) 如果希望使用getCurrentSession()
,我們需要在hibernate.cfg.xml文件中加入如下配置:
(1)如果使用的是本地事務(jdbc事務)
<property name="current_session_context_class">thread</property>
(2)如果使用的是全局事務(jta事務)
<property name="current_session_context_class">jta</property>
本地事務:針對一個數據庫的事務
全局事務:跨數據庫的事務(比如銀行的跨行轉賬)
openSession()和getCurrentSession()究竟使用哪一個?原則是:
如果需要在同一線程中,保證使用同一個Session,則使用getCurrentSession()
如果在一個線程中,需要使用不同的Session,則使用openSession()
這裏我們可以改進我們的入門案例,增加HibernateUtil
類,使用ThreadLocal
(線程局部變量模式)將Session
與線程關聯起來。
ackage com.gavin.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static SessionFactory sessionFactory = null;
// 用ThreadLocal模式(線程局部變量模式)管理Session
private static ThreadLocal<Session> threadLocal = new ThreadLocal<>();
private HibernateUtil(){}
static{
sessionFactory = new Configuration().configure().buildSessionFactory();
}
/**
* 獲取全新的Session
* @return
*/
public static Session openSession(){
return sessionFactory.openSession();
}
/**
* 獲取和線程關聯的session
* @return
*/
public static Session getCurrentSession(){
Session session = threadLocal.get();
if (session == null) {
session = sessionFactory.openSession();
// 把session對象設置到threadLocal
// 相當於該session已經和線程綁定
threadLocal.set(session);
}
return session;
}
}
openSession()和getCurrentSession()深入討論
在SessionFactory啓動的時候,Hibernate會根據配置創建相應的CurrentSessionContext,在getCurrentSession()
被調用的時候,實際執行的方法是CurrentSessionContext.currentSession()
。在currentSession執行時,如果當前Session爲空,currentSession會調用SessionFactory的openSession()
。
Session接口的幾個重要方法:
(1) 保存一個對象(記錄):——save方法
(2) 刪除一個對象(記錄):——delete方法
(3) 查詢一個對象(記錄):——get/load方法
(4) 修改一個對象(記錄):——update方法
get()和load()方法的區別:
(1) get()
方法直接返回實體類,如果查不到數據則返回null
,load()
會返回一個實體代理對象(當前這個對象可以自動轉化爲實體對象),但當代理對象被調用時,如果數據不存在,就會拋出個org.hibernate.ObjectNotFoundException
異常
(2) load()
方法先到緩存(session緩存/二級緩存)中去查,如果沒有則返回一個代理對象(不馬上到DB中去找),等後面使用這個代理對象操作的時候,纔到DB中查詢,這就是我們常說的load在默認情況下支持延遲加載(lazy)
(3) get()
先到緩存(session緩存/二級緩存)中去查,如果沒有就到DB中去查(即馬上發出sql語句)。總之,如果你確定DB中有這個對象就用load()
,不確定就用get()(這樣效率高)
(4) 通過修改配置文件我們可以取消懶加載。–> 在對象關係映射文件中的class結點配置:lazy=”false”
Hibernate緩存的原理
Hibernate緩存分兩級。通過緩存可以有效減少對數據庫的頻繁查詢,提高性能。
Transaction(事務)接口
這裏簡單說明一些什麼是事務:事務簡單的說,就是一組對數據庫的操作集合,它們要麼全部成功,要麼全部失敗,這個保證數據的一致性,事務具有原子性。
- Transaction是底層的事務實現中抽象出來的接口
- 可能是一個JDBC或者JTA事務,這樣有利於Hibernate在不同執行環境的移植
- Hibernate要求顯示的調用事務(如果僅僅是查詢則可以不調用)
Query接口
Query接口類型的對象可以對數據庫操作,它可以使用HQL,QBC,QBE和原生SQL對數據庫操作。官方推薦使用HQL語句。
舉例:通過用戶名查詢數據:
public static void queryByName(String name){
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
// 獲取Query引用[這裏的Employee不是指的表,而是domain類名]
Query query = session.createQuery("from Employee where name = '" + name + "'");
// 通過list方法獲取結果,這個list會自動地將結果封裝成對應的domain對象類型
List<Employee> list = query.list();
list.forEach(e -> System.out.println("e = " + e));
transaction.commit();
}catch (Exception e){
if (transaction != null) {
transaction.rollback();
}
throw new RuntimeException(e);
}finally {
if (session != null && session.isOpen()) {
session.close();
}
}
}
Criteria接口
Criteria接口也可用於面向對象方式的查詢,關於它的具體用法這裏先不做介紹。簡單看幾個案例:
- 最簡單案例,返回50條記錄
Criteria crit = session.createCriteria(Cat.class);
crit.setMaxResults(50);
List cats = crit.list();
- 限制結果集內容:
List cats = session.createCriteria(Cat.class).add(Restrictions.like("name", "Fritz%")).add(Restrictions.between("weight", "minWeight", "maxWeight")).lsit();
- 測試代碼如下:
public static void testCriteria(){
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
Criteria criteria = session.createCriteria(Employee.class).setMaxResults(2).addOrder(Order.desc("id"));
List<Employee> list = criteria.list();
list.forEach(e -> System.out.println("e = " + e));
transaction.commit();
}catch (Exception e){
if (transaction != null) {
transaction.rollback();
}
throw new RuntimeException(e);
}finally {
if (session != null && session.isOpen()) {
session.close();
}
}
}