Hibernate缓存和加强

Hibernate缓存和加强

懒加载

    懒加载(Load On Demand)是一种独特而又强大的数据获取方法 ,是指程序推迟访问数据库,这样做可以保证有时候不必要的访问数据库,因为访问一次数据库是比较耗时的。当查询一个对象的时候,在默认情况下,返回的只是该对象的普通属性,当用户去使用对象属性时,才会向数据库发出再一次查询。

 

懒加载几种情况

    Domain对象是非final的,才能实现懒加载。解决懒加载的方法如下:

1、明确初始化

    在session还没有关闭时,访问一次 xxx.getXxx(),强制访问数据库。或者 Hibernate.initialize(xxx)

 

2openSessionView

    Web开发中,在请求时的过滤器里打开Session,在返回之前再次经过过滤器关闭Session。这样就将Session的作用域扩展到了一次请求响应。

 

openSessionView原理图

 

3、在SSH中,可以实现在Service层,标注方式解决懒加载
4、在对象映射文件中配置,lazy="false"

缓存

    缓存的作用主要用来提高性能,可以简单的理解成一个Map;使用缓存涉及到三个操作:把数据放入缓存、从缓存中获取数据、删除缓存中的无效数据。

 

缓存原理示意图


一级缓存(Session级共享)

    save,update,saveOrUpdate,load,get,list,iterate,lock这些方法都会将对象放在一级缓存中,一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用evict,clear方法清除缓存中的内容。 

 

一级缓存的细节
1什么操作会向一级缓存放入数据

    save,update,saveOrUpdate,load,get,list,iterate,lock

save 案例:

//添加一个学生
Student student=new Student();
student.setName("小东");
s.save(student);//放入一级缓存
//查询
Student stu2=(Student) s.get(Student.class, student.getId()); //select
System.out.println("你刚刚加入的学生名字是"+stu2.getName());

2什么操作会从一级缓存取数据

    get / load / list,get / load 会首先从一级缓存中取,如没有.再有不同的操作[get 会立即向数据库发请求,而load 会返回一个代理对象,直到用户真的去使用数据,才会向数据库发请求]

 

案例:

//查询45号学生
Student stu=(Student) s.get(Student.class, 45);
System.out.println("|||||||||||||||||||");
String hql="from Student where id=45";
Student stu2=(Student) s.createQuery(hql).uniqueResult();
System.out.println(stu2.getName());

PSquery.list()query.uniueResult() 不会从一级缓取数据但是query.list()或者query.uniqueResult() 会向一级缓存放数据的

 

PS一级缓存不需要配置,就可以使用,它本身没有保护机制,所以程序员要考虑这个问题可以evict或者clear来清除session缓存中对象. evict是清除一个对象,clear是清除所有的session缓存对象

 

PSsession级缓存中对象的生命周期session关闭后,就自动销毁


扩展:用HashMap来模拟一个Session缓存,加深对缓存的深入
package com.pc.view;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyCache {
	//使用map来模拟缓存
	static Map<Integer,Student> maps=new HashMap<Integer,Student>();

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		getStudent(1);
		getStudent(1);
		getStudent(1);
		getStudent(1);
		getStudent(3);
		getStudent(3);
	}
	
	public static Student getStudent(Integer id){  //s.get()
		//先到缓存去
		if(maps.containsKey(id)){
			//在缓存有
			System.out.println("从缓存取出");
			return maps.get(id);
		}else{
			System.out.println("从数据库中取");
			//到数据库取
			Student stu=MyDB.getStudentFromDB(id);
			//放入缓存
			maps.put(id, stu);
			return stu;
		}
	}
}

// 我的数据库
class MyDB{
	static List<Student> lists=new  ArrayList<Student>();
	//初始化数据库,假设有三个学生
	static{
		Student s1=new Student();
		s1.setId(1);
		s1.setName("aaa");
		Student s2=new Student();
		s2.setId(2);
		s2.setName("bbb");
		Student s3=new Student();
		s3.setId(3);
		s3.setName("ccc");
		lists.add(s1);
		lists.add(s2);
		lists.add(s3);
	}
	public static Student getStudentFromDB(Integer id){
		for(Student s: lists){
			if(s.getId().equals(id)){
				return s;
			}
		}
		return null; // 在数据库中没有.
	}
}

