延遲加載(懶加載)
hibernate有些查詢API,在執行後並沒有立刻去查數據庫,而是在後續使用對象數據時纔會發送SQL加載數據。
以下三種操作涉及延遲加載相對的是立刻性:
1.load查詢 -- get
2.關聯查詢 -- join fetch
3.query.iterator -- query.list
上述方法,使用中會經常遇到下面異常信息
org.hibernate.LazyInitializationException: could not initialize proxy [cn.xdl.entity.Subject2#50] - no Session
解決方案:
1.將session關閉推遲到對象加載完畢後(推薦)
2.使用立即加載方法替代延遲加載方法
//請求--》StrutsFilter控制器--》Action--》Service--》Dao---》Result--》JSP/JSON--》關閉session--》輸出HTML/JSON響應結果
項目流程結構中,需要採用OpenSessionInView模式保持session在響應結果生成前處於打開莊濤,之後再關閉,最後輸出響應結果。可以通過以前過濾器、攔截器或AOP技術實現。
在Spring框架中提供了OpenSessionInViewFilter組件,可以直接配置使用即可。
例子:
public class TestLazy {
@Test
public void test1(){
Session session = HibernateUtil.getSession();
Subject2 load = session.load(Subject2.class, 50);//測試典型錯誤
session.close();//session的關閉可以放到load查詢之後,報錯就會解決
System.out.println("------------");
System.out.println(load.getName());
}
}
對象持久化
Hibenrate框架可以爲應用程序構建一個持久層。
框架層級包含兩種:
表現層、控制層、業務層、持久層(用於DB數據訪問)
表現層、控制層、業務層、數據訪問層(用於DB數據訪問)
Hibernate中對象有以下三種狀態:
1.臨時狀態(臨時對象)
new Subject2()
可以被垃圾回收機制回收
2.持久狀態(持久對象)
用session.get、load、sava、update等方法操作的對象。
生命週期長,修改後會影響DB(數據庫)記錄。
不會被垃圾回收機制回收
3.遊離狀態/託管狀態
session.clear()清除所有持久對象的狀態
sesson.evict(持久對象名)清除指定的對象
sesson.close()清除所有對象和資源的鏈接
可以被垃圾回收機制回收
例子:
public class TestPersistent {
@Test
public void test1(){
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Subject2 load = session.get(Subject2.class, 50);//持久層對象
System.out.println(load.getId()+" "+load.getName());
//再這加上session.clear()或 sesson.evict(load)方法。
//則將load對象變爲遊離,不更改數據庫內容。
load.setName("PYTHON");//更改數據庫記錄
//session.flush();
tx.commit();//內部先flush再提交事務
session.close();
}
@Test
public void test2(){
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Subject2 subject2 = new Subject2();//臨時對象
subject2.setId(99);
subject2.setName("JAVA");
session.save(subject2);//提升爲持久對象,改變DB記錄
subject2.setName("JAEE");//再次修改DB記錄
tx.commit();//內部先flush再提交事務
session.close();
}
}
Hibernate緩存
1.一級緩存
指的是session緩存,每個session對象都有一個緩存區,緩存獨享。session創建就分配空間,close關閉時釋放空間+關閉連接
自動放入緩存空間:get、load、save、update方法
手動維護和管理空間方法:clear、evict、close
優點:一個session查詢同一個對象多次,第一次查DB,後續從緩存獲取。
@Test
public void test1(){
Session session = HibernateUtil.getSession();
//第一次查詢
Subject2 load = session.get(Subject2.class, 50);//持久層對象
System.out.println(load.getId()+" "+load.getName());
//第二次查詢,此次是查緩存。
Subject2 load2 = session.get(Subject2.class, 50);//持久層對象
System.out.println(load.getId()+" "+load.getName());
session.close();
}
2.二級緩存
指的是SessionFactory緩存,每個SessionFactory對象都有一個,由Factory創建出來的多個sesson共享緩存。Factory關閉會銷燬緩存
二級查詢默認關閉,需要使用時,得做很多設置開啓。
--引入二級緩存工具包ehcache.jar和ehcache.xml配置文件
--在hibernate.cfg.xml追加緩存配置參數
--在映射描述信息追加@Cache 或在sql xml文件加入標籤< cache/>
3.查詢緩存
一級和二級緩存都只能緩存單個對象。如果有查詢結果集需要緩存,就需要採用查詢緩存。
查詢緩存默認關閉,使用時需要先開啓二級緩存,再開啓查詢緩存。
主要步驟如下:
-開啓二級緩存
-在hibernate.cfg.xml開啓查詢緩存參數
-在查詢執行時,可以使用query.setCacheable(true);
Hibernate關聯映射
Hibernate中支持多種關聯映射,有一對多,多對一,多對多,一對一等關係類型。(多表聯合查詢)
表(主) -----》表(外鍵)
DEPT ------> EMP(DEPTNO)
一方 ------》多方,採用一對多關係加載
@OneToMany、<one-to-many>
一方 《------- 多方,採用多對一關係加載
@ManyToOne、<many-to-one>
多對多關係,需要3張表參與表示,使用@ManyToMany、<many-to-many>
一對一關係,可以通過主鍵對逐漸表示,也可以通過主鍵對外鍵(唯一性)表示。@OneToOne、<one-to-one>
一對多例子:
1.再Direction.java中添加映射查詢的關係類型
// @OneToMany(fetch=FetchType.EAGER) //是否延遲加載,默認延遲LAZY,EAGER爲飢餓
@OneToMany //映射關係類型
@JoinColumn(name="direction_id")//設置映射的字段,一般爲表中的外鍵
private List<Subject> subjects;
public List<Subject> getSubjects() {
return subjects;
}
public void setSubjects(List<Subject> subjects) {
this.subjects = subjects;
}
2.對比hql語句和關聯映射。實現查詢id=1的方向信息並查方向id=1的學科信息。
public class TestAssocation {
@Test //hql語句
public void test1(){
Session session = HibernateUtil.getSession();
//查詢id=1的方向信息
Direction d = session.load(Direction.class, 2);
System.out.println(d.getId()+" "+d.getName());
//查詢方向ID=1的學科信息
String hql = "from Subject where directionId=?1";
Query<Subject> query = session.createQuery(hql);
query.setParameter(1, d.getId());
List<Subject> subjects = query.getResultList();
for(Subject s:subjects){
System.out.println(s.getId()+" "+s.getName());
}
session.close();
}
@Test //關聯映射
public void test2(){
Session session = HibernateUtil.getSession();
//查詢id=1的方向信息
Direction d = session.load(Direction.class, 2);
System.out.println(d.getId()+" "+d.getName());
//查詢方向ID=1的學科信息
//@OneToMany(fetch=FetchType.EAGER)開啓飢餓狀態,就算不查subjects也會生成語句,就
//是程序事先給你查好,等你使用。
// for(Subject s:d.getSubjects()){
// System.out.println(s.getId()+" "+s.getName());
// }
session.close();
}
多對一例子使用xml文件配置:
1.再Direction.java中添加映射查詢的關係類型
private Direction direction;
public Direction getDirection() {
return direction;
}
public void setDirection(Direction direction) {
this.direction = direction;
}
在Subject.hbm.xml中配置,外鍵是DIRECTION_ID。
<many-to-one name="direction"
class="cn.xdl.entity.Direction" column="DIRECTION_ID"/>
</class>
測試:通過學科信息加載相關的方向信息,多對一。
@Test
public void test3(){
Session session = HibernateUtil.getSession();
//加載學科信息
Subject subject = session.get(Subject.class, 1);
System.out.println("學科名:"+subject.getName());
//加載相關的方向信息
System.out.println("方向信息:"+subject.getDirection().getName());
session.close();
}
@Test
public void test4(){
Session session = HibernateUtil.getSession();
//加載學科信息
Query<Subject> query = session.createQuery(
"from Subject s join fetch s.direction");
List<Subject> list = query.getResultList();
for(Subject subject:list){
System.out.println(subject.getId()+" "+subject.getName()+" "+subject.getDirection().getName());
}
session.close();
}