一、Hibernate簡介(網上搜的,理解性地看看) 1.概念:Hibernate是持久層(數據訪問層)的框架,對JDBC進行了封裝,是對資料庫訪問提出的面向對象的解決方案。 2.作用:使用Hibernate可以直接訪問對象,Hibernate自動將訪問轉換成SQL執行,從而實現簡介訪問資料庫的目的 ...
一、Hibernate簡介(網上搜的,理解性地看看)
1.概念:Hibernate是持久層(數據訪問層)的框架,對JDBC進行了封裝,是對資料庫訪問提出的面向對象的解決方案。
2.作用:使用Hibernate可以直接訪問對象,Hibernate自動將訪問轉換成SQL執行,從而實現簡介訪問資料庫的目的,簡化了數據訪問層的代碼開發。
3.JDBC、MyBatis對比:
a)JDBC需要編寫大量SQL語句,以及對大量參數賦值。需要手動將ResultSet結果集轉換成實體對象;SQL中包含特有函數,無法移植。而Hibernate可以自動生成SQL和參數賦值,自動將ResultSet結果集轉換成實體對象,採用一致的方法對資料庫操作,移植性好。
b)MyBatis與Hibernate都對JDBC進行了封裝,採用ORM思想解決了Entity和資料庫的映射問題。MyBatis採用SQL與Entity映射,對JDBC封裝程度比較輕,需要自己寫SQL,更具有靈活性;而Hibernate採用資料庫與Entity映射,對JDBC封裝程度比較重,自動生成SQL,對於基本的操作,開發效率高。
4.原理:Hibernate框架是ORM思想的一種實現,解決了對象和資料庫映射問題。我們可以通過Hibernate提供的一系列API,允許我們直接訪問實體對象,然後其根據ORM映射關係,轉換成SQL並且執行,從而達到訪問資料庫的目的。
ORM:Object Relation Mapping,即對象關係映射,指Java對象和關係資料庫之間的映射。
ORM思想:將對象與資料庫數據進行相互轉換的思想,不同的框架實現ORM的手段不同,但更多的是採用配置+反射的方式來實現ORM。
5.框架體繫結構
a)主配置文件,通常為“hibernate.cfg.xml”,用於配置資料庫連接參數,框架參數,已經映射關係文件。
b)實體類,與資料庫對應的Java類型,用於封裝資料庫記錄的對象類型。
c)映射關係文件,通常為“實體類.hbm.xml”,並放置在與實體類相同的路徑下。該文件是指定實體類和資料庫的對應關係,以及類中屬性和表中欄位之間的對應關係。
d)底層API,對映射關係文件的解析,根據解析出來的內容,動態生成SQL語句,自動將屬性和欄位映射。
二、Hibernate使用
1.常用API
Configuration:負責載入主配置文件信息,同時載入映射關係文件信息
/** 到src下找到名稱為hibernate.cfg.xml的配置文件,創建對象,把配置文件放到對象中(載入核心配置文件) */ Configuration cfg = new Configuration().configure(); //載入指定的核心配置文件 Configuration cfg = new Configuration().configure("com/konrad/hibernate.cfg.xml"); //載入指定的映射配置文件 cfg.addResource("com/konrad/entity/User.hbm.xml");
SessionFactory:負責創建Session對象,根據核心配置文件的配置,在資料庫創建對應的表,一個項目只應有一個此對象
private static final SessionFactory sessionFactory; static{ try{ //配置文件的方式 sessionFactory = new Configuration().configure("hibernate.cfg.xml") .buildSessionFactory(); /* 註解的方式 sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); */ }catch (Throwable ex){ ex.printStackTrace(); throw new ExceptionInInitializerError(ex); } }
Session:資料庫連接會話,負責執行增刪改操作
Transaction:負責事務控制
Query:負責執行特殊查詢
2.使用步驟
a)導入Hibernate包,以及資料庫驅動包。(開發需要的包不知道的自行百度,也可以通過Maven構建)
b)引入Hibernate主配置文件hibernate.cfg.xml
- <session-factory>標簽要寫在<hibernate-configuration>標簽內部
c)創建實體類
d)創建映射關係文件(也可以通過註解的方式進行映射,這樣就不需要xml映射文件)
- <class>標簽中的name屬性寫的是類的全路徑
- <id>和<property>標簽中的name屬性寫的是實體類中的屬性名稱
- <id>和<property>標簽中,column可以省略,若省略就是以name屬性值生成表的欄位名
- <property>標簽中還有一個type屬性,用於生成表的欄位的類型,但是使用比較少
e)使用Hibernate API執行增刪改查等操作
f)額外說明:註解方式的應用
- 如果實體類屬性名與表欄位名稱不同時,要麼都註解在屬性前,要麼都註解在get方法前。
- 如果實體類屬性名和表欄位名稱統一,可以部分註解在屬性前,部分註解在get方法前。
- 若都不註解,則預設表欄位名和屬性名一致
- 若實體類中某個屬性不需要存進資料庫表,使用@Transient進行註解即可
- 表名稱可以在實體類前進行註解
- 所有註解都在javax.persistence包下
3.映射類型
a)Java類型:映射關係文件中,配置屬性和欄位關係時,可以在type屬性上指定Java類型,用於做Java屬性和資料庫欄位的轉換。指定時需要完整的類型名,如java.lang.String。
b)自定義類型:當某些特殊類型,Java預置類型無法支持,需要自定義一個類來實現,這個類要求實現介面UserType。比如boolean類型,資料庫中一般存char(1),存y/n或者t/f,Java預置類型無法支持boolean類型的配置,需要自定義。
c)Hibernate也提供了一些類型來支持這些映射,提供了7中映射類型,書寫時全是小寫。
三、Hibernate的主鍵生成方式
1.sequence:採用序列生成主鍵,適用於Oracle資料庫。
<generator class="sequence"> <param name="sequence">序列名</param> </generator>
2.identity:採用資料庫自增長機制生成主鍵,適用於Oracle之外的其他資料庫。
<generator class="identity"> </generator>
3.native:根據當前配置的資料庫方言,自動選擇sequence或者identity。
<generator class="native"> <param name="sequence">序列名</param> </generator>
4.increment:不是採用資料庫自身的機制來生成主鍵,而是Hibernate提供的一種生成主鍵的方式,它會獲取當前表中主鍵的最大值,然後加1作為新的主鍵。PS:這種方式在併發量高時存在問題,可能會產生重覆的主鍵,因此不推薦。
<generator class="increment"> </generator>
5.assigned:Hibernate不負責生成主鍵,需要程式員自己處理主鍵的生成。
<generator class="assigned"> </generator>
6.uuid/hilo:採用uuid或hilo演算法生成一個主鍵值,這個主鍵值是一個不規則的長數字。PS:這個方式生成的主鍵可以保證不重覆,但是沒有規律,因此不能按主鍵排序。
<generator class="uuid"> </generator>
四、一些使用的代碼實例
1.核心配置文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 配置資料庫信息 必須的 --> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/test</property> <property name="connection.username">root</property> <property name="connection.password"></property> <property name="javax.persistence.validation.mode">none</property> <!-- 支持mysql方言 --> <property name="dialect">org.hibernate.dialect.SQLServerDialect</property> <property name="current_session_context_class">thread</property> <!-- 配置是否在控制台顯示sql語句 --> <property name="show_sql">true</property> <!-- 配置是否按照格式顯示sql語句 --> <property name="format_sql">true</property> <!-- 配置生成策略 update:如果沒有表創建之,有表就更新表 --> <property name="hbm2ddl.auto">update</property> <!-- 引入映射配置文件 必須的 -->
<mapping resource="com/maven/test/hibernate/entity/PersonEntity.hbm.xml"/>
<!-- 若使用註解的方式,需要如下配置 --> <!-- <mapping class="com.maven.test.hibernate.entity.PersonEntity">-->
</session-factory> </hibernate-configuration>
2.關係映射XML文件dtd約束:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
3.HibernateUtil類:提供獲取Session和關閉Session的方法。Hibernate中我們使用ThreadLocal管理Session。
package com.maven.test.hibernate.util; import org.apache.log4j.Logger; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { public static final ThreadLocal<Session> SESSIONMAP = new ThreadLocal<Session>(); private static final SessionFactory sessionFactory; private static final Logger LOGGER = Logger.getLogger(HibernateUtil.class); static{ try{ LOGGER.debug("HibernateUtil.static - loading config"); sessionFactory = new Configuration().configure("hibernate.cfg.xml") .buildSessionFactory(); LOGGER.debug("HibernateUtil.static - end"); }catch (Throwable ex){ ex.printStackTrace(); LOGGER.error("HibernateUtil error: ExceptionInInitializerError"); throw new ExceptionInInitializerError(ex); } } private HibernateUtil(){} public static Session getSession() throws HibernateException{ Session session = SESSIONMAP.get(); if(session == null){ session = sessionFactory.openSession(); SESSIONMAP.set(session); } return session; } public static void closeSession() throws HibernateException{ Session session = SESSIONMAP.get(); SESSIONMAP.set(null); if(session != null) session.close(); } }
4.添加操作
PersonEntity person = new PersonEntity(); person.setId(100); person.setName("Konrad"); Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); session.save(person); tx.commit(); HibernateUtil.closeSession();
5.刪除操作
Session session = HibernateUtil.getSession(); session.beginTransaction(); PersonEntity person = session.get(PersonEntity,class, 1); session.delete(person); session.getTransaction().commit(); HibernateUtil.closeSession();
6.查詢操作
Session session = HibernateUtil.getSession(); session.beginTransaction(); //單行查詢,根據主鍵查詢 PersonEntity p = session.get(PersonEntity.class, 1) //多行查詢 @SuppressWarnings("unchecked") List<PersonEntity> personList = session.createQuery("select p from PersonEntity p").list(); for(PersonEntity person : personList){ System.out.println(person); } session.getTransaction().commit(); HibernateUtil.closeSession();
7.修改操作
Session session = HibernateUtil.getSession(); session.beginTransaction(); PersonEntity person = session.get(PersonEntity.class, 1); person.setName("haha"); session.update(person); session.getTransaction().commit(); HibernateUtil.closeSession();
五、Hibernate進階
1.一級緩存
a)Hibernate創建每個Session對象時,都會給該Session對象分配一塊獨立的緩存區,用於存放該Session查詢出來的對象,這個分配給Session的緩存區稱之為一級緩存,也叫Session級緩存。Session的save、update、delete操作會觸發緩存更新。
b)使用一級緩存的原因是,Session取數據時,會優先向緩存區取數據,如果存在數據則直接返回,不存在才會去資料庫查詢,從而降低了資料庫訪問次數,提升代碼性能。
c)一級緩存是預設開啟的,使用Hibernate API查詢時會自動使用。
d)session.evict(obj) - 將obj從一級緩存中移除
session.clear() - 清除一級緩存中所有的obj
session.close() - 關閉session,釋放緩存空間
2.Hibernate中,實體對象的3中狀態:臨時態、持久態、游離態
a)臨時態:臨時態的對象可以被垃圾回收,未進行持久化,未與session關聯
- 通過new創建的對象為臨時態
- 通過delete方法操作的對象將轉變為臨時態
b)持久態:持久態對象垃圾回收器不能回收,進行了持久化,與session關聯。實際上持久態對象存在於session緩存中,由session負責管理;持久態對象的數據可以自動更新到資料庫中,在調用session.flush()時執行,而提交事務時會使用session.flush(),因此提交事務也會觸發同步
- 通過get、load、list、iterate方法查詢到的對象為持久態
- 通過save、update方法操作的對象轉變為持久態
c)游離態:游離態的對象可以被垃圾回收,進行過持久化,但已與session解除了關聯
- 通過session的evict、clear、close方法操作的對象會轉變為游離態
3.延遲載入
a)使用某些Hibernate方法查詢數據時,Hibernate返回的只是一個空對象(除id外屬性都為null),並沒有真正查詢資料庫。而在使用這個對象時才會觸發查詢資料庫,並將查詢到的數據註入到這個空對象中,這種將查詢時機制推遲到對象訪問時的機制稱之為延遲載入。
b)延遲載入可以提升記憶體資源的使用率,降低對資料庫的訪問次數。
c)session.load()、query.iterate()、關聯映射中對關聯屬性的載入,以上屬於採用延遲載入的方法。
d)採用具有延遲載入機制的操作,需要避免session提前關閉。
4.關聯映射
a)關聯映射即使用Hibernate操作一張表時,它可以通過配置關係自動地幫助我們操作另一張表
b)關聯查詢出關係表的數據、關聯新增/修改關係表的數據、關聯刪除關係表的數據
c)類型:一對多關聯、多對一關聯、多對多關聯、一對一關聯、繼承關聯
一對多關聯實現步驟:
- 在“一”方實體類添加集合屬性,以及get、set方法
- 在“一”方hbm文件中配置關聯關係
<!-- set指定屬性類型為Set集合 name指定屬性名 --> <set name="courses"> <!-- column指定了關聯欄位名--> <key column="stu_id"/> <!-- one-to-many指定了關聯關係,class指定了另一方類型--> <one-to-many class="com.konrad.entity.Course" /> </set>
多對一關聯實現步驟:
- 在“多”方實體類添加屬性,以及get、set方法
- 在“多”方hbm文件中配置關聯關係
<!-- many-to-one指定了關聯關係, name指定了屬性名, column指定了關係欄位, class指定了另一方類型--> <many-to-one name="student" column="stu_id" class="com.konrad.entity.Student" />
多對多關聯實現步驟:
- 在雙方方實體類添加集合屬性,以及get、set方法
- 在雙方hbm文件中配置關聯關係
Student映射配置文件
<!-- 配置學生對應的教師集合 name屬性:配置教師實體類中的Set集合屬性的名稱 table屬性:表示中間表的名稱 --> <set name="teachers" table="t_stu_tea"> <!-- column屬性:配置當前映射文件在中間表中外鍵名稱 --> <key column="sid"></key> <!-- class屬性:配置另一個張表的實體類全路徑名稱 colunm屬性:配置一張表在中間表中的外鍵名稱 --> <many-to-many class="cn.konrad.entity.Teacher" column="tid"></many-to-many> </set>
Teacher映射配置文件 <!-- 配置教師對應的學生集合 name屬性:配置學生實體類中的Set集合屬性的名稱 table屬性:中間表的名稱 --> <set name="students" table="t_stu_tea"> <!-- column屬性:配置當前映射文件在中間表中外鍵名稱 --> <key column="tid"></key> <!-- class屬性:配置另一個張表的實體類全路徑名稱 colunm屬性:配置一張表在中間表中的外鍵名稱 --> <many-to-many class="cn.konrad.entity.Student" column="sid"></many-to-many> </set>
一對一關聯實現步驟:
- 在雙方方實體類添加屬性,以及get、set方法
- 在雙方hbm文件中配置關聯關係
Wife映射關係文件
<!-- property-ref屬性:指定使用被關聯實體主鍵以外的欄位作為關聯欄位 --> <one-to-one name="husband" class="cn.konrad.entity.Husband" property-ref="wife"></one-to-one>
Husband映射配置文件 <id name="hid" column="hid"> <!-- 2.1配置主鍵的策略 foreign屬性:表示主鍵參照外鍵生成 --> <generator class="foreign"> <!-- param標簽配置當前實體類中引用的對方實體對象的引用名 --> <param name="property">wife</param> </generator> </id> <!-- 3.配置實體類與表的其他屬性 name屬性:實體類的屬性名稱 column屬性:表中的列名 Contrained=true表示生產外鍵 --> <property name="hname" column="hname"></property> <one-to-one name="wife" class="cn.konrad.entity.Wife" constrained="true"></one-to-one>
5.關聯操作
a)預設情況下,關聯屬性時採用延遲載入機制載入的,可以通過映射關係文件中關聯屬性配置標簽中的lazy屬性進行修改,true/false。
b)通過一個連接查詢一次性取出2張表的數據,避免2次查詢。在關聯屬性標簽上通過fetch屬性進行設置,稱之為抓取策略。“join”表示查詢時使用連接查詢,一起把對方數據抓取過來;“select”表示查詢時不使用連接查詢,是預設情況。當fetch為“join”時,關聯屬性的延遲載入失效。
6.級聯操作:通過關聯映射,在對一方進行增刪改時,連帶增刪改關聯的另一方數據
a)實現級聯添加/修改,需要在映射關係文件中的關聯屬性標簽中,通過cascade屬性進行設置,cascade="save-update"
b)實現級聯刪除,cascade="delete"
c)若想級聯添加、修改、刪除一起支持,cascade="all"
d)控制反轉,在一對多關聯中,使用級聯新增、刪除時,當前操作的“一”方會試圖維護關聯欄位,然而關聯欄位是在“多”方對象中,它會自動維護這個欄位,因此“一”方沒必要做這樣的處理。在關聯屬性標簽上通過inverse屬性(true/false)交出控制權,預設是false,不控制反轉。
7.Hibernate查詢
a)HQL按條件查詢:條件中寫的是屬性名,在執行查詢前調用query對象為條件參數賦值
String hql = "from Course where name=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setString("math"); List<Course> courses = query.list();
b)HQL查詢部分欄位:可以只查詢表中的一部分欄位,需要在from之前追加select語句,指定要查詢列對應的屬性名
String hql = "select id,name" + " from Course";
註意:查詢部分欄位時,query.list()方法返回的集合中封裝的不是實體對象,而是一個Object[],數組中的值與select語句後面的屬性按順序對應。
c)分頁查詢:通過API統一實現
int from = (page - 1) * pageSize; query.setFirstResult(from); query.setMaxResults(pageSize);
註意:查詢的起點是本頁第一行,按照JDBC計算,公式為(page -1) * pageSize +1;Hibernate中行數的起點是0,不同於JDBC是從1開始,所以要在上面公式的基礎上-1,即(page -1) * pageSize
d)查詢總頁數:根據以下hql查詢總行數,再計算總頁數
String hql = "select count(*) from Student";
e)多表聯合查詢:可以使用HQL進行多表聯合查詢,不過HQL中寫的是關聯的對象的屬性名;有3中關聯查詢的方式:
對象方式關聯
String hql = "select s.id, s.name, c.name from Student s, Course c " + "where s.course.id = c.id";
join方式關聯(不能直接join對象,需要join關聯屬性)
String hql = "select s.id, s.name, c.name from Student s inner join s.courses c";
select子句關聯
f)直接使用SQL查詢
String sql = "select * from t_course where c_name=?"; Session session = HibernateUtil.getSession(); SQLQuery query = session.createSQLQuery(sql); query.setString(0,"math"); List<Object[]> list = query.list(); //返回集合封裝的是Object[] //若想返回集合中封裝實體對象 query.addEntity(Course.class); List<Course> list = query.list();
g)使用Criteria查詢
Criteria c = session.createCriteria(Course.class); c.add(Restriction.eq("name", "math")).add(Restrictions.or(Restrictions.eq(), Restrications.eq())); List<Course> list = c.list();
8.二級緩存
a)二級緩存類似於一級緩存,可以緩存對象,但它是SessionFactory級別的緩存,有SessionFactory負責管理。因此二級緩存的數據是Session間共用的,不同的Session對象都可以共用二級緩存中的數據。
b)二級緩存適用於:對象數據頻繁共用,數據變化頻率低
c)二級緩存使用步驟:
導入ehcache.jar
在src下添加緩存配置文件ehcache.xml
<ehcache> <!--緩存到硬碟時的緩存路徑,java.io.tmpdir表示系統預設緩存路徑--> <diskStore path="java.io.tmpdir"/> <!--預設緩存配置 maxElementsInMemory:二級緩存可容納最大對象數 eternal:是否保持二級緩存中對象不變 timeToIdleSeconds:允許對象空閑的時間,即對象最後一次訪問起,超過該時間即失效 timeToLiveSeconds:允許對象存活的時間,即對象創建起,超過該時間即失效 overflowToDisk:記憶體不足,是否允許使用硬碟緩存,寫入路徑參考diskStore --> <defaultCache> maxElementsInMemory = "300" eternal = "false" timeToIdleSeconds = "120" timeToLiveSeconds = "300" overflowToDisk = "true" /> <!--自定義配置--> <cache name="myCache"> maxElementsInMemory = "2000" eternal = "false" timeToIdleSeconds = "200" timeToLiveSeconds = "300" overflowToDisk = "true" /> </ehcache>
在hibernate.cfg.xml中開啟二級緩存,指定採用的二級緩存驅動類
<property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property>
在要緩存的對象對應的映射關係文件中,開啟當前對象的二級緩存支持,並指定緩存策略
<!--開啟二級緩存,並指定緩存策略 可以用region屬性指定自定義的緩存設置--> <cache usage="read-only" />
d)緩存策略:
-只讀型(read-only):緩存不會更新,適用於不會發生改變的數據,效率最高,事務隔離級別最低
-讀寫型(read-write):緩存會在數據變化時更新,適用於變化的數據
-不嚴格讀寫型(nonstrict-read-write):緩存不定期更新,適用於變化頻率低的數據
-事務型(transactional):緩存會在數據變化時更新,並且支持事務。效率最低,事務隔離界別最高。
<!-- 配置事務的隔離級別 1 -- Read uncommitted isolation 2 -- Read committed isolation 3 -- Repeatable read isolation 4 -- Serializable isolation --> <property name="hibernate.connection.isolation">2</property>
9.查詢緩存
a)查詢緩存依賴於二級緩存,可以理解為特殊的二級緩存,也是SessionFactory級別的,也是由SessionFactory負責維護
b)查詢緩存可以緩存任何查詢到的結果
c)查詢緩存是以hql為key,緩存該hql查詢到的整個結果。如果執行2次同樣的hql,第二次執行可以從查詢緩存中取到第一次查詢緩存的內容
d)使用查詢緩存步驟:
開啟二級緩存
在hibernate.cfg.xml中,開啟查詢緩存
<!--開啟查詢緩存--> <property name="hibernate.cache.use_query_cache">true</property>
在查詢代碼執行前,指定開啟查詢緩存
query.setCacheable(true); //開啟查詢緩存
整理到此為止,有不足的歡迎指正討論。。。☺