Java框架之Hibernate(三)

本文主要讲解:

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 就会去数据库中找一百次。

posted @ 2018-01-06 11:53  江河湖泊  阅读(538)  评论(0编辑  收藏  举报