day02_1hibernate

对象状态与一级缓存

一、对象缓存状态的介绍

①在使用hibernate时对象的三种状态:(代码如下)

瞬时状态 :没有与session关联,没有主键OID标识(主键的OID指的是对象id,在配置文件中与数据库主键绑定在一起)
持久状态 :与session有关联 ,有主键OID的标识(因为主键策略为native当保存时会自动调用数据库的自增给OID赋值)
游离状态:没有session关联,有主键OID表示

public void fun01(){
            //第一步我们可以使用工具类来得到Session
        Session session = HibernateUtils.openSession();
        Transaction transaction=session.beginTransaction();//开启事务
        //--------------------------------------------------
        //对象存在三种状态,每种状态的特点不一样
        User user =new User();//瞬时状态  :没有与session关联,没有主键uid(主键的uid在面向对象编程的Java中指的就是对象的uid他与主键绑定了)
        user.setUsername("小君君");//瞬时状态
        user.setPassword("745920");//瞬时状态
        
        session.save(user);//持久状态 :与session有关联 ,有主键uid的标识(因为主键策略为native当保存时会自动调用数据库的自增给uid赋值)
                            //问题:调用完save方法后数据库有没有对应数据
                            //答:没有当调用save方法则会保存到一级缓存区中而且也会产生一个对应数据的快照(一级缓存和快照后面会介绍)
                            //当调用了commit方法后就会打印出sql语句提交到数据库中,因为提交方法里面包含flush()方法的操作(flush后面介绍)
        //---------------------------------
        transaction.commit();//提交事务
        session.close();//关闭session资源,关闭session后对象状态就会变为游离
        System.out.println(user);//游离状态:没有session关联,有主键uid表示    
        }
hibernate中对象三种状态的展示

 

②三种状态如何互相转换:瞬时==>持久  瞬时==>游离  持久==>瞬时  持久==>游离  游离==>瞬时游离==>持久

瞬时转持久:分析:瞬时和持久状态区别在于session关联和主键标识

 

    public void fun02(){
        //第一步我们可以使用工具类来得到Session
        Session session = HibernateUtils.openSession();
        Transaction transaction=session.beginTransaction();//开启事务
        //--------------------------------------------------
    /**********************瞬时转持久(分析:瞬时和持久状态区别在于session关联和主键标识)*************************/
为了展示状态的转换这里我将代码写在一行
User user =new User(); user.setUsername("小西西"); user.setPassword("5201314");//瞬时状态
//瞬时状态转换成持久状态调用 save() 或saveOrUpdate()方法产生session关联
    session.save(user);        //持久状态     

        
        //---------------------------------
        transaction.commit();//提交事务
        session.close();//关闭资源
        System.out.println(user);
        }
瞬时转持久

 

瞬时转游离:分析:瞬时和游离状态区别在于主键标识的有无

        public void fun02(){
        //第一步我们可以使用工具类来得到Session
        Session session = HibernateUtils.openSession();
        Transaction transaction=session.beginTransaction();//开启事务
        //--------------------------------------------------
        
        /**********************瞬时转游离(分析:瞬时和游离状态区别在于主键标识的有无)*************************/
        User user =new User(); user.setUsername("小西西"); user.setPassword("5201314");//瞬时状态
        //瞬时状态转换成游离我们可以直接调用user.setUid(uid)设置主键标识
        user.setUid(4);//游离状态
        

        
        //---------------------------------
        transaction.commit();//提交事务
        session.close();//关闭资源
        System.out.println(user);
        }
瞬时转游离

 

持久转瞬时:分析:持久和瞬时状态区别在于session关联和主键标识

 

public void fun02(){
        //第一步我们可以使用工具类来得到Session
        Session session = HibernateUtils.openSession();
        Transaction transaction=session.beginTransaction();//开启事务
        //--------------------------------------------------
//使用get()方法得到持久状态的对象
        User user=(User) session.get(User.class, 1);//持久状态对象
        //持久转瞬时我们需要清除对象主键标识uid使用setUid(null),在调用evict(清除指定的对象)方法清楚掉在一级缓存中指定的对象这样就断了session联系
        user.setUid(null);
        session.evict(user);//瞬时状态对象  对象在缓存中被清除
//---------------------------------
        transaction.commit();//提交事务
        session.close();//关闭资源
        System.out.println(user);
        }
持久转瞬时

 

