1 什麼是Hibernate
Hibernate是一個開放源代碼的對象關係映射框架,它對JDBC進行了非常輕量級的對象封裝,使得Java程序員可以隨心所欲的使用對象編程思維來操縱數據庫。 Hibernate可以應用在任何使用JDBC的場合
,既可以在Java的客戶端程序使用,也可以在Servlet/JSP的Web應用中使用,最具革命意義的是,Hibernate可以在應用EJB的J2EE架構中取代CMP,完成數據持久化的重任
2 Hibernate的核心類和接口:
<1>Configuration
負責管理Hibernate的配置信息,這些配置信息都是從配置文件hibernate.cfg.xml或者
Hibernate.properties讀取的,當然也可以自定義文件名稱,只要在實例化Configuration
的時候指定具體的路徑就可以了
<2>SessionFactory
Configuration的實例會根據當前的配置信息,構造SessionFactory實例。
SessionFactory是線程安全的,一般情況下一個應用中一個數據庫共享一個SessionFactory實例。
<3>Session
一般的持久化方法(CRUD)都是通過Session來調用的,Session是非線程安全的
<4>Transaction
事務管理
<5>Query
查詢的接口
3 Hibernate的原理及步驟
1. 讀取並解析配置文件
//採用默認的hibernate.cfg.xml來啓動一個Configuration的實例
例:Configuration configuration=new Configuration().configure();
//如要修改默認配置文件名 當然你可以選擇不修改
Configuration configuration=new Configuration().configure(new File("src/xxx.xml"));
2. 讀取並解析映射信息,創建SessionFactory
例:SessionFactory sessionFactory = configuration.buildSessionFactory();
3. 打開Session實例(它是線程不安全的,是共享同一個session,所以要ThreadLocal模式來控制)
例:Session session = sessionFactory.openSession();
4. 創建事務Transaction
例:Transaction transaction = (Transaction) session.beginTransaction();
5. CRUD操作(執行數據操作 CRUD 增刪查改)
例: 增加
session.save(pojo對象);
增加或者修改
session.saveOrOpdate(pojo對象)//當修改時候,id不存在將被拋出異常
刪除
session.delete(pojo對象)
//根據主鍵加載
p1=(Person)session.load(pojo類.class, 主鍵值);
6. 提交事務
例:transaction.commit();
7. 關閉Session實例及SessionFactory
例: session.close();
sessionFactory.close();
4.Session的管理
Session是Hibernate運作的中心,對象的生命週期、事務的管理、數據庫的存取,都與 Session息息相關,
就如同在編寫JDBC時需關心Connection的管理,以有效的方法創建、利用與回收Connection,以減少資源的消耗,
增加系統執行效能一樣,有效的Session管理,也是Hibernate應用時需關注的焦點。
Session是由SessionFactory所創建, SessionFactory是線程安全的(Thread-Safe),您可以讓多個
線程同時存取SessionFactory而不會有數據共享的問題,然而Session則不是設計爲線程安全的,所以試圖讓
多個線程共享一個 Session,將會發生數據共享而發生混亂的問題。
在各種Session 管理方案中, ThreadLocal 模式得到了大量使用。ThreadLocal 是Java中一種較爲特殊的
線程綁定機制。通過ThreadLocal存取的數據,總是與當前線程相關,也就是說,JVM 爲每個運行的線程,
綁定了私有的本地實例存取空間,從而爲多線程環境常出現的併發訪問問題提供了一種隔離機制。
首先,我們需要知道,SessionFactory負責創建Session,SessionFactory是線程安全的,多個併發線程
可以同時訪問一個SessionFactory 並從中獲取Session 實例。而Session並非線程安全,也就是說,如果
多個線程同時使 用一個Session實例進行數據存取,則將會導致Session 數據存取邏輯混亂
5 Hibernate三種查詢方式 1 hql 2 qbc《qbe --- query by example》 3 sql
1.hql
1.1 冒號法
Query query=session.createQuery(" from Person p where name=:xxx");//from後面跟的是PO對象名,而不是表名
query.setString("xxx", "ttt");
List<Person> list=query.list();
for(Person p:list)
System.out.println(p.getName())
1.2 問號法
Query query=session.createQuery(" from Person p where name=?");
query.setString(0, "ttt");
List<Person> list=query.list();
for(Person p:list)
System.out.println(p.getName())
模糊查詢 舉例
問號法
Query query=session.createQuery(" from Person p where name like ?");
query.setString(0, "%a%");
冒號法
Query query=session.createQuery(" from Person p where name like :xxx");
query.setString("xxx", "%w%");
2.QBC ( query by Criteria )
Criteria接口與DetachedCriteria(二者的區別就是DetachedCriteria可以離線操作,而Criteria不可以)
<1>利用Restrictions進行條件篩選
方法 說明
Restrictions.eq 等於
Restrictions.allEq 使用Map,使用key/value進行多個等於的比對
Restrictions.gt 大於 >
Restrictions.ge 大於等於 >=
Restrictions.lt 小於 <
Restrictions.le 小於等於 <=
Restrictions.between 對應SQL的BETWEEN子句
Restrictions.like 對應SQL的LIKE子句
Restrictions.in 對應SQL的in子句
Restrictions.and and關係
Restrictions.or or關係
Restrictions.sqlRestriction SQL限定查詢
<2>criteria.addOrder(Order.asc("id"));//排序
舉例1
Criteria criteria = session.createCriteria(Person.class);
criteria.add(Restrictions.like("name", "ad%")); //模糊查詢,姓名以ad開頭,注意要加%號
criteria.add(Restrictions.between("age", 20, 30)); //年齡在20到30之間
criteria.addOrder(Order.asc("id"));//
List<Person> list = criteria.list();
for (Person p : list) {
System.out.println(p);
}
上面是與查詢 ,如果是或查詢 查詢等於20或者或者年齡爲空的記錄
Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.or(
Restrictions.eq("age", new Integer(20)),
Restrictions.isNull("age")
));
List users = criteria.list();
DetachedCriteria 使用 //?離線操作的意義何在?
DetachedCriteria dc = DetachedCriteria.forClass(Person.class);
dc.add(Restrictions.eq("name","aaa"));
List<Person> list = dc.getExecutableCriteria(session).list();
for (Person p : list) {
System.out.println(p.getId());
}
3.Sql
<3.1>採用addScalar方法 一個字段一個字段指定類型
SQLQuery sQLQuery = session.createSQLQuery("select * from user"); //普通的SQL語句
sQLQuery.addScalar("id",Hibernate.INTEGER);
sQLQuery.addScalar("name",Hibernate.STRING);
List<Object[]> list = sQLQuery.list(); //注意,得到的是一個裏面放的是object數組的List集合
for (Object[] object : list) {
System.out.println(Arrays.toString(object)); //在這裏可以根據自己的需要,進行封裝
}
<3.2>採用addEntity方法 如果想返回的是list裏是person對象
SQLQuery sQLQuery = session.createSQLQuery("select {person.*} from person"); //注意這裏的語法{person.*}
sQLQuery.addEntity("person", Person.class);
sQLQuery.addScalar("id",Hibernate.INTEGER);
sQLQuery.addScalar("name",Hibernate.STRING);
List<Person> list = sQLQuery.list();
for (Person object : list) {
System.out.println(object);
}
總結:不管是SQL還是HQL,當你查詢出來的不是整個對象,而是其中的某些字段,或者說:當幾個表關聯查詢的時候,取二個表中的某些字段,
返回的不是一個封裝好的對象的List集合,那麼這個List裏到底是什麼東西呢,這裏分爲兩種情況
<1>如果只有一個字段,list裏每個元素都是Object
<2>如果多於一個字段,list裏每個元素都是Object[], 所以,這時候就需要自己去封裝對象了,將object數組中的元素迭代出來,
然後封裝到一個對象裏。
三種查詢方式優缺點
HQL功能最強大,適合各種情況,但是動態條件查詢構造起來很不方便
Criteria最適合動態條件查詢,不太適合統計查詢,QBE還不夠強大,只適合相當簡單的查詢
NativeSQL可以實現特定數據庫的SQL,但是可移植性就犧牲了
利用 Query接口的setMaxResults方法 表示一次從list裏拿多少條記錄 通常用於分頁中
6.pojo的3種狀態
瞬態 持久化 託管(離線)
一個PO有三種狀態:
1、未被持久化的VO
此時就是一個內存對象VO,由JVM管理生命週期
2、已被持久化的PO,並且在Session生命週期內
此時映射數據庫數據,由數據庫管理生命週期
3、曾被持久化過,但現在和Session已經detached了,以VO的身份在運行
這種和Session已經detached的PO還能夠進入另一個Session,繼續進行PO狀態管理,此時它就成爲PO的第二種狀態了。這種PO實際上是跨了Session進行了狀態維護的。 在傳統的JDO1.x中,PO只有前面兩種狀態,一個PO一旦脫離PM,就喪失了狀態了,不再和數據庫數據關聯,成爲一個純粹的內存VO,它即使進入一個新的PM,也不能恢復它的狀態了
POJO<Plain old java Object>, PO<Persistance Object>
POJO<Plain old java Object>:一種純粹的java,即private屬性及對這個屬性的get/set方法,不實現,不繼承任何java框架的接口,類。
PO<Persistance Object>:持久對象,,
有時也被稱爲Data對象,對應數據庫中的entity,可以簡單認爲一個PO對應數據庫中的一條記錄。
在hibernate持久化框架中與insert/delet操作密切相關。
PO中不應該包含任何對數據庫的操作。
可以是由POJO發展而來
POJO(plain old java object)-------->PO(persistance Object)
POJO是沒有狀態的,加了狀態之後就變成了PO(POJO+xml就是PO);
7 HIbernate主鍵詳解
1.Assigned
Assigned方式由程序生成主鍵值,並且要在save()之前指定否則會拋出異常
特點:主鍵的生成值完全由用戶決定,與底層數據庫無關。用戶需要維護主鍵值,
在調用session.save()之前要指定主鍵值。
主鍵由外部程序負責生成,無需Hibernate參與。
2.Hilo
Hilo使用高低位算法生成主鍵,高低位算法使用一個高位值和一個低位值,
然後把算法得到的兩個值拼接起來作爲數據庫中的唯一主鍵。
Hilo方式需要額外的數據庫表和字段提供高位值來源。
默認請況下使用的表是通過hi/lo 算法實現的主鍵生成機制,
需要額外的數據庫表保存主鍵生成歷史狀態。
3.hibernate_unique_key
默認字段叫作next_hi。next_hi必須有一條記錄否則會出現錯誤。
特點:需要額外的數據庫表的支持,能保證同一個數據庫中主鍵的唯一性,
但不能保證多個數據庫之間主鍵的唯一性。Hilo主鍵生成方式由Hibernate維護,
所以Hilo方式與底層數據庫無關,但不應該手動修改hi/lo算法使用的表的值,
否則會引起主鍵重複的異常。
4.Increment
Increment方式對主鍵值採取自動增長的方式生成新的主鍵值,但要求底層數據庫的支持Sequence。
如Oracle,DB2等。需要在映射文件xxx.hbm.xml中加入Increment標誌符的設置。
特點:由Hibernate本身維護,適用於所有的數據庫,不適合多進程併發更新數據庫,
適合單一進程訪問數據庫。不能用於羣集環境。
主鍵按數值順序遞增。此方式的實現機制爲在當前應用實例中維持一個變量,以保存着當前的最大值,
之後每次需要生成主鍵的時候將此值加1作爲主鍵。
這種方式可能產生的問題是:如果當前有多個實例訪問同一個數據庫,那麼由於各個實例各自維護
主鍵狀態,不同實例可能生成同樣的主鍵,從而造成主鍵重複異常。因此,如果同一數據庫有多個
實例訪問,此方式必須避免使用。
5.Identity
Identity當時根據底層數據庫,來支持自動增長,不同的數據庫用不同的主鍵增長方式。
特點:與底層數據庫有關,要求數據庫支持Identity,如MySQl中是auto_increment, SQL Server
中是Identity,支持的數據庫有MySql、SQL Server、DB2、Sybase和HypersonicSQL。
Identity無需Hibernate和用戶的干涉,使用較爲方便,但不便於在不同的數據庫之間移植程序。
採用數據庫提供的主鍵生成機制。如DB2、SQL Server、MySQL中的主鍵生成機制。
6.Sequence
Sequence需要底層數據庫支持Sequence方式,例如Oracle數據庫等
特點:需要底層數據庫的支持序列,支持序列的數據庫有DB2、PostgreSql、Qracle、SAPDb等在不
同數據庫之間移植程序,特別從支持序列的數據庫移植到不支持序列的數據庫需要修改配置文件
採用數據庫提供的sequence 機制生成主鍵。如Oralce 中的Sequence。
與hilo 類似,通過hi/lo 算法實現的主鍵生成機制,只是主鍵歷史狀態保存在Sequence中,
適用於支持Sequence的數據庫,如Oracle。
7.Native
Native主鍵生成方式會根據不同的底層數據庫自動選擇Identity、Sequence、Hilo主鍵生成方式
特點:根據不同的底層數據庫採用不同的主鍵生成方式。由於Hibernate會根據底層數據庫採用
不同的映射方式,因此便於程序移植,項目中如果用到多個數據庫時,可以使用這種方式。
由Hibernate根據底層數據庫自行判斷採用identity、hilo、sequence其中一種作爲主鍵生成方式。
8. uuid.hex
UUID使用128位UUID算法生成主鍵,能夠保證網絡環境下的主鍵唯一性,也就能夠保證在不同
數據庫及不同服務器下主鍵的唯一性。
特點;能夠保證數據庫中的主鍵唯一性,生成的主鍵佔用比較多的存貯空間
由Hibernate基於128 位唯一值產生算法生成16 進制數值(編碼後以長度32的字符串表示)作爲主鍵。
9.Foreign GUID
Foreign用於一對一關係中。GUID主鍵生成方式使用了一種特殊算法,保證生成主鍵的唯一性,
支持SQL Server和MySQL
使用外部表的字段作爲主鍵。
一般而言,利用uuid.hex方式生成主鍵將提供最好的性能和數據庫平臺適應性。
10.uuid.string
與uuid.hex 類似,只是生成的主鍵未進行編碼(長度16)。在某些數據庫中可能出現問題(如PostgreSQL)。
8 在Hibernate應用中如何處理批量更新和批量刪除?---executeUpdate()方法支持
Hibernate3.0對批量更新和批量刪除提供了支持,能夠直接執行批量更新或批量刪除語句,無需把被更新或刪除的對象先加載到內存中。以下是通過Hibernate3.0執行批量更新的程序代碼:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
以下是通過Hibernate3.0執行批量刪除的程序代碼:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete Customer where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
9 Hibernate 映射
<1>基本映射
基本屬性映射
<property name="PO屬性名" column="數據庫表列名" type="數據類型" />
<2> 集合映射 知道是如何實現 加一個表利用主鍵關聯
1.List
<list name="list屬性名" table="list對應表名">
<key column="list屬性所在類的id(主鍵" not-null="true"></key>
<list-index column="list_order" />
<element type="list元素類型" column="list元素列名"></element>
</list>
2.Set
<set name="set屬性名" table="set對應表名">
<key column="set屬性所在類的id(主鍵" not-null="true"></key>
<element type="set元素類型" column="set元素列名"></element>
</set>
3.Map
<map name="map屬性名" table="map對應表名">
<key column="map屬性所在類的id(主鍵)" not-null="true"></key>
<map-key type="map的key的數據類型" column="map的key對應的列名"></map-key>
<element type="map的value的數據類型" column="map的value對應的列名"></element>
</map>
<3>關聯映射 重點掌握 一對多 級聯保存,更新 以及刪除
單向映射和雙向映射的區別:
單向:一方持有另一方的實例或集合 我中有你,你中沒有我。也就是說:通過這個表可以查詢到另外一個表數據,但是通過另外一個表,查詢不到這個表的數據
雙向:彼此持有對方的實例或集合 我中有你,你中也有我。也就是說:通過這個表可以查詢到另外一個表的數據,通過另外一個表也可以查詢到這個表的數據
1對1
<1> 外鍵關聯實現
單向: <many-to-one name="對象屬性名" column="字段名(可不寫則與屬性名相同)" unique="true"/>
雙向: <many-to-one name="對象屬性名" column="字段名(可不寫則與屬性名相同)" unique="true"/>
<one-to-one name="對象屬性名" property-ref="另一對象屬性名"/>
<2> 主鍵關聯實現 一對一主鍵關聯映射中,默認了cascade屬性
單向:
<id name="id">
<generator class="foreign">
<param name="property">對象屬性名</param>
</generator>
</id>
<one-to-one name="對象屬性名" constrained="true"/>
雙向:
<id name="id">
<generator class="foreign">
<param name="property">對象屬性名</param>
</generator>
</id>
<one-to-one name="對象屬性名" constrained="true"/>
另一方
<one-to-one name="對象屬性名"/>
1對多
單向:
<set name="set屬性名">
<key column="外鍵列名"/>
<one-to-many class="包名.類名"/>
</set>
雙向:
<set name="set屬性名">
<key column="外鍵列名"/>
<one-to-many class="包名.類名"/>
</set>
<many-to-one name="對象屬性名" column="字段名(可不寫則與屬性名相同"/>
多對多 中間表
單向:
<set name="addresses" table="jointable">
<key column="personId"/>
<many-to-many column="addressId" class="Address"/>
</set>
雙向 在多對多關聯中 下面的兩個方向配置字段一致 如果想不一致 則可以使用不一樣的中間表名,不一致時候會沒有默認值錯誤
<set name="set" table="jointable">
<key column="personid"/>
<many-to-many column="wifeid" class="com.Wife"/>
</set>
<set name="persons" inverse="true" table="jointable">
<key column="wifeid"/>
<many-to-many column="personid" class="Person"/>
</set>
級聯保存,更新 以及刪除 在一對多演示
級聯刪除分爲兩種刪除
<1>只把多方(子表)中的關聯字段設置爲null 當主表爲pojo 也就是瞬時狀態時
<2>多方(子表)記錄刪除掉 當主表爲po 也就是持久化狀態時
<set name="set屬性名" inverse="false" cascade="save-update,delete">
<key column="外鍵列名"/>
<one-to-many class="包名.類名"/>
</set>
cascade(級聯)與inverse(反轉)的作用
Cascade:
用來說明當對主對象進行某種操作時是否對其關聯的從對象也作類似的操作,常用的Cascade取值有:
none,all,save-update,delete,lock,refresh,evict,replicate,persist,merge,
delete-orphan(one-to-many)。
一般以many-to-one,many-to-many不設置級聯,
在one-to-one和one-to-many不設置級聯,
在one-to-one和one-to-many中設置級聯。
說明:
none:表示沒有任何操作級聯(默認值)
All:表示所有的操作都級聯
Save-update:表示在save/update/save-update時產生級聯
Delete:表示刪除時級聯
若要多個操作都產生級聯則:可以用,分開如:save-update,delete
Delete-orphan表示若在從對象中把對應的主對象的值修改爲null則刪除從對象。通常在one-one中使用。
一般以many-to-one,many-to-many不設置級聯,在one-to-one和one-to-many中設置save-update級聯。
Inverse表示:
“是否放棄維護關聯關係”(在Java裏面個對象產生關聯時,對數據庫的影響),
在one-to-many和many-to-many的集合定義中使用,inverse=“true”表示該對象不維護關聯關係;
該屬性的值一般在使用有序集合時設置成false(注意hibernate的缺省值是false).
One-to-many維護關聯關係就是更新外鍵.many-to-many維護關聯關係就是在中間表增減記錄
<4>繼承映射:三種策略
<1>每個類層次映射一個表 (表個數與類個數相同)
<hibernate-mapping>
<class name="test.Sup" table="sup">
<id name="id" column="id" type="java.lang.String">
<generator class="uuid.hex" />
</id>
<property name="name" column="name" type="java.lang.String" />
<joined-subclass name="test.Sub1" table="sub1">
<key column="id" />
<property name="sex" type="java.lang.String" />
</joined-subclass>
<joined-subclass name="test.Sub2" table="sub2">
<key column="id" />
<property name="age" type="java.lang.String" />
</joined-subclass>
</class>
</hibernate-mapping>
<2>每個子類(具體類)映射一個表 (表個數與子類個數相同)
<hibernate-mapping>
<class name="test.Sup" table="sup" abstract="true">
<id name="id" column="id" type="java.lang.String">
<generator class="uuid.hex" />
</id>
<property name="name" column="name" type="java.lang.String" />
<union-subclass name="test.Sub1" table="sub1" >
<property name="sex" type="java.lang.String" />
</union-subclass>
<union-subclass name="test.Sub2" table="sub2">
<property name="age" type="java.lang.Integer" />
</union-subclass>
</class>
</hibernate-mapping>
<3>所有類用一個表映射
<hibernate-mapping>
<class name="test.Sup" table="sup" discriminator-value="sup">
<id name="id" column="id" type="java.lang.String">
<generator class="uuid.hex" />
</id>
<discriminator column="personType" type="java.lang.String" /><!--在id屬性標籤後-->
<property name="name" column="name" type="java.lang.String" />
<subclass name="test.Sub1" discriminator-value="sub1">
<property name="sex" type="java.lang.String" />
</subclass>
<subclass name="test.Sub2" discriminator-value="sub2">
<property name="age" type="java.lang.Integer" />
</subclass>
</class>
</hibernate-mapping>
三種方法的優缺點:
1、每一個具體子類映射成單個數據庫表,而抽象基類不參與映射。
優點:
數據操作實現簡單,每個表中都包含自己所需要的具體子類的所有信息,減少了多表關聯操作時的性能消耗。
缺點:
類的修改會導致相對應的表及其子類所對應表的更改。不支持多態查詢。
應用:
適合在類層次結構上有一定數量的抽象類的情況下使用。
2、將整個類層次映射爲單個數據庫表。
這對於子類屬性不多的情況非常有效。每個子類由識別列(discriminator column)區分。
優點:
實現簡單,並支持多態。同時數據訪問也比較簡單,因爲數據庫表中包含了所有需要的信息。
缺點:
增加類層次中的耦合,類層次中任何類的屬性的增加都有會導致表的變更。另外,對子類屬性的修改錯誤將會影響到整個類的層次結構。當然也浪費了大量的數據庫空間。表中引入區分子類的字段,子類的字段不能創建爲空。
3、繼承關係中每個類均映射爲一個數據庫表
優點:
此時,與面向對象的概念是一致的,這種映射實現策略的最大好處就是關係模型完全標準化,關係模型和領域模型完全一致,易於修改基類和增加新的子類。
缺點:
數據庫中存在大量的表,爲細粒度級的數據模型,訪問數據時將存在大量的關聯表的操作,效率較低。
《5》組件映射
在hibernate中,component是某個實體的邏輯組成部分,它與實體的根本區別是沒有oid,
component可以成爲是值對象(DDD)
採用component映射的好處:它實現了對象模型的細粒度劃分,層次會更分明,複用率會更高
<component name="contact(Javabean屬性名)">
<property name="email"/>
<property name="address"/>
<property name="zipCode"/>
<property name="contactTel"/>
</component>
<6> 組合映射
通常將複合主鍵相關的屬性,單獨放到一個類中
* 此類必須實現序列化接口
* 覆寫hashcode和equals方法
12. Hibernate 緩存機制
12.1 Hibernate緩存概述
緩存是介於物理數據源與應用程序之間,是數據庫數據在內存中的存放臨時copy的容器,
其作用是爲了減少應用程序對物理數據源訪問的次數,從而提高了應用的運行性能。
Hibernate在進行讀取數據的時候,根據緩存機制在相應的緩存中查詢,
如果在緩存中找到了需要的數據(我們把這稱做“緩存命中"),則就直接把命中的數據作爲結果加以利用,
避免了建立數據庫查詢的性能損耗。
簡單介紹完Hibernate的緩存後,再結合Hibernate的獲取數據方式來說明緩存的具體使用方式,
在Hibernate中獲取數據常用的方式主要有四種:Session.load、Session.get、Query.list、Query.iterator。
是否延遲加載
Session.load-------一級緩存-------二級緩存------------------------數據庫
Session.get-------一級緩存-------二級緩存------------------------數據庫
Query.list---------查詢緩存-------------------------------------數據庫-----------------這裏有可能調用Session.load(如果從數據庫出來時id集合)
尋找id集合
Query.iterator ----------數據庫--------session.load
2、Session.get
在執行Session.get時,和Session.load不同的就是在當從緩存中獲取不到時,直接從數據庫中獲取id對應的值。
12.2 Hibernate的緩存分爲:
1、一級緩存(內置緩存)->session級別緩存,是Hibernate 的內置緩存 一級緩存在Hibernate中對應的即爲session範圍的緩存,也就是當session關閉時緩存即被清除,一級緩存在Hibernate中是不可配置的部分
測試一級緩存
1 在同一個session中發出兩次load或者是get查詢
不會發出sql,因爲load使用緩存
2 在同一個session中發出兩次iterate查詢實體對象(查屬性)
2.1 查對象
會發出查詢id的sql,不會發出查詢實體對象的sql,因爲iterate使用緩存
2.2查屬性
iterate查詢普通屬性,一級緩存不會緩存,所以發出sql
一級緩存是緩存實體對象的
3 開啓兩個session中發出load查詢
會發出查詢語句,session間不能共享一級緩存的數據
因爲它會伴隨session的生命週期存在和消亡
4 在同一個session中先save,在發出load查詢save過的數據
//不會發出sql,因爲save是使用緩存的
不會發出sql,因爲save是使用緩存的
2 二級緩存(應用級緩存):
二級緩存也稱進程級的緩存或SessionFactory級的緩存,二級緩存可以被所有的session共享
二級緩存的生命週期和SessionFactory的生命週期一致,SessionFactory可以管理二級緩存 ,二級緩存是緩存實體對象的
什麼樣的數據適合存放到第二級緩存中?《人的登錄信息》
1 很少被修改的數據
2 不是很重要的數據,允許出現偶爾併發的數據
3 不會被併發訪問的數據
4 參考數據,指的是供應用參考的常量數據,它的實例數目有限,它的實例會被許多其類的實例引用,實例極少或者從來不會被修改。
不適合存放到第二級緩存的數據?
1 經常被修改的數據
2 財務數據,絕對不允許出現併發
3 與其他應用共享的數據。
.開啓二級緩存的步驟:
二級緩存的配置和使用:
* 開啓二級緩存,修改hibernate.cfg.xml文件
<property name="hibernate.cache.use_second_level_cache">true</property>
* 指定緩存產品提供商,修改hibernate.cfg.xml文件
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
* 指定那些實體類使用二級緩存(兩種方法)
* 在映射文件中採用<cache>標籤
<cache usage="read-only"/>
usage="read-only"是“只讀”緩存策略。這個<cache>標籤只能放在<class>標籤的內部,而且必須處在<id>標籤的前面
這個<cache>標籤放在哪些<class>標籤下面,就說明會多這些類的對象進行緩存
* 在hibernate.cfg.xml文件中,採用<class-cache>標籤
<class-cache class="com.bjsxt.hibernate.Classes" usage="read-only" /> 注意,這個<class-cache>標籤必須放在<mapping>標籤的後面。
測試二級緩存
1 開啓兩個session,分別調用load或者get
//不會發出sql,因爲開啓了二級緩存,session是共享二級緩存的
2 開啓兩個session,分別調用load,在使用SessionFactory清除二級緩存
//管理二級緩存
SessionFactory factory = HibernateUtils.getSessionFactory();
//factory.evict(Student.class);
factory.evict(Student.class, 1);
//會發出查詢sql,因爲二級緩存中的數據被清除了
3 開啓兩個session 一級緩存和二級緩存的交互
//僅向二級緩存讀數據,而不向二級緩存寫數據
session.setCacheMode(CacheMode.GET);
//發出sql語句,因爲session設置了CacheMode爲GET,所以二級緩存中沒有數據
//只向二級緩存寫數據,而不從二級緩存讀數據
session.setCacheMode(CacheMode.PUT);
//會發出查詢sql,因爲session將CacheMode設置成了PUT
3、查詢緩存:
查詢緩存的生命週期和session無關
query.list----> 數據放到 查詢緩存(id)與二級緩存
查詢緩存是針對普通屬性結果集的緩存
對實體對象的結果集只緩存id
如果採用的是select po.property這樣的方式那麼value爲整個結果集,如採用的是from這樣的方式那麼value爲獲取的結果集中各po對象的主鍵ID
查詢緩存的生命週期,當前關聯的表發生修改,那麼查詢緩存生命週期結束
查詢緩存的配置和使用:
* 在hibernate.cfg.xml文件中啓用查詢緩存,如:
<property name="hibernate.cache.use_query_cache">true</property>
* 在程序中必須手動啓用查詢緩存,如:
query.setCacheable(true);
//查詢緩存只對query.list()起作用,query.iterate不起作用,也就是query.iterate不使用
//查詢緩存
測試
1 開啓查詢緩存,關閉二級緩存 開啓一個session,分別調用query.list
沒有發出查詢sql,因爲啓用了查詢緩存
2 開啓查詢緩存,關閉二級緩存 開啓兩個session,分別調用query.list
不會發出查詢sql,因爲查詢緩存的生命週期和session無關
3 開啓查詢緩存,關閉二級緩存 開啓兩個session,分別調用query.iterate
查詢緩存只對query.list()起作用,query.iterate不起作用,也就是query.iterate不使用
4 關閉查詢緩存,關閉二級緩存 開啓兩個session,分別調用query.list查詢實體對象
會發出查詢sql,因爲list默認每次都會發出查詢sql
5 開啓查詢緩存,關閉二級緩存 開啓兩個session,分別調用query.list查詢實體對象
會發出n條查詢語句,因爲開啓了查詢緩存,關閉了二級緩存,那麼查詢緩存會緩存實體對象的id
所以hibernate會根據實體對象的id去查詢相應的實體,如果緩存中不存在相應的
實體那麼將發出根據實體id查詢的sql語句,否則不會發出sql使用緩存中的數據
6 開啓查詢緩存,開啓二級緩存 開啓兩個session,分別調用query.list查詢實體對象
不會發出查詢sql,因爲開啓了二級緩存和查詢緩存,查詢緩存緩存了實體對象的id列表
hibernate會根據實體對象的id列表到二級緩存中取得相應的數據
13Hibernate 延遲加載(lazy)
重要的概念:
1、lazy的概念,指在需要的時候才發出sql
2、lazy策略只是在session打開期間纔是有效的
3、Hibernate加載本身就是延遲加載,在*.hbm.xml配置文件中<class>裏配置lazy="false"將其改
爲非延遲加載。
4、Hibernate屬性的延遲加載,在*.hnm.xml配置文件中<property>裏配置lazy="false"即可。
注意:類的延遲加載並不影響屬性的延遲加載;
hibernate3.0中lazy有三個值,true,false,proxy,延遲加載
Lazy的有效期:只有在session打開的時候纔有效;session關閉後lazy就沒效了。
lazy策略可以用在:
* <class>標籤上:可以取值true/false
* <property>標籤上,可以取值true/false,這個特性需要類增強 ??? 要對類增強器進行類增強org.hibernate.tool.instrument.InstrumentTask
bulid.xml代碼 用ant運行 對類進行增強
<?xml version="1.0" encoding="UTF-8"?>
<project name="hibernateSample" default="instrument" basedir=".">
<property name="lib.dir" value="./lib"/>
<property name="classes.dir" value="./bin"/>
<path id="lib.class.path">
<fileset dir="${lib.dir}">
<include name="**/*.jar"/>
</fileset>
</path>
<target name="instrument">
<taskdef name="instrument"
classname="org.hibernate.tool.instrument.InstrumentTask">
<classpath path="${classes.dir}"/>
<classpath refid="lib.class.path"/>
</taskdef>
<instrument verbose="true">
<fileset dir="${classes.dir}/com">
<include name="Person.class"/><!-- 此處是要增強的類-->
</fileset>
</instrument>
</target>
</project>
* <set>/<list>等集合上,可以取值爲true/false/extra
* <one-to-one>/<many-to-one>等標籤上,可以取值false/proxy/no-proxy
類(Class)的延遲加載:
* 設置<class>標籤中的lazy="true",或是保持默認(即不配置lazy屬性)
* 如果lazy的屬性值爲true,那麼在使用load方法加載數據時,只有確實用到數據的時候纔會發出sql語句;
這樣有可能減少系統的開銷。
集合(collection)的延遲加載:可以取值true,false,extra
* 保持集合上的lazy的默認值,此時的效果和lazy="extra"是基本一樣的。
* 設置集合上的lazy=extra,此時的效果和lazy屬性的默認值是基本一樣的。但是推薦使用這個屬性值
,因爲在統計時這種情況顯得比較智能。當然延遲