1. 懒加载是什么? 为什么需要懒加载?
2. 如何解决懒加载问题
u 为什么需要缓存?
看一个案例:->原理图
从上图看出: 当我们去查询对象的时候,首先到一级缓存去取数据,如果有,则不到数据库中取,如果没有则到数据库中取,同时在一级缓存中放入对象.
u 一级缓存的细节
① 什么操作会向一级缓存放入数据
save,update,saveOrUpdate,load,get,list,iterate,lock
save 案例:
//添加一个学生
Studentstudent=new Student();
student.setName("小东");
s.save(student);//放入一级缓存
//我马上查询
Studentstu2=(Student) s.get(Student.class, student.getId()); //select
System.out.println("你刚刚加入的学生名字是"+stu2.getName());
② 什么操作会从一级缓存取数据.
get / load / list
get / load 会首先从一级缓存中取,如没有.再有不同的操作[get 会立即向数据库发请求,而load 会返回一个代理对象,直到用户真的去使用数据,才会向数据库发请求]
?list 会不会从session缓存取数据?
案例:
//查询45号学生
Studentstu=(Student) s.get(Student.class, 45);
System.out.println("|||||||||||||||||||");
Stringhql="from Student where id=45";
Studentstu2=(Student) s.createQuery(hql).uniqueResult();
System.out.println(stu2.getName());
从上面的案例,我看出query.list() query.uniueResut() 不会从一级缓取数据! 但是query.list 或者query.uniqueRestu() 会向一级缓存放数据的.
③ 一级缓存不需要配置,就可以使用,它本身没有保护机制,所以我们程序员要考虑这个问题,我们可以同 evict 或者 clear来清除session缓存中对象. evict 是清除一个对象,clear是清除所有的sesion缓存对象
④ session级缓存中对象的生命周期, 当session关闭后,就自动销毁.
⑤ 我们自己用HashMap来模拟一个Session缓存,加深对缓存的深入.
package com.hsp.view;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyCache {
//使用map来模拟缓存
staticMap<Integer,Student> maps=new HashMap<Integer,Student>();
publicstatic 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(Integerid){ //s.get()
//先到缓存去
if(maps.containsKey(id)){
//在缓存有
System.out.println("从缓存取出");
returnmaps.get(id);
}else{
System.out.println("从数据库中取");
//到数据库取
Studentstu=MyDB.getStudentFromDB(id);
//放入缓存
maps.put(id,stu);
returnstu;
}
}
}
//我的数据库
class MyDB{
staticList<Student> lists=new ArrayList<Student>();
//初始化数据库,假设有三个学生
static{
Students1=new Student();
s1.setId(1);
s1.setName("aaa");
Students2=new Student();
s2.setId(2);
s2.setName("bbb");
Students3=new Student();
s3.setId(3);
s3.setName("ccc");
lists.add(s1);
lists.add(s2);
lists.add(s3);
}
publicstatic Student getStudentFromDB(Integer id){
for(Students: lists){
if(s.getId().equals(id)){
returns;
}
}
returnnull;// 在数据库中没有.
}
}
class Student{
privateInteger id;
privateString name;
publicInteger getId() {
returnid;
}
publicvoid setId(Integer id) {
this.id= id;
}
publicString getName() {
returnname;
}
publicvoid setName(String name) {
this.name= name;
}
}
u 为什么需要二级缓存?
因为一级缓存有限(生命周期短),所以我们需要二级缓存(SessionFactory缓存)来弥补这个问题
1. 需要配置
2. 二级缓存是交给第三方去处理,常见的Hashtable ,OSCache , EHCache
3. 二级缓存的原理
4. 二级缓存的对象可能放在内存,也可能放在磁盘.
u 快速入门案例
使用OsCache来演示二级缓存的使用.
1. 配置二级缓存
对配置说明:
<propertyname="hbm2ddl.auto">update</property>
<!--启动二级缓存 -->
<property name="cache.use_second_level_cache">true</property>
<!--指定使用哪种二级缓存-->
<propertyname="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
<mappingresource="com/hsp/domain/Department.hbm.xml" />
<mappingresource="com/hsp/domain/Student.hbm.xml" />
<!--指定哪个domain启用二级缓存
特别说明二级缓存策略:
1.read-only
2.read-write
3.nonstrict-read-write
4.transcational
-->
<class-cache class="com.hsp.domain.Student"usage="read-write"/>
2. 可以文件放在 src目录下,这样你可以指定放入二级缓存的对象capacity 大小. 默认1000
3 使用
// TODO Auto-generated method stub
//通过获取一个sesion,让hibernate框架运行(config->加载hibernate.cfg.xml)
Sessions=null;
Transactiontx=null;
try{
//我们使用基础模板来讲解.
s=HibernateUtil.openSession();
tx=s.beginTransaction();
//查询45号学生
Studentstu1=(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号学生
Studentstu1=(Student) s.get(Student.class, 45);
System.out.println(stu1.getName());
Studentstu3=(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();
}
}
//完成一个统计,统计的信息在Sessfactory
//SessionFactory对象.
Statisticsstatistics= HibernateUtil.getSessionFactory().getStatistics();
System.out.println(statistics);
System.out.println("放入"+statistics.getSecondLevelCachePutCount());
System.out.println("命中"+statistics.getSecondLevelCacheHitCount());
System.out.println("错过"+statistics.getSecondLevelCacheMissCount());
3. 在配置了二级缓存后,请大家要注意可以通过Statistics,查看你的配置命中率高不高
u 主键增长策略
① increment
自增,每次增长1, 适用于所有数据库. 但是不要使用在多进程,主键类型是数值型
select max(id) from Student
② identity
自增,每次增长1, 适用于支持identity的数据(mysql,sqlserver), 主键类型是数值
③ sequence
④ native
会根据数据类型来选择,使用identity,sequence ,hilo
selecthibernate_sequence.nextval from dual
主键类型是数值long , short ,int
<id name="id"type="java.lang.Integer">
<generatorclass="native"/>
</id>
⑤ hilo
hilo标识符生成器由Hibernate按照一种high/low算法生成标识符
用法:
<id name=”id” type=”java.lang.Integer” column=”ID”>
<generatorclass=”hilo”>
<paramname=”table”>my_hi_value</param>
<param name=”column”>next_value</param>
</generator>
</id>
⑥ uuid
会根据uuid算法,生成128-bit的字串
主键属性类型不能是数值型,而是字串型
⑦ assigned
用户自己设置主键值,所以主键属性类型可以是数值,字串
⑧ 映射复合主键
⑨ foreign
在one-to-one的关系中,有另一张表的主键(Person) 来决定 自己主键/外键( IdCard)
给出一个简单原则:
针对oracle [主键是int/long/short 建议使用 sequence] 主键是String 使用uuid或者assinged
针对 mysql [主键是 int/long/short 建议使用increment/assigend,如果是字串 UUId/assigned]
针对 sql server [主键是 int/long/short 建议使用identity/native/assinged ,如果主键是字串,使用uuid/assigned ]
one-to-one 又是基于主键的则使用foreign
u hibernate最佳实践(在什么项目中使用最好)
对于数据量大,性能要求高系统,不太使用使用hiberante.
主要用于事务操作比较多的项目(oa/某个行业软件[石油、税务、crm, 财务系统.]
olap->hibernate用的比较少 oltp->hibernate