持久转游离:分析:持久和游离状态区别在于session关联

 

    public void fun01(){
            //第一步我们可以使用工具类来得到Session
        Session session = HibernateUtils.openSession();
        Transaction transaction=session.beginTransaction();//开启事务
        //--------------------------------------------------
    //使用get()方法得到持久状态的对象
        User user =(User) session.get(User.class, 1);
        //持久转游离只需要断开与session的关联即可调用在调用evict(清除指定的对象)方法清楚掉在一级缓存中指定的对象这样就断了session联系
        session.evict(user);//游离状态对象  。  没有session关联,当关闭session时也是相当于关闭session 这个存储空间 
    //---------------------------------
        transaction.commit();//提交事务
        session.close();//关闭资源
        System.out.println(user);
        }
持久转游离

游离转瞬时:分析:游离和瞬时状态区别在于主键标识

public void fun02(){
        //第一步我们可以使用工具类来得到Session
        Session session = HibernateUtils.openSession();
        Transaction transaction=session.beginTransaction();//开启事务
        //--------------------------------------------------

//用get()得到持久状态对象再调用evict(object)方法来清楚除缓存区指定对象得到游离状态
        User user = (User) session.get(User.class, 1);//获得持久状态对象
        session.evict(user);//游离状态。缓存区被清除
        user.setUid(null);//瞬时状态

      
    //---------------------------------------  
      transaction.commit();//提交事务
      session.close();//关闭资源
    }
游离转瞬时

游离转持久:分析:游离和持久状态区别在于session关联

public void fun02(){
        //第一步我们可以使用工具类来得到Session
        Session session = HibernateUtils.openSession();
        Transaction transaction=session.beginTransaction();//开启事务
        //--------------------------------------------------

/**********************游离转持久(分析:游离和持久状态区别在于session关联)*************************/
        User user =new User(); user.setUsername("张小西"); user.setPassword("5201314");//瞬时状态
        user.setUid(1);//游离状态
        //调用update()、saveOrUpdate()或lock()
        //更新数据得到持久状态对象
        session.update(user);//持久状态
        
        //---------------------------------
        transaction.commit();//提交事务
        session.close();//关闭资源
        System.out.println(user);
        }
游离转持久

 

③三种状态有什么用? 

持久状态,我们使用Hibernate主要是为了持久化我们的数据
对于对象的状态,我们期望我们需要同步到数据库的数据都被转换成持久状态
持久状态特点:Hibernate会自动将持久化状态对象的变化同步缓存中最后提交到数据库

 

二、一级缓存的介绍:

