延迟加载(懒加载)
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();
}