class Student{
	private Integer id;
	private String name;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

二级缓存(SessionFactory级共享)

    因为一级缓存有限(生命周期短),所以我们需要二级缓存(SessionFactory缓存)来弥补这个问题

 

二级缓存的细节

1、二级缓存需要配置

2、二级缓存是交给第三方去处理,常见的Hashtable , OSCache , EHCache

3二级缓存的对象可能放在内存,也可能放在磁盘

4、二级缓存的原理,在上图已经给出

 

使用OsCache来演示二级缓存的使用
1配置二级缓存
    <property name="hbm2ddl.auto">update</property>
	<!-- 启动二级缓存 -->
	<property name="cache.use_second_level_cache">true</property>
	<!-- 指定使用哪种二级缓存 -->
	<property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
    <!-- 使用统计工具 -->
    <property name="hibernate.generate_statistics">true</property>
	<!-- 指定哪个domain启用二级缓存 
	    特别说明二级缓存策略:
	    1. read-only 只读
	    2. read-write 读写
	    3. nonstrict-read-write 不严格读写
	    4. transcational 事务
	-->
	<class-cache class="com.hsp.domain.Student" usage="read-write"/>
2拷贝一个oscache.propertis配置文件,可以使用参考文档,使用相应Jar包。
3、测试二级缓存
//通过获取一个session,让hibernate框架运行(config->加载hibernate.cfg.xml)
Session s=null;
Transaction tx=null;
	try {
			s=HibernateUtil.openSession();
			tx=s.beginTransaction();
			//查询45号学生
			Student stu1=(Student) s.get(Student.class, 45);//45->一级缓存		
			System.out.println(stu1.getName());
			tx.commit();	
		} catch (Exception e) {
			e.printStackTrace();
			if(tx!=null){
				tx.rollback();
			}
		}finally{
			if(s!=null && s.isOpen()){
				s.close();
			}
		}
		System.out.println("*********************************");
		try {
			s=HibernateUtil.openSession();
			tx=s.beginTransaction();
			//查询45号学生
			Student stu1=(Student) s.get(Student.class, 45);	
			System.out.println(stu1.getName());
			Student stu3=(Student) s.get(Student.class, 46);	
			System.out.println(stu3.getName());
			tx.commit();
		} catch (Exception e) {
			e.printStackTrace();
			if(tx!=null){
				tx.rollback();
			}
		}finally{
			if(s!=null && s.isOpen()){
				s.close();
			}
	}
4、使用统计
//完成一个统计,统计的信息在Sessfactory
//SessionFactory对象
Statistics statistics= HibernateUtil.getSessionFactory().getStatistics();
System.out.println(statistics);
System.out.println("放入"+statistics.getSecondLevelCachePutCount());
System.out.println("命中"+statistics.getSecondLevelCacheHitCount());
System.out.println("错过"+statistics.getSecondLevelCacheMissCount());

PS在配置了二级缓存后,要注意可以通过 Statistics查看配置命中率高不高


主键增长策略

    Hibernate主键增长策略有incrementindentitysequencehilonativeassigneduuid 、符合主键、foreign

 

对象标识符(OID Object ID)

     Hibernate 中的持久化对象对应数据库中的一张数据表,因此区分不同的持久化对象,在Hibernate中是通过OID(对象标识符)来完成的,从表的角度看,OID对应表的主键。从类的角度看OID对应类的主键属性.

 

1increment标识符生成器

Hibernate自动以递增方式生成标识符,每次增量为1

优点:不依赖于底层数据库系统,适用于所有的数据库系统。

缺点:适用于单进程环境下,在多线程环境下很可能生成相同主键值,而且OID必须为数值类型,比如long,int,short类型

配置方式:

<id name="id" type="long" column="ID">
     <generator class="increment"/>
</id>

PS:底层为select max(id) from Student,然后结果+1

 

2identity标识符生成器

由底层数据库生成标识符。

前提条件:数据库支持自动增长字段类型,比如(sql server,mysql),而且OID必须为数值类型,比如long,int,short类型。

配置文件:

<id name="id" type="long" column="ID">
 	<generator class="identity"/>
</id>

3sequence标识符生成器

依赖于底层数据库系统的序列

前提条件:需要数据库支持序列机制(如:oracle等),而且OID必须为数值类型,比如long,int,short类型。

配置文件:

<id name=”id” type=”java.lang.Long” column=”ID”>
 	<generator class=”sequence”>
   	    <param name=”sequence”>my_seq</param>
 	</generator> 
</id>

4native标识符生成器