1.一级缓存的简介:

 一级缓存:又称为session级别的缓存。当获得一次会话(session),hibernate在session中创建多个集合(map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用;如果没有再查询数据库。当session关闭时,一级缓存销毁。

2.证明一级缓存的存在:

根据下面代码发现用get方法调用的查询,查询了三次但是只有第一次回打印sql语句为什么?

因为第一次查询时会将数据放入一级缓存区中所以第二次第三次查询时会先去缓存区去找找到了就不会再去请求数据库了,不然则会打印sql语句请求数据库

@Test//证明session缓存的存在
    public void fun01(){
        //用工具得到Session对象
        Session session = HibernateUtils.openSession();
        //开启事务
        Transaction  transaction=session.beginTransaction();
        //-------------------------------------------------
        //可以发现只执行了一条语句这是因为执行第一条后,它会将数据库中的数据取出封装成持久态对象,放入session缓冲区中
        User user = (User) session.get(User.class, 1);
        //后面的根据主键标识获取时会先去缓存去找有没有对应的数据如果有则直接返回,不回去访问数据库也就不会打印sql语句
        User user2 = (User) session.get(User.class, 1);
        User user3 = (User) session.get(User.class, 1);
        
        System.out.println(user==user2);
        System.out.println(user2==user3);
        System.out.println(user3==user);
        
        //-------------------------------------------------
        transaction.commit();//提交事务
        session.close();//释放资源
    }

代码图解,一级缓存的运行流程:一级缓存是一以一个Map<串行化,Object>存在的

3.一级缓存的生命周期和存取和清除方法

①一级缓存的生命周期很短
openSession==>一级缓存生命周期的开始
session.close()==>一级缓存销毁

②当调用save 、update、get、load等方法时会将数据保存到一级缓存区里

③想要清除一级缓存里面的对象可以使用:evict(object)这个是清除指定对象  clear()是清除一级缓存区里面的所有对象

 

4、一级缓存的快照:

①快照的简介:

 快照:与一级缓存一样的存放位置,对一级缓存数据备份。保证数据库的数据与 一级缓存的数据必须一致。如果一级缓存修改了,在执行commit提交时,将自动刷新一级缓存,执行update语句,将一级缓存的数据更新到数据库。

②快照的功能:

@Test//证明session中的快照
    //快照会与数据库的数据同步,而且只要持久态对象进入缓存中则会复制一份到快照中。
    public void fun02(){
        //用工具得到Session对象
        Session session = HibernateUtils.openSession();
        //开启事务
        Transaction  transaction=session.beginTransaction();
        //-------------------------------------------------
        //可以发现只执行了一条语句这是因为执行第一条后,它会将数据库中的数据取出封装成持久态对象,放入session缓冲区中,也会存入快照中
        User user = (User) session.get(User.class, 1);
         session.update(user);//他将不会打印update语句因为快照会校验数据有没有改变,如果改变则会保存改变的信息存入缓存和快照中快照则会向数据库发出update语句反之则不会
        
        //-------------------------------------------------
        transaction.commit();//提交事务
        session.close();//释放资源
    }

  当数据进入缓存时会同时复制到快照中,数据会对比快照里面的对象数据每个对象的唯一标识就是OID,如果对比缓存中的对象同一个,里面的属性值发生了改变则会保存重新保存到缓存里面,缓存会比较快照里面的内容如果发生改变则执行update语句并且更新快照使快照内容与数据库同步,如果没有发生变化则不会执行sql语句

使用快照的一些案例代码:

@Test//证明session中的快照
    //快照会与数据库的数据同步,而且只要持久态对象进入缓存中则会复制一份到快照中。
    public void fun03(){
        //用工具得到Session对象
        Session session = HibernateUtils.openSession();
        //开启事务
        Transaction  transaction=session.beginTransaction();
        //-------------------------------------------------
        User user =new User();
        user.setUid(1);//主键标识
        user.setPassword("5201314");
        user.setUsername("刘小小君");//瞬时态对象
        //此时不会打印update因为缓存和快照中没有数据必须等到提交事务时会刷出缓存到数据库才会发出update语句
        session.update(user);//持久态对象。到达这一步后会将数据存入缓存区和快照中因为快照中没有数据所以快照会保存数据然后再发送update语句;
        session.flush();
        User user2=(User)session.get(User.class, 1);//并没有打印select语句是因为缓存中有数据可以直接拿出来
        System.out.println(user2);
        user2.setPassword("5201314");//这里如果持久化对象发生了数据变化会第一时间更新缓存和快照当提交事务或刷新缓存时会发送sql语句与数据库同步数据
        //-------------------------------------------------
        transaction.commit();//提交事务
        session.close();//释放资源
    }
快照

 

三、其他方法的介绍:

1.save()方法和persis()方法的区别

保存对象时使用save方法
保存对象时使用persist方法
区别:


persist(持久)方法来自JPA接口
save(保存)方法来自Hibernate

当主键策略为assigned
改变主键标识时如果是用save方法则主键会被数据库给维护覆盖掉Uid,
如果是使用persist方法则不会被数据库覆盖掉Uid,但是会抛出异常

 

@Test//save()方法和persis()方法的区别
    //1.保存对象时使用save方法
    //保存对象时使用persist方法
    //区别
    //persist(持久)方法来自JPA接口
    //save(保存)方法来自Hibernate
    //当主键策略为assigned
    //改变主键标识时如果是用save方法则主键会被数据库给维护覆盖掉Uid,
    //如果是使用persist方法则不会被数据库覆盖掉Uid,但是会抛出异常
    public void fun04(){
        //用工具得到Session对象
        Session session = HibernateUtils.openSession();
        //开启事务
        Transaction  transaction=session.beginTransaction();
        //-------------------------------------------------
        User user =new User();
        user.setUid(99);
                    
        user.setUsername("张西西");
        user.setPassword("123456");
        
     // session.save(user )    ;//保存操作会立刻打印insert语句但是需要等到事务提交后才会在数据库中同步
        session.persist(user);    //persist方法体现先了持久化思想,持久化指的是对象会完整的持久化保存到数据库,包括Uid
                                //但是主键生成策略是由数据库来维护的所以产生了矛盾会抛出异常
        //-------------------------------------------------
        transaction.commit();//提交事务
        session.close();//释放资源
    }
save&persist

 

2.①HQL查询时是否使用了一级缓存?答:严格意义上来讲是使用了一级缓存,虽然每次都调用HQL语句都会调用sql语句,

但是将数据保存到一级缓存区的时候第二次查询返回得到的结果会根据ID标识看缓存区是不是有相同的结果对象如果有则直接返回第一次放入的数据返回给集合
②HQL语句的查询会保存到一级缓存里面吗?答:会,当调用HQL语句之后再去调用get方法会发现get方法不会打印sql,说明get方法没有请求数据库而是请求的缓

 

public void fun05(){
        //用工具得到Session对象
        Session session = HibernateUtils.openSession();
        //开启事务
        Transaction  transaction=session.beginTransaction();
        //-------------------------------------------------
        //1.HQL查询时是否使用了一级缓存?答:严格意义上来讲是使用了一级缓存
        Query  query=session.createQuery("from User");
        List<User> users=query.list();//查询多条记录的方法
      Query  query2=session.createQuery("from User");
        List<User> users2=query2.list();//查询多条记录的方法
        System.out.println(users.get(0)==users2.get(0));//true
//        Query  query3=session.createQuery("from User");
//        List<User> users3=query3.list();//查询多条记录的方法
        
        //2.HQL语句的查询会保存到一级缓存里面吗?答:会,当调用HQL语句之后再去调用get方法会发现get方法不会打印sql,说明get方法没有请求数据库而是请求的缓存
        User user=(User) session.get(User.class, 1);//会发现并没有打印sql语句说明get方法没有去找数据库而是找的缓存
        System.out.println(user);
        
        
        //-------------------------------------------------
        transaction.commit();//提交事务
        session.close();//释放资源
    }
HQL是否使用一级缓存

 

 

3.①.原生sql语句查询会进入到一级缓存当中吗?答:当如果没有指定封装的类型,则对象不会放入一级缓存中的 

②.原生sql查询会使用一级缓存吗?答:不会同HQL查询一样,每次查询都会查询数据库

 

 

    public void fun07(){
        //用工具得到Session对象
        Session session = HibernateUtils.openSession();
        //开启事务
        Transaction  transaction=session.beginTransaction();
        //-------------------------------------------------
        //当如果没有指定封装的类型,则对象不会放入一级缓存中的 
        
        List<Object[]> users = session.createSQLQuery(" select * from t_user").list();
        
        //List<User> users2 = session.createSQLQuery(" select * from t_user").addEntity(User.class).list();
            
        User user=(User) session.get(User.class, 1);//发现并没有打印sql说明原生sql语句查询的对象数据进入了缓存
       System.out.println(user);
        //-------------------------------------------------
        transaction.commit();//提交事务
        session.close();//释放资源
    
    }
    
    //Criteria查询==>会将查询结果放入一级缓存区中但是不会使用一级缓存每次都会发送sql语句,结论与HQL查询一致
}
sql查询是否使用一级缓存

 

