本文主要讲解:
1 级联 cascade 关键字
2 级联删除
3 inverse 关键字
4 懒加载
5 缓存的模拟
6 Hibernate 的一级缓存
7 Hibernate 的二级缓存
一、级联 cascade 关键字
public static void add(){ NiGuAn niguan=new NiGuAn(); niguan.setName("九峰寺"); NiGu nigu1=new NiGu(); nigu1.setNiguan(niguan); nigu1.setFahao("空空大师"); NiGu nigu2=new NiGu(); nigu2.setNiguan(niguan); nigu2.setFahao("玄玄"); NiGu nigu3=new NiGu(); nigu3.setNiguan(niguan); nigu3.setFahao("灭绝"); try{ Session s=HibUtil.getSession(); Transaction tx=s.beginTransaction(); List<NiGu> niguList=new ArrayList<NiGu>(); niguList.add(nigu1); niguList.add(nigu2); niguList.add(nigu3); niguan.setNiguList(niguList); s.save(niguan); /*s.save(nigu1); s.save(nigu2); s.save(nigu3); 如果这里不写,将出错 */ tx.commit(); }
可以使用 cascade (级联) 的方式进行处理
在 NiGuAn.hbm.xml 中 添加
<bag name="niguList" cascade="save-update"> //添加的是级联保存 <key column="niguan_id" /> <one-to-many class="NiGu" /> </bag>
级联 cascade 的几个配置
-- none // 忽略其他关联的对象,默认值
-- all //级联删除, 级联更新,但解除父子关系时不会自动删除子对象.
-- save-update //当session通过save(),update(),saveOrUpdate()方法来保存或更新对象时,级联保存所有关联的新建的临时对象,并且级联更新所有关联的游离对象
-- delete //级联删除, 但不具备级联保存和更新
-- lock //通过lock()把当前游离对象加入session缓存时,会把所有的游离对象也加入Session缓存中。
-- refresh //:通过refresh()刷新当前对象时,会级联刷新所有关联的对象。(刷新是指同步更新session缓存中数据)
-- evict //通过evict()清除session缓存中对象时,会级联清除所有关联的对象
-- replicate //通过replicate()复制当前对象时,会级联复制所有关联的对象
-- persist //当session通过persist()方法来保存当前对象时,会级联保存所有关联的新建的临时对象。
-- merge //通过Session的merge()方法来保存当前对象时,会级联融合所有关联的游离对象。
-- delege-orphan(one-to-many) //删除所有和当前对象时,解除关联行为的对象。
二、级联删除
//删除尼姑庵 public static void delNiGuAn(){ try{ Session s=HibUtil.getSession(); Transaction tx= s.beginTransaction(); NiGuAn niguan=new NiGuAn(); niguan.setId(13); //删除13号 尼姑庵,并且将其关联的所有的尼姑删除 s.delete(niguan); tx.commit(); }finally{ HibUtil.closeSession(); } }
//在 NiGuAn.hbm.xml 中 添加 <bag name="niguList" cascade="delete" > //这里指明要级联删除 <key column="niguan_id" /> <one-to-many class="NiGu" /> </bag>
运行发现.它生成两条sql语句
Hibernate: update NiGu set niguan_id=null where niguan_id=? Hibernate: delete from NiGuAn where id=?
可以看到,并没有真正的级联删除
//真正的级联删除 public static void delNiGuAn(){ try{ Session s=HibUtil.getSession(); Transaction tx= s.beginTransaction(); NiGuAn niguan =(NiGuAn) s.get(NiGuAn.class, 11); //要把主对象查询出来再删除 s.delete(niguan); tx.commit(); } }
三、inverse 关键字
inverse 关键字 表示是否放弃关联关系,(在java对象上建立关联的时候,对数据库产生的影响)
在 one-to-manay 和 many-to-many 这两个集合中定义,在 many -to -one 中不可以
inverse = true 表示对象不维护关联关系
维护 one-to-manay 的时候就是更新外键
维护 many-to-many 就是往中间表里添数据
例如: 多对多映射中
<set name="teacherList" table= "teacher_student" inverse="true" > //让学生放弃关联关系的维护 <key column="student_id" /> <many-to-many class="Teacher" column="teacher_id" /> </set>
在Many2Many 的save方法中,写上下面的内容,也不会出错 ,因为学生已经放弃了关联关系,它不会再产生sql语句了
////////////////////// stu1.setTeacherList(teacherList); stu2.setTeacherList(teacherList); //////////////////////
说明:它不允许在多对一映射的时候,在多的一方,放弃关联关系。
四、懒加载
public static NiGuAn search2(){ try{ Session s=HibUtil.getSession(); NiGuAn niguan= (NiGuAn) s.get(NiGuAn.class, 4); Hibernate.initialize(niguan.getNiguList()); //因为它默认是用懒加载的 return niguan; }finally{ HibUtil.closeSession(); } }
如果默认想不使用懒加载可以在配置文件中
NiGuAn.hbm.xml 中
<bag name="niguList" lazy="false" > <key column="niguan_id" /> <one-to-many class="NiGu" /> </bag> //默认: <bag name="niguList" > <key column="niguan_id" /> <one-to-many class="NiGu" /> </bag> //相当于 <bag name="niguList" lazy="true" fetch="select" > // fetch 抓取策略 <key column="niguan_id" /> <one-to-many class="NiGu" /> </bag>
fetch="select" 它的另一个取值是 join ,如果 fetch="join" 则它是以关联查询的方式查询数据,即始 lazy="true" 它也能把从对象的数据查询出来
五、缓存的模拟
public class CacheDemo { private static Map userCache=new HashMap(); //缓存容器 public static Userinfo getUserById(int id){ Userinfo userInfo =null; //先到缓存中去查询用 String key=Userinfo.class.getName()+id; userInfo=(Userinfo)userCache.get(key); //如果缓存中没有查到,就去数据库中查 if(userInfo==null){ userInfo=(Userinfo) HibUtil.get(Userinfo.class, id); userCache.put(key,userInfo); System.out.println("从数据库中查的"); } else{ System.out.println("命中缓存 ,从缓存中查的"); } return userInfo; } //在进行update的时候对缓存进行更新 public static void update(Userinfo user){ HibUtil.update(user); String key=Userinfo.class.getName()+user.getId(); // userCache.remove(key); //把对应的对象从缓存中移除 userCache.put(key, user) ; //也可以这样,以新换旧,也可以 } //在删除的时候对缓存进行更新 public static void delete(Userinfo user){ HibUtil.del(user); String key=Userinfo.class.getName()+user.getId(); userCache.remove(key); //移除 } //在添加的时候对缓存进行更新 public static void add(Userinfo user){ HibUtil.add(user); String key=Userinfo.class.getName()+user.getId(); userCache.put(key, user); } public static void main(String[] args) { getUserById(101); //从数据库中查的 getUserById(101); //命中缓存 ,从缓存中查的 Userinfo user= getUserById(101); //命中缓存 ,从缓存中查的 user.setUserName("这是换过之后的名字"); HibUtil.update(user); //更新用户信息 Userinfo user2=getUserById(101); //命中缓存 ,从缓存中查的 System.out.println(user2.getUserName()); //这是换过之后的名字 getUserById(101); //命中缓存 ,从缓存中查的 } }
六、Hibernate 的一级缓存
Hibernate 中的缓存有两种, 一级缓存,二级缓存
一级缓存 Session 范围内的
二级缓存 SessionFactory 范围内的
//例 一 public static void test(){ Session s=HibUtil.getSession(); Userinfo user=(Userinfo) s.get(Userinfo.class, 1); //会添充一级缓存 Userinfo user2=(Userinfo) s.get(Userinfo.class, 1); Userinfo user3=(Userinfo) s.get(Userinfo.class, 1); Userinfo user4=(Userinfo) s.get(Userinfo.class, 1); s.close(); //可以发现,最后只输出一条sql语句,因为后面的都是从一级缓存中取的 }
关于一级缓存的说明
1) 它的 key Userinfo.class+id
2) 一级缓存是 Session范围的,生命周期短,不实用
3) save ,update ,saveOrUpdate,load,get,list,lock,iterator 等方法都会把对象添到一级缓存中
4) 可以用 Session 的 evict, clear 等方法清除一级缓存
s.evict(user) //接收参数,只请一个
s.clear(); //不用传参.会把所有的都清掉
5) 一级缓存不能控制缓存对象的个数,所以有大量操作的时候,可能会造成内存溢出,要注意处理
for(int i=0;i<910000000;i++){ if(i%1000==0){ s.flush(); //同步一级缓存中的数据到数据库中 s.clear(); } s.save(useri ...) }
七、Hibernate 的二级缓存
SessionFactory 范围的
Hibernate 把缓存交给别的缓存框架实现 ( 有 对应的jar )
配置文件
hibernate.cache.use_second_level_cache false //是否合用二级缓存
## choose a cache implementation 请选择缓存提供者
#hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider //hibernate 默认使用
#hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider
在hibernet中使用 EhCacheProvider 二级缓存
1) 导包 ehcache-1.2.3.jar
2) 配置主配置文件
<property name="hibernate.cache.use_second_level_cache">true</property> //声明打开二级缓存,默认就是打开 <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> //指明缓存提供者 <property name="hibernate.generate_statistics">true</property> //生成关于缓存的统计信息 省略.... <class-cache usage="read-only" class="cat.beans.Userinfo"/> //在后面加这句,指明哪个类使用二级缓存 //不写这句也可以 ,可以加在映射文件Userinfo.hbm.xml 中, <cache usage="read-write"/>
测试代码:
public class SecondCacheTest { public static void main(String[] args) { HibUtil.get(Userinfo.class, 1); HibUtil.get(Userinfo.class, 1); HibUtil.get(Userinfo.class, 2); //如果为null不会往缓存中放 HibUtil.get(Userinfo.class, 1); HibUtil.get(Userinfo.class, 1); HibUtil.get(Userinfo.class, 2); //得到统计信息 Statistics totalInfo=HibUtil.getSessionFactory().getStatistics(); System.out.println(totalInfo); System.out.println("二级缓存放入 "+totalInfo.getSecondLevelCachePutCount()+"次"); System.out.println("二级缓存命中 "+totalInfo.getSecondLevelCacheHitCount()+"次"); System.out.println("二级缓存msiss "+totalInfo.getSecondLevelCacheMissCount()+"次"); } }
二级缓存放入 2次 //前题是查出来的对象都不为null
二级缓存命中 4次
二级缓存msiss 2次
正常来说,还要有缓存配置文件 ehcache.xml
<diskStore path="java.io.tmpdir"/> //用来指定缓存的存放位置 <diskStore path="c:/cache/xxx" /> ... <defaultCache maxElementsInMemory="10000" //内存中最多可以少多少个对象 eternal="false" //指定缓存是不是永不过期 timeToIdleSeconds="120" //指的是缓存在空闲时间(秒) 过期以后会被删除 timeToLiveSeconds="120" //指的是缓存对象一共存在的时间 diskPersistent="true" //指定jvm 结束的时候是不是要把缓存存到磁盘上 overflowToDisk="true" //超出个数(maxElementsInMemory 指定的) 以后,是不是往磁盘上存 diskExpiryThreadIntervalSeconds ="60" //清除过期缓存的线程的轮循时间 />
关于二级缓存的说明
1) 哪些方法可以填充二级缓存
get , save (它不适合于native 主键的类 ,因为这样的主键,不把数据存到数据库的时候是得不到的,所以没有办法生成key,但 uuid,hilo 都可以)
update,saveOrupdate, get, load,list,iterator ,以及 Query,Cirteria 都会填充二级缓存。
但查询的时候,只有 session iterator ,get,load 会从二级缓存中取数据。
iterator 可能会存在 N+1 查询的问题
Query,Cirteria 缓存的命中率比较低, 在hibernate默认是关闭 ,因为它的查询条件复杂,而且缓存的数据量大
可以用
<property name="hibernate.cache.use_query_cache">true</property> // 可以打开Query,Cirteria 查询的二级缓存支持
2) 清除二级缓存
HibUtil.getSessionFactory().evict(Userinfo.class,1); //清id为1的 Userinfo 对象的缓存 HibUtil.getSessionFactory().evict(Userinfo.class,); //清除所有的 Userinfo 对象的缓存 //注意:它没有清除所有的缓存的方法
3) 关于Query 的缓存
public static void main(String[] args) { QueryCacheTest(1); QueryCacheTest(1); QueryCacheTest(1); // ...打印缓存命中信息 public static UserInfo QueryCacheTest(int id){ Session s=HibUtil.getSession(); Query q=s.createQuery("from UserInfo as u where u.id="+id); //它会把查询条件当做Key q.setCacheable(true); //注意要有这句 UserInfo u=(UserInfo)q.uniqueResult(); s.close(); return u; }
说明,Query q=s.createQuery("from UserInfo as u where u.id="+id) 这种,它会把查询条件当做key,把 usersId 列表做为一个key
然后再根据key中的 user id 去缓存中找 UserInfo的信息,最差的情况是它失效了,如果有一百个id 就会去数据库中找一百次。