    native生成器能根据底层数据库系统的类型,自动选择合适的标识符生成器,因此非常适用于跨数据库平台开发,它会由Hibernate根据数据库适配器中的定义,自动采用identity,hilo,sequence的其中一种作为主键生成方式,但是OID必须为数值类型(比如long,short,int)

配置文件:

<id name=”id” type=”java.lang.Integer” column=”ID”>
 	<generator class=”native”/>
</id>

5hilo标识符生成器

    hilo标识符生成器由Hibernate按照一种high/low算法生成标识符,它从数据库中的特定表的字段中获取high值,因此需要额外的数据库表保存主键生成的历史状态,hilo生成方法不依赖于底层数据库,因此适用于每一种数据库,但是OID必须为数值类型(long,int,shor类型)

配置文件:

<id name=”id” type=”java.lang.Integer” column=”ID”>
 	<generator class=”hilo”>
       <param name=”table”>my_hi_value</param>
       <param name=”column”>next_value</param>
 	</generator>
</id>

6uuid标识符生成器

    由Hibernate基于128位唯一值产生算法,根据当前设备IP,时间,JVM启动时间,内部自增量等4个参数生成16进制数值作为主键,一般而言,利用uuid方式生成的主键提供最好的数据插入性能和数据库平台适应性. OID一般使用是String类型。

配置文件:

<id name=”id” type=”java.lang.String” column=”ID”>
 	<generator class=”uuid”/>
</id>

7assigned标识符生成器

    采用assign生成策略表示由应用程序逻辑来负责生成主键标识符,OID类型没有限制。

配置文件:

<id name=”id” type=”java.lang.Integer” column=”ID”>
 	<generator class=”assigned”/>
</id>

8、映射复合主键
create table CUSTOMERS
(  
    CUSNAME     VARCHAR2(40) not null,
    HOMEADDRESS VARCHAR2(50) not null,
    BIRTHDAY    DATE not null,
    SEX         VARCHAR2(2),
    CUSCOMPANY  VARCHAR2(40)
);
--设置复合主键
alter table CUSTOMERS add constraint CUS_PK primary key (CUSNAME, HOMEADDRESS, BIRTHDAY)

以独立主键类映射复合主键,这样可以达到将逻辑加以隔离的目的,配置文件如下:

<composite-id name="id" class="com.test.model.pojo.CustomersId">
    <key-property name="cusname" type="java.lang.String">
        <column name="CUSNAME" length="40" />
    </key-property>
    <key-property name="homeaddress" type="java.lang.String">
        <column name="HOMEADDRESS" length="50" />
    </key-property>
    <key-property name="birthday" type="java.util.Date">
        <column name="BIRTHDAY" length="7" />
    </key-property>
</composite-id>

9foreign外键

    在one-to-one的关系中,有另一张表的主键(Person) 来决定 自己主键/外键( IdCard)。

对象标识符使用简单原则:

1针对oracle [主键是int/long/short建议使用sequence]主键是String使用uuid或者assinged

2针对mysql [主键是int/long/short建议使用increment/assigend ,如果主键是字串,使用UUId/assigned。

3针对sql server [主键是int/long/short建议使用identity/native/assinged ,如果主键是字串,使用uuid/assigned

4one-to-one又是基于主键的则使用foreign


hibernate使用范围

1数据量巨大,性能要求苛刻的系统,hibernate也很难达到要求批量操作数据的效率也不高。 

2主要用于事务操作比较多的项目。

3、不适合OLAP(On-Line Analytical Processing联机分析处理),以查询分析数据为主的系统;适合OLTPon-line transaction processing联机事务处理)。

4、对于些关系模型设计不合理的老系统,也不能发挥hibernate优势。

 

 

----------参考《韩顺平.hibernate从入门到精通》


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值