四、其他api介绍:

 

1.清理缓存的方法

①.evict将缓存中的指定对象移除。
②.clear 清空1级缓存

@Test//问题:缓存中的数据如果与数据库中的不同步,会怎么样?
    //会优先使用缓存中的。使用JDBC
    //在一级缓存中出现的该问题的几率比较小。因为一级缓存的生命周期很短
    //openSession==》一级缓存生命周期的开始
    //session.close()==>一级缓存销毁
    public void fun01(){
        //1.evict将缓存中的指定对象移除。
        //2.clear 清空1级缓存
        //用工具得到Session对象
        Session session = HibernateUtils.openSession();
        //开启事务
        Transaction  transaction=session.beginTransaction();
        //-------------------------------------------------
            User u1= (User) session.get(User.class, 1);
            //session.evict(u1);//将u1对象在缓存中移除,则会打印两条select语句
            session.clear();//直接清除整个缓冲区里的内容
            User u2=(User) session.get(User.class, 1);
            
        
        //-------------------------------------------------
        transaction.commit();//提交事务
        session.close();//释放资源
    }
清理缓存的方法

2.再次刷新的方法:

1.refresh方法用于使一级缓存区和快照区与数据库内容保持一致,使用时会重新查询数据库,会刷新对象重新查询select数据库,恢复快照区和一级缓存区的内容

    @Test
    public void fun02(){
        //1.refresh方法用于使一级缓存区和快照区与数据库内容保持一致,使用时会重新查询数据库
        
        //用工具得到Session对象
        Session session = HibernateUtils.openSession();
        //开启事务
        Transaction  transaction=session.beginTransaction();
        //-------------------------------------------------
            User u1= (User) session.get(User.class, 1);
            u1.setUsername("刘小君");//持久化对象发生改变则会,更新缓存和快照
            //session.refresh(u1);//会刷新对象重新查询select数据库,恢复快照区和一级缓存区的内容
            
        //-------------------------------------------------
        transaction.commit();//提交事务
        session.close();//释放资源
    }
