Hibernate常用的配置文件
Hibernate配置文件之映射配置文件
1. 映射文件,即Stu.hbm.xml的配置文件
* <class>標籤 -- 用來將類與數據庫表建立映射關係
* name -- 類的全路徑
* table -- 表名.(類名與表名一致,那麼table屬性也可以省略)
* catalog -- 數據庫的名稱,基本上都會省略不寫
* <id>標籤 -- 用來將類中的屬性與表中的主鍵建立映射,id標籤就是用來配置主鍵的。
* name -- 類中屬性名
* column -- 表中的字段名.(如果類中的屬性名與表中的字段名一致,那麼column可以省略.)
* length -- 字段的程度,如果數據庫已經創建好了,那麼length可以不寫。如果沒有創建好,生成表結構時,length最好指定。
* <property> -- 用來將類中的普通屬性與表中的字段建立映射.
* name -- 類中屬性名
* column -- 表中的字段名.(如果類中的屬性名與表中的字段名一致,那麼column可以省略.)
* length -- 數據長度
* type -- 數據類型(一般都不需要編寫,如果寫需要按着規則來編寫)
* Hibernate的數據類型 type="string"
* Java的數據類型 type="java.lang.String"
* 數據庫字段的數據類型 <column name="name" sql-type="varchar"/>
Hibernate配置文件之核心配置文件
1. 核心配置文件的兩種方式
* 第一種方式是屬性文件的形式,即properties的配置文件
* hibernate.properties
* hibernate.connection.driver_class=com.mysql.jdbc.Driver
* 缺點
* 不能加載映射的配置文件,需要手動編寫代碼去加載
* 第二種方式是XML文件的形式,開發基本都會選擇這種方式
* hibernate.cfg.xml
* <property name="hibernate.connection.driver_class" >com.mysql.jdbc.Driver</property>
* 優點
* 格式比較清晰
* 編寫有提示
* 可以在該配置文件中加載映射的配置文件(最主要的)
2. 關於hibernate.cfg.xml的配置文件方式
* 必須有的配置
* 數據庫連接信息:
hibernate.connection.driver_class -- 連接數據庫驅動程序
hibernate.connection.url -- 連接數據庫URL
hibernate.connection.username -- 數據庫用戶名
hibernate.connection.password -- 數據庫密碼
* 方言:
hibernate.dialect -- 操作數據庫方言
* 可選的配置
* hibernate.show_sql -- 顯示SQL
* hibernate.format_sql -- 格式化SQL
* hibernate.hbm2ddl.auto -- 通過映射轉成DDL語句
* create -- 每次都會創建一個新的表.---測試的時候
* create-drop -- 每次都會創建一個新的表,當執行結束之後,將創建的這個表刪除.---測試的時候
* update -- 如果有表,使用原來的表.沒有表,創建一個新的表.同時更新表結構.
* validate -- 如果有表,使用原來的表.同時校驗映射文件與表中字段是否一致如果不一致就會報錯.
* 加載映射
* 如果XML方式:<mapping resource="cn/itcast/hibernate/domain/User.hbm.xml" />
Hibernate常用的接口和類
Configuration類和作用
1. Configuration類
* Configuration對象用於配置並且啓動Hibernate。
* Hibernate應用通過該對象來獲得對象-關係映射文件中的元數據,以及動態配置Hibernate的屬性,然後創建SessionFactory對象。
* 簡單一句話:加載Hibernate的配置文件,可以獲取SessionFactory對象。
2. Configuration類的其他應用(瞭解)
* 加載配置文件的種類,Hibernate支持xml和properties類型的配置文件,在開發中基本都使用XML配置文件的方式。
* 如果採用的是properties的配置文件,那麼通過Configuration configuration = new Configuration();就可以假裝配置文件
* 但是需要自己手動加載映射文件
* 例如:config.addResource("cn/itcast/domain/Student.hbm.xml");
* 如果採用的XML的配置文件,通過Configuration configuration = new Configuration().configure();加載配置文件
SessionFactory:重要
1. 是工廠類,是生成Session對象的工廠類
2. SessionFactory類的特點
* 由Configuration通過加載配置文件創建該對象。
* SessionFactory對象中保存了當前的數據庫配置信息和所有映射關係以及預定義的SQL語句。同時,SessionFactory還負責維護Hibernate的二級緩存。
* 預定義SQL語句
* 使用Configuration類創建了SessionFactory對象是,已經在SessionFacotry對象中緩存了一些SQL語句
* 常見的SQL語句是增刪改查(通過主鍵來查詢)
* 這樣做的目的是效率更高
* 一個SessionFactory實例對應一個數據庫,應用從該對象中獲得Session實例。
* SessionFactory是線程安全的,意味着它的一個實例可以被應用的多個線程共享。
* SessionFactory是重量級的,意味着不能隨意創建或銷燬它的實例。如果只訪問一個數據庫,只需要創建一個SessionFactory實例,且在應用初始化的時候完成。
* SessionFactory需要一個較大的緩存,用來存放預定義的SQL語句及實體的映射信息。另外可以配置一個緩存插件,這個插件被稱之爲Hibernate的二級緩存,被多線程所共享
3. 總結
* 一般應用使用一個SessionFactory,最好是應用啓動時就完成初始化。
Session接口
1. 概述
* Session是在Hibernate中使用最頻繁的接口。也被稱之爲持久化管理器。它提供了和持久化有關的操作,比如添加、修改、刪除、加載和查詢實體對象
* Session 是應用程序與數據庫之間交互操作的一個單線程對象,是 Hibernate 運作的中心
* Session是線程不安全的
* 所有持久化對象必須在 session 的管理下纔可以進行持久化操作
* Session 對象有一個一級緩存,顯式執行 flush 之前,所有的持久化操作的數據都緩存在 session 對象處
* 持久化類與 Session 關聯起來後就具有了持久化的能力
2. 特點
* 不是線程安全的。應避免多個線程使用同一個Session實例
* Session是輕量級的,它的創建和銷燬不會消耗太多的資源。應爲每次客戶請求分配獨立的Session實例
* Session有一個緩存,被稱之爲Hibernate的一級緩存。每個Session實例都有自己的緩存
3. 常用的方法
* save(obj)
* delete(obj)
* get(Class,id)
* update(obj)
* saveOrUpdate(obj) -- 保存或者修改(如果沒有數據,保存數據。如果有,修改數據)
* createQuery() -- HQL語句的查詢的方式
Transaction接口
1. Transaction是事務的接口
2. 常用的方法
* commit() -- 提交事務
* rollback() -- 回滾事務
3. 特點
* Hibernate框架默認情況下事務不自動提交.需要手動提交事務
* 如果沒有開啓事務,那麼每個Session的操作,都相當於一個獨立的事務
Hibernate的持久化類
什麼是持久化類
1. 持久化類:就是一個Java類(咱們編寫的JavaBean),這個Java類與表建立了映射關係就可以成爲是持久化類。
* 持久化類 = JavaBean + xxx.hbm.xml
持久化類的編寫規則
1. 提供一個無參數 public訪問控制符的構造器 -- 底層需要進行反射.
2. 提供一個標識屬性,映射數據表主鍵字段 -- 唯一標識OID.數據庫中通過主鍵.Java對象通過地址確定對象.持久化類通過唯一標識OID確定記錄
3. 所有屬性提供public訪問控制符的 set或者get 方法
4. 標識屬性應儘量使用基本數據類型的包裝類型
區分自然主鍵和代理主鍵
1. 創建表的時候
* 自然主鍵:對象本身的一個屬性.創建一個人員表,每個人都有一個身份證號.(唯一的)使用身份證號作爲表的主鍵.自然主鍵.(開發中不會使用這種方式)
* 代理主鍵:不是對象本身的一個屬性.創建一個人員表,爲每個人員單獨創建一個字段.用這個字段作爲主鍵.代理主鍵.(開發中推薦使用這種方式)
2. 創建表的時候儘量使用代理主鍵創建表
主鍵的生成策略
1. increment:適用於short,int,long作爲主鍵.不是使用的數據庫自動增長機制.
* Hibernate中提供的一種增長機制.
* 先進行查詢 :select max(id) from user;
* 再進行插入 :獲得最大值+1作爲新的記錄的主鍵.
* 問題:不能在集羣環境下或者有併發訪問的情況下使用.
2. identity:適用於short,int,long作爲主鍵。但是這個必須使用在有自動增長數據庫中.採用的是數據庫底層的自動增長機制.
* 底層使用的是數據庫的自動增長(auto_increment).像Oracle數據庫沒有自動增長.
3. sequence:適用於short,int,long作爲主鍵.底層使用的是序列的增長方式.
* Oracle數據庫底層沒有自動增長,想自動增長需要使用序列.
4. uuid:適用於char,varchar類型的作爲主鍵.
* 使用隨機的字符串作爲主鍵.
5. native:本地策略.根據底層的數據庫不同,自動選擇適用於該種數據庫的生成策略.(short,int,long)
* 如果底層使用的MySQL數據庫:相當於identity.
* 如果底層使用Oracle數據庫:相當於sequence.
6. assigned:主鍵的生成不用Hibernate管理了.必須手動設置主鍵.
Hibernate持久化對象
持久化對象的狀態
1. Hibernate的持久化類
* 持久化類:Java類與數據庫的某個表建立了映射關係.這個類就稱爲是持久化類.
* 持久化類 = Java類 + hbm的配置文件
2. Hibernate的持久化類的狀態
* Hibernate爲了管理持久化類:將持久化類分成了三個狀態
* 瞬時態:Transient Object
* 沒有持久化標識OID, 沒有被納入到Session對象的管理.
* 持久態:Persistent Object
* 有持久化標識OID,已經被納入到Session對象的管理.
* 脫管態:Detached Object
* 有持久化標識OID,沒有被納入到Session對象的管理.
Hibernate持久化對象的狀態的轉換
1. 瞬時態 -- 沒有持久化標識OID, 沒有被納入到Session對象的管理
* 獲得瞬時態的對象
* User user = new User()
* 瞬時態對象轉換持久態
* save()/saveOrUpdate();
* 瞬時態對象轉換成脫管態
* user.setId(1)
2. 持久態 -- 有持久化標識OID,已經被納入到Session對象的管理
* 獲得持久態的對象
* get()/load();
* 持久態轉換成瞬時態對象
* delete(); --- 比較有爭議的,進入特殊的狀態(刪除態:Hibernate中不建議使用的)
* 持久態對象轉成脫管態對象
* session的close()/evict()/clear();
3. 脫管態 -- 有持久化標識OID,沒有被納入到Session對象的管理
* 獲得託管態對象:不建議直接獲得脫管態的對象.
* User user = new User();
* user.setId(1);
* 脫管態對象轉換成持久態對象
* update();/saveOrUpdate()/lock();
* 脫管態對象轉換成瞬時態對象
* user.setId(null);
4. 注意:持久態對象有自動更新數據庫的能力!!!
Hibernate對象的一級緩存
Session對象的一級緩存(重點)
1. 什麼是緩存?
* 其實就是一塊內存空間,將數據源(數據庫或者文件)中的數據存放到緩存中.再次獲取的時候 ,直接從緩存中獲取.可以提升程序的性能!
2. Hibernate框架提供了兩種緩存
* 一級緩存 -- 自帶的不可卸載的.一級緩存的生命週期與session一致.一級緩存稱爲session級別的緩存.
* 二級緩存 -- 默認沒有開啓,需要手動配置纔可以使用的.二級緩存可以在多個session中共享數據,二級緩存稱爲是sessionFactory級別的緩存.
3. Session對象的緩存概述
* Session接口中,有一系列的java的集合,這些java集合構成了Session級別的緩存(一級緩存).將對象存入到一級緩存中,session沒有結束生命週期,那麼對象在session中存放着
* 內存中包含Session實例 --> Session的緩存(一些集合) --> 集合中包含的是緩存對象!
4. 證明一級緩存的存在,編寫查詢的代碼即可證明
* 在同一個Session對象中兩次查詢,可以證明使用了緩存
5. Hibernate框架是如何做到數據發生變化時進行同步操作的呢?
* 使用get方法查詢User對象
* 然後設置User對象的一個屬性,注意:沒有做update操作。發現,數據庫中的記錄也改變了。
* 利用快照機制來完成的(SnapShot)
控制Session的一級緩存(瞭解)
1. 學習Session接口中與一級緩存相關的方法
* Session.clear() -- 清空緩存。
* Session.evict(Object entity) -- 從一級緩存中清除指定的實體對象。
* Session.flush() -- 刷出緩存
Hibernate中的事務與併發
事務的相關概念(面試題)
1. 什麼是事務
* 事務就是邏輯上的一組操作,組成事務的各個執行單元,操作要麼全都成功,要麼全都失敗.
* 轉賬的例子:冠希給美美轉錢,扣錢,加錢。兩個操作組成了一個事情!
2. 事務的特性
* 原子性 -- 事務不可分割.
* 一致性 -- 事務執行的前後數據的完整性保持一致.
* 隔離性 -- 一個事務執行的過程中,不應該受到其他的事務的干擾.
* 持久性 -- 事務一旦提交,數據就永久保持到數據庫中.
3. 如果不考慮隔離性:引發一些讀的問題
* 髒讀 -- 一個事務讀到了另一個事務未提交的數據.
* 不可重複讀 -- 一個事務讀到了另一個事務已經提交的update數據,導致多次查詢結果不一致.
* 虛讀 -- 一個事務讀到了另一個事務已經提交的insert數據,導致多次查詢結構不一致.
4. 通過設置數據庫的隔離級別來解決上述讀的問題
* 未提交讀:以上的讀的問題都有可能發生.
* 已提交讀:避免髒讀,但是不可重複讀,虛讀都有可能發生.
* 可重複讀:避免髒讀,不可重複讀.但是虛讀是有可能發生.
* 串行化:以上讀的情況都可以避免.
5. 如果想在Hibernate的框架中來設置隔離級別,需要在hibernate.cfg.xml的配置文件中通過標籤來配置
* 通過:hibernate.connection.isolation = 4 來配置
* 取值
* 1—Read uncommitted isolation
* 2—Read committed isolation
* 4—Repeatable read isolation
* 8—Serializable isolation
丟失更新的問題(面試題)
1. 如果不考慮隔離性,也會產生寫入數據的問題,這一類的問題叫丟失更新的問題。
2. 例如:兩個事務同時對某一條記錄做修改,就會引發丟失更新的問題。
* A事務和B事務同時獲取到一條數據,同時再做修改
* 如果A事務修改完成後,提交了事務
* B事務修改完成後,不管是提交還是回滾,如果不做處理,都會對數據產生影響
3. 解決方案有兩種
* 悲觀鎖
* 採用的是數據庫提供的一種鎖機制,如果採用做了這種機制,在SQL語句的後面添加 for update 子句
* 當A事務在操作該條記錄時,會把該條記錄鎖起來,其他事務是不能操作這條記錄的。
* 只有當A事務提交後,鎖釋放了,其他事務才能操作該條記錄
* 樂觀鎖
* 採用版本號的機制來解決的。會給表結構添加一個字段version=0,默認值是0
* 當A事務在操作完該條記錄,提交事務時,會先檢查版本號,如果發生版本號的值相同時,纔可以提交事務。同時會更新版本號version=1.
* 當B事務操作完該條記錄時,提交事務時,會先檢查版本號,如果發現版本不同時,程序會出現錯誤。
4. 使用Hibernate框架解決丟失更新的問題
* 悲觀鎖
* 使用session.get(Customer.class, 1,LockMode.UPGRADE); 方法
* 樂觀鎖
* 1.在對應的JavaBean中添加一個屬性,名稱可以是任意的。例如:private Integer version; 提供get和set方法
* 2.在映射的配置文件中,提供<version name="version"/>標籤即可。
綁定本地的Session
1. 之前在講JavaWEB的事務的時候,需要在業務層使用Connection來開啓事務,
* 一種是通過參數的方式傳遞下去
* 另一種是把Connection綁定到ThreadLocal對象中
2. 現在的Hibernate框架中,使用session對象開啓事務,所以需要來傳遞session對象,框架提供了ThreadLocal的方式
* 需要在hibernate.cfg.xml的配置文件中提供配置
* <property name="hibernate.current_session_context_class">thread</property>
* 使用SessionFactory的getCurrentSession()方法,獲取當前的Session對象。並且該Session對象不用手動關閉,線程結束了,會自動關閉。
public static Session getCurrentSession(){
return factory.getCurrentSession();
}
* 注意:想使用getCurrentSession()方法,必須要先配置才能使用。