美文网首页Java学习笔记Java学习笔记
Hibernate框架(第二天笔记)

Hibernate框架(第二天笔记)

作者: 04999b3feda6 | 来源:发表于2018-03-06 00:06 被阅读15次

    第一天重点回顾

    1. HIbernate的开发环境的搭建

      1. 建包
      2. 导入jar包
      3. 编写JavaBean实体类
    2. 创建类与表结构的映射文件

      1. 在JavaBean所在包下创建xml映射文件 (命名规范:实体类.hbm.xml)
      2. 引入约束
      3. 配置
        • 设置实体类的全路径名和对应的数据库表名(class)
        • 设置主键id,class属性一般选择native(Mysql自增长 orcale序列化)
        • 设置其余属性的映射(property)
    3. 编写hibernate的核心配置文件

      1. 在src目录下创建核心配置文件 hibernate.cfg.xml

      2. 引入约束

      3. 配置

        • 配置数据库连接信息
        • 配置方言
        • 配置数据库连接池(hibernate的连接池性能不好)
        • 配置其他项
          (1) 显示sql语句
          (2) 格式化
          (3) hibernate.hbm2ddl.auto (4个值 一般用update)
        • 指定映射文件
      4. session的几个常用方法(增删改查)


    Hibernate框架(第二天)

    Hibernate 的持久化类和对象标识符

    1. 持久化类:可以永久的存储到数据库中的类(一个java类与数据库表建立了映射关系)

    2. 持久化类的编写规范

      • 要有无参构造(hibernate底层需要使用反射生成类的实例)
      • 持久化类的属性需要私有,对私有属性提供get和set方法(hibernate底层会对查询到的数据进行封装)
      • 持久化的类属性尽量使用包装类的类型
      • 尽量不要使用final进行修饰
      • 一般都需要实现Serializable接口
    3. Hibernate 中的对象标识符OID

    OID是hibernate用于的是区分两个对象是否是同一个对象的标记
    虚拟机内存区分两个对象看的是内存地址是否一致。数据库区分两个对象,靠的是主键。hibernate负责把内存中的对象持久化到数据库中靠的就是对象标识符来区分两个对象是否是同一个。实体类中映射主键的字段就是OID.

        /** 测试OID的作用 
          * 准备工作:去掉主键生成策略,我们自己来指定主键 
          */
         @Test
          public void test(){
        //创建两个实体类对象,并且设置相同的id和name属性
        Customer customer1 = new Customer();
        customer1.setCustId(9L);
        customer1.setCustName("阿里巴巴");      
        Customer customer2 = new Customer();
        customer2.setCustId(9L);
        customer2.setCustName("阿里巴巴");
        Session session = HibernateUtils.openSession();
        //开启事务
        Transaction tx = session.beginTransaction();
        //调用方法
        session.save(customer1);
        session.save(customer2);
        //提交事务
        tx.commit();
        //释放资源
        session.close();    
          }
        }
    

    运行时后台会报异常,原因是:在Hibernate内存里不允许出现两个不一致的对象有着相同的OID

    1. Hibernate 的主键生成策略
    • 自然主键:
      把具有业务含义的字段作为主键,例如数据库中的name字段,根据数据库的设计要求,此处name字段不允许为null,不能重复,并且不许修改,尽管这样也是可行的,但是不能满足不断变化的业务需求,一旦出现了允许name字段重名的业务需求,就必须修改数据模型,重新定义表的主键,这给数据库的维护增加了难度。

    • 代理主键
      把不具备业务含义的字段作为主键,称之为代理主键。该字段一般取名为ID,通常为整数类型(整数类型比字符串类型要节省更多数据库空间

      <!-- 主键生成策略的设置 -->
      <id name="custId" column="cust_id">
       <generator class="native"></generator>
       </id>
      

    (1). 当主键生成策略为native时,我们创建对象时不需要指定对象的OID的值,就可以保存,因为可以采用自增或者序列化的方式来维护主键
    (2).当主键生成策略为identity时,表示采用自增的方式来维护主键。适合MySql、SqlServer数据库。同样的,我们在创建实体类时也不需要指定OID的值。
    (3).当主键生成策略为sequence时,表示采用序列的方式来维护主键。适合Oracle数据库。同样的,我们在创建实体类时也不需要指定OID的值。
    (4).当主键生成策略为assigned时,表示由我们程序员自己来维护主键。在创建对象时,需要我们手动的为OID设置一个值。当没有为id标签指定generator时,默认就是assigned.
    (5).当主键生成策略为increment时,Hibernate会在插入数据之前,先查询表中主键的最大值,然后把主键的最大值+1,作为这一次的主键插入到表中。不适合集群的情况。increment和identity的区别是:increment是由Hibernate框架来为维护主键,每次查询主键的最大值,再加1作为这一次插入的主键;而identity是由数据库自身来维护,mysql自身就具备自增的能力。
    (6).当主键生成策略为uuid时,表示采用UUID算法来随机生成一个32位的字符串作为主键。一般不用,因为字符串类型的主键比整数类型的主键占用更多的空间。

    Hibernate 的一级缓存和对象状态

    1. HIbernate的一级缓存

    hibernate的一级缓存就是指session缓存.
    hibernate的一级缓存的作用就是减少对数据库的访问次数

    hibernate的一级缓存特点:
    - 当应用程序调用Session接口的save()、update()、saveOrUpdate时,如果Session缓存中没有相应的对象,Hibernate就会自动的把从数据库中查询到的相应对象信息加入到一级缓存中去。
    - 当调用Session接口的load()、get()方法,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询对象,再去数据库中查询对应对象,并添加到一级缓存中。
    - 当调用Session的close()方法时,Session缓存会被清空。

    1. 测试Hibernate的一级缓存

    下面通过代码来验证Hibernate的一级缓存确实是存在的:先查询OID为2的客户,再查询OID为2的客户,测试发现只会打印一条sql语句,并且前后两次查询得到的对象是一致的。

         /**
          * 测试hibernate一级缓存
         */
        @Test
        public void test2(){
        Session session = HibernateUtils.openSession();
        //开启事务
        Transaction tx = session.beginTransaction();
        //根据id查询出对象的,并放入一级缓存中
        Customer customer = session.get(Customer.class, 3L);
        //第二次查询id为2的对象,发现一级缓存中有id为3的对象,就直接从一级缓存中取,不查询数据库
        Customer customer2 = session.get(Customer.class, 3L);
        System.out.println(customer == customer2);
        //提交事务
        tx.commit();
        session.close();
    
        }
    
    
      下面测试session的生命周期:创建session,查询一个对象,提交事务,关闭session;再创建一个session,查询同一个对象,会发出第二条select语句:测试发现会打印两条sql语句,并且前后两次查询得到的对象是不一致的。
    
       /**
       * sesssion缓存是有生命周期的:
       * 当session关闭时,缓存失效
       */
        @Test
        public void test3(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        Customer customer1 = session.get(Customer.class, 3L);
        tx.commit();
        session.close();
    
        session = HibernateUtils.openSession();
        tx = session.beginTransaction();
        Customer customer2 = session.get(Customer.class, 3L);
        System.out.println(customer1 == customer2);
        tx.commit();
        session.close();
        }
    
    1. 快照机制

    Hibernate 向一级缓存放入数据时,同时复制一份数据放入到Hibernate快照中,当使用commit()方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照中的对象是否一致,如果两个对象中的属性发生变化,则执行update语句,将缓存的内容同步到数据库,并更新快照;如果一致,则不执行update语句。Hibernate快照的作用就是确保一级缓存中的数据和数据库中的数据一致。
    测试代码如下:

       /**
       * 测试快照机制
       */
        @Test
        public void test4(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //查询id为3的客户,放到一级缓存中,同时复制一份数据放到hibernate快照中
        Customer customer = session.get(Customer.class, 3L);
        //打印出来的是:小米
        System.out.println(customer.getCustName());
        //修改客户的姓名为:苹果,没有更新,直接提交事务,发现会发送update语句更新数据库中的数据
        customer.setCustName("苹果");
        tx.commit();
        session.close();
        }
    

    4.Hibernate 中的对象的三种状态

    • 瞬时态
    • 持久态
    • 脱管状态或者时游离状态
      区分三种状态的方式
      • 两个指标:
        (1).是否有OID
        (2).是否与session有联系
        临时状态:没有OID,和session没有联系
        持久化状态:有OID,和session有联系
        脱管状态:有OID,和session没有联系

    Hibernate的事务的管理

    1.事务的相关知识(面试常考)

    • 什么是事务:
      事务是指逻辑上的一组操作,组成这个操作的单元要么一起成功,要么一起失败。

    • 事务的特性:
      原子性:事务不可分割
      一致性:事务执行前后,数据完整性保持一致
      隔离性:事务的执行不受其他事务的干扰
      持久性:一旦事务结束,数据就持久到数据库中

    • 如果不考虑隔离性,事务并发引起的安全性问题:
      读问题:
      脏读: 一个事务读到另一个事务未提交的数据
      不可重复读:一个事务读到另一个事务已经提交update的数据,导致一个事务中多次查询结果不一致
      虚读 :一个事务读到另一个事务已经提交insert的数据,导致一个事务中多次查询结果不一致。

      解决读问题:设置事务隔离级别
      read uncommitted :以上读问题都有可能发生
      read committed :避免脏读,但是不可重复读和虚读有可能发生。
      epeatable read :避免脏读和不可重复读,但是虚读有可能发生。
      serializable :以上读问题都能解决。

    1. Hibernate中设置事务隔离级别

      <property name="hibernate.connection.isolation">4</property>
      

    3.Hibernate中设置session与当前线程绑定

    <!-- 把session与当前线程绑定 -->
    <property name="hibernate.current_session_context_class">thread</property>
    

    4.openSession()和getCurrentSession()的区别:

    getCurrentSession()获取的时同一个session,而openSession()在任何情况下,始终创建新的session

    Hibernate 的查询API

    Query:HQL查询

    1.概述

    • 在Hibernate中使用Query对象的步骤,具体所示:
      (1)获得Hibernate的Session对象。
      (2)编写HQL语句。
      (3)调用session.createQuery 创建查询对象。
      (4)如果HQL语句包含参数,则调用Query的setXxx设置参数。
      (5)调用Query对象的方法执行查询。

    • HQL的说明:

      把表的名称换成实体类名称。把表字段名称换成实体类属性名称。

    • 举例:

      sql:

       select * from cst_customer
       select * from cst_customer where cust_name = ‘百度’
      

      Hql:

       from Customer
       from Customer where custName = ‘百度’
      
    1. 常用的查询方法

      查询所有

       /**
      * 查询所有
      * sql语句的写法: select * from 表名
      * hql语句的写法: from 类名
      */
       @Test
       public void test(){
       Session session = HibernateUtils.getCurrentSession();
       Transaction tx = session.beginTransaction();
       
       //获取query对象
       Query query = session.createQuery("from Customer");
       
       //执行query
       
       List<Customer> list = query.list();
       //遍历
       for (Customer customer : list) {
           System.out.println(customer);   
       }
       tx.commit();
      }
      

      条件查询

      /**
      *条件查询
      */
       @Test
       public void test1(){
       Session session = HibernateUtils.getCurrentSession();
       Transaction tx = session.beginTransaction();
       
       //获取query对象
       Query query = session.createQuery("from Customer where custName like ?");
       
       //设置参数
       //一个参数表示第几个?  从0开始数起
       query.setString(0, "%百%");
       
       //执行query
       List<Customer> list = query.list();
       
       //遍历
       for (Customer customer : list) {
           System.out.println(customer);   
       }
       tx.commit();        
      }
      

      分页查询

      /**
      *分页查询
      */
       @Test
       public void test2(){
       Session session = HibernateUtils.getCurrentSession();
       Transaction tx = session.beginTransaction();
       
       //获取query对象
       Query query = session.createQuery("from Customer");
       //设置分页信息
       //设置开始查询的位置
       query.setFirstResult(0);
       //设置查询的最大记录数
       query.setMaxResults(3);
       //执行query
       List<Customer> list = query.list();
       for (Customer customer : list) {
           System.out.println(customer);
           
       }
       tx.commit();    
      }
      

      排序查询

      /**
      *排序查询
      *使用order by关键字
      *asc 默认升序
      *desc 降序
      */
       @Test
       public void test3(){
       Session session = HibernateUtils.getCurrentSession();
       Transaction tx = session.beginTransaction();
       //获取确认样对象
       Query query = session.createQuery("from Customer order by custId desc");
       
       //执行query
       List<Customer> list = query.list();
       for (Customer customer : list) {
           System.out.println(customer);   
       }
               tx.commit();    
      }
      

      统计查询

       /**
      * 统计查询
      */
       @Test
       public void test4(){
       Session session = HibernateUtils.getCurrentSession();
       Transaction tx = session.beginTransaction();
       
       //创建query对象
       Query query = session.createQuery("select count(*) from Customer");
       
       //执行query
       //当确定结果集只有一行数据时,才能使用uniqueResult
       //count函数查询到值是long型
       long total = (long) query.uniqueResult();
       System.out.println(total);
               tx.commit();    
       }
      

      投影查询

       /**
      * 投影查询:使用一个实体的部分字段信息,来构建实体类对象,叫做对象的投影
      *select  new Customer() from Customer  
      * 实体类要求:
      *    必须提供一个相同参数列表的构造函数
          *public Customer(Long custId,String custName) {...}
      */
       @Test
       public void test5(){
       Session session = HibernateUtils.getCurrentSession();
       Transaction tx = session.beginTransaction();
       
       //创建query对象
       Query query = session.createQuery("select new Customer(custId,custName) from Customer");
       
       //执行query
       List<Customer> list = query.list();
       for (Customer customer : list) {
           System.out.println(customer);
       }
                 tx.commit();  
      }
      

    3.query中的方法说明

    • list方法:该方法用于查询语句,返回的结果是一个list集合。
    • setter方法:Query接口中提供了一系列的setter方法用于设置查询语句中的参数,针对不同的数据类型,需要用到不同的setter方法。
    • uniqueResult()方法:该方法用于返回唯一的结果,在确保只有一条记录的查询时可以使用该方法。
    • setFirstResult()方法:该方法可以设置获取第一个记录的位置,也就是它表示从第几条记录开始查询,默认从0开始计算。
    • setMaxResult()方法:该方法用于设置结果集的最大记录数,通常与setFirstResult()方法结合使用,用于限制结果集的范围,以实现分页功能。

    Query:QBC查询

    1.概述

    • 使用Criteria对象查询数据库的主要步骤:
      (1)获得Hibernate的Session对象。
      (2)通过Session获得Criteria对象。
      (3)使用Restrictions的静态方法创建Criterion条件对象。Restrictions类中提供了一系列用于设定查询条件的静态方法,这些静态方法都返回Criterion实例,每个Criterion实例代表一个查询条件。
      (4)向Criteria对象中添加Criterion 查询条件。Criteria的add()方法用于加入查询条件。
      (5)执行Criterita的 list() 或uniqueResult() 获得结果。
    • 细节:
      HQL能查的,QBC都能查,反之亦然。

    2.常用查询

    查询所有

    /**
     *查询所有
     */
    @Test
    public void test(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tx = session.beginTransaction();
        
        //获取Criteria对象
        Criteria criteria = session.createCriteria(Customer.class);
        
        //执行查询
        
        List<Customer> list = criteria.list();
        for (Customer customer : list) {
            System.out.println(customer);
        }
        tx.commit();
    }
    

    条件查询

    /**
     *条件查询(and 和 or)
     */
    @Test
    public void test1(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tx = session.beginTransaction();
        
        //获取criteria对象
        Criteria criteria = session.createCriteria(Customer.class);
        //设置查询条件
        //1. or 连接(满足一个即可)
        //criteria.add(Restrictions.or(Restrictions.like("custName", "%百%"),Restrictions.eq("custLevel", "VIP")));
        //2.and 连接(必须同时满足)
        criteria.add(Restrictions.like("custName", "%百%"));
        criteria.add(Restrictions.eq("custLevel", "VIP"));
        
        //执行查询
        List<Customer> list = criteria.list();
        //遍历
        for (Customer customer : list) {
            System.out.println(customer);
            
        }
        tx.commit();
    }
    

    用到的类、方法说明:
    Restrictions是用来构造条件的工具类,通过该类中的一系列静态方法可以构造各种条件,例如:
    Restrictions.eq(property_name,value)表示=条件,property_name是属性名,value是=号后边的值;
    Restrictions.like(property_name,value)表示like条件,property_name是属性名,value是like后边的值;

    分页查询

    /**
     *分页查询
     */
    @Test
    public void test2(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tx = session.beginTransaction();
        Criteria criteria = session.createCriteria(Customer.class);
        //设置分页信息
        //设置开始索引
        criteria.setFirstResult(0);
        //设置查询的最大记录数
        criteria.setMaxResults(3);
        
        //执行查询结果
        List<Customer> list = criteria.list();
        for (int i = 0; i < list.size(); i++) {
            Customer customer = list.get(i);
            System.out.println(customer);
            
        }
        tx.commit();
    
    }
    

    排序查询

    /**
     *排序查询
     */
    @Test
    public void test3(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tx = session.beginTransaction();
        Criteria criteria = session.createCriteria(Customer.class);
        //按照cust_id降序排序
        criteria.addOrder(Order.desc("custId"));
        
        //执行查询
        List<Customer> list = criteria.list();
        for (Customer customer : list) {
            System.out.println(customer);
            
        }
        tx.commit();
    }
    

    统计查询

    /**
     *统计查询
     */
    @Test
    public void test4(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tx = session.beginTransaction();
        Criteria criteria = session.createCriteria(Customer.class);
        
        //调用统计方法
        //select count(*)
        criteria.setProjection(Projections.rowCount());
        
        //执行查询
        long total = (long) criteria.uniqueResult();
        System.out.println(total);
        tx.commit();
    }
    

    离线查询
    使用场景:页面上有个搜索表单,提交搜索表单后,发送多个搜索参数到servlet,在servlet里用离线查询对象DetachedCriteria拼接sql条件,再把拼接好条件的DetachedCriteria对象依次传递给service、dao,在dao层直接执行DetachedCriteria,不用拼接sql条件了。

    public class QBCDemo2 {
    /**
     * 离线查询
     * 模拟三层架构
     */
    @Test
    public void test(){
        List<Customer> list = servletFindAllCustomer();
        for (Customer customer : list) {
            System.out.println(customer);
            
        }
    }
    
    /**
     * 
     *模拟查询所有用户的servlet
     */
    public List<Customer> servletFindAllCustomer(){
        //获取DetachedCriteria对象
        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
        detachedCriteria.add(Restrictions.like("custName", "%百%"));
        detachedCriteria.add(Restrictions.eq("custLevel", "VIP"));
        List<Customer> list = serviceFindAllCustomer(detachedCriteria);
        return list;
    }
    
    /**
     * 
     * 模拟查询所有用户的service
     */
    public List<Customer> serviceFindAllCustomer(DetachedCriteria detachedCriteria){
        List<Customer> list = daoFindAllCustomer(detachedCriteria);
        return list;
        
    }
    /**
     * 
     * 模拟查询所有用户的dao
     */
    public List<Customer> daoFindAllCustomer(DetachedCriteria detachedCriteria){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tx = session.beginTransaction();
        //把离线查询对象DetachedCriteria转变成在线查询Criteria
        Criteria criteria = detachedCriteria.getExecutableCriteria(session);
        List<Customer> list = criteria.list();
        return list;
    }
    }
    

    相关文章

      网友评论

        本文标题:Hibernate框架(第二天笔记)

        本文链接:https://www.haomeiwen.com/subject/rbwhfftx.html