再次刷新的方法

3.刷新方法

①flush方法在主键标识相同的对象上,如果我们修改了对象内容则会更新缓存区,然后缓存区会与快照做比较发现不一样则会更新快照但是不会打印update语句,当调用flush之后可以刷新出缓存中要执行的sql语句。

    @Test
        public void fun03(){
            //1.flush方法使缓存区的东西刷新出来数据库会
            //用工具得到Session对象
            Session session = HibernateUtils.openSession();
            //开启事务
            Transaction  transaction=session.beginTransaction();
            //-------------------------------------------------
                User u1= (User) session.get(User.class, 2);
                u1.setUsername("张小西");//持久化对象发生改变则会,更新缓存和快照
                session.flush();//会刷新缓存区直接刷出内容但是不会刷新到数据库中必须要提交事务才能提交到数据库
                
            //-------------------------------------------------
            transaction.commit();//提交事务
            session.close();//释放资源
        }
flush方法

4.saveOrUpdare方法

①当有主键时会调用update方法(当然这个主键是数据库真实存在的)

 

②当主键为空时会调用save方法

    @Test
        public void fun04(){
            //1.update与saveOrUpdare方法
            //主键对应的数据库主键值==》update
            //主键为空==》save
            //用工具得到Session对象
            Session session = HibernateUtils.openSession();
            //开启事务
            Transaction  transaction=session.beginTransaction();
            //-------------------------------------------------
//                User user =new User();
//                user.setUsername("皮卡丘");
//                user.setPassword("123");
//                session.saveOrUpdate(user);//因为没有主键标识所以这个时候会进行保存的操作            
                User user =new User();     
                user.setUid(9);
                user.setUsername("妙蛙种子");   
                user.setPassword("123");   
                session.saveOrUpdate(user);//因为有和数据库对应的主键标识,这个时候会进行修改的操作
                
//                User user =new User();     
//                user.setUid(99);
//                user.setUsername("杰尼龟");   
//                user.setPassword("123");   
//                session.saveOrUpdate(user);//因为主键标识与数据库不对应这个时候会报错这个和save方法不同
            //-------------------------------------------------
            transaction.commit();//提交事务
            session.close();//释放资源
        }
saveOrUpdate方法

 

 

③saveOrUpdare当主键标识数据库库中不存在时就会经行save方法的操作但是前提是主键策略要为assgined不然会抛出异常

 @Test//修改为自然主键assigned后必须指定主键标识不然会报错
        public void fun05(){
            //1.update与saveOrUpdare方法
            //用工具得到Session对象
            Session session = HibernateUtils.openSession();
            //开启事务
            Transaction  transaction=session.beginTransaction();
            //-------------------------------------------------
                
                User user =new User();     
                //user.setUid(101);
                user.setUsername("小火龙");   
                user.setPassword("123"); 
                //因为主键标识与数据库不对应这个时候会报错这个和save方法不同,但是如果我们修改主键生成策略让数据库不要维护主键自增
                session.saveOrUpdate(user);//(会先调用select查询数据库中有没有这个主键,有则update没有则insert)修改为自然主键assigned自己指定主键值
            //-------------------------------------------------
            transaction.commit();//提交事务
            session.close();//释放资源
        }
saveOrUpdate方法之主键标识数据库中不存在

 

5.在我们使用Hibernate的时候,注意要避免出现,两个相同的OID对象,放入一级缓存的情况这样会报错

public void fun06(){
            //用工具得到Session对象
            Session session = HibernateUtils.openSession();
            //开启事务
            Transaction  transaction=session.beginTransaction();
            //-------------------------------------------------
                User user =(User) session.get(User.class, 1);//标识为1持久态对象在一级缓存区里面
                session.evict(user);//在session里面移除指定对象,变为游离态标识为1,不再缓存区中
                User user2 =(User)session.get(User.class, 1);//标识为1持久态对象在一级缓存区里面
                session.update(user);//持久态放入缓存中,这时运行会报错因为一级缓存里面不能有两份一模一样的数据
            //-------------------------------------------------
            transaction.commit();//提交事务
            session.close();//释放资源
        }
避免缓存当中有两个OID标识相同的对象

 

posted @ 2019-07-31 17:43  希~希  阅读(94)  评论(0编辑  收藏  举报