SSH Hibernate框架特征之延迟加载、对象持久化、缓存、关联映射day-04

延迟加载(懒加载)

   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();
	}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值