美文网首页程序员Java 杂谈
二、加深理解Hibernate框架

二、加深理解Hibernate框架

作者: 明天你好向前奔跑 | 来源:发表于2017-06-30 18:26 被阅读0次

    Hibernate_缓存&事务&批量查询概述

    一、 Hibernate的持久化类

    持久化类是指一个Java类和数据库建立了映射关系,那么这个类就是持久化类。

    1. 持久化类的编写规则:

    • 持久化类需要有无参的构造方法。
      • 因为Hibernate的底层需要使用反射生成类的实例。
    • 持久化类的属性需要私有,私有的属性需要提供set和get方法。
      • 因为Hibernate底层要将查询到的数据进行封装。
    • 持久化类尽量使用包装类的类型。
      • 因为包装类与普通类的默认值不同。例:Integer为null,int为0.在数据库表中代表的含义包装类更准确。
    • 持久化类需要有一个唯一标识OID与表的主键对应。
      • Hibernate不允许在内存中出现两个相同OID的持久化对象。
    • 持久化类不能使用final修饰。
      • 因为Hibernate中有延迟加载机制,这个机制会产生代理对象。而这个代理对象是使用字节码的增强技术完成的,其实就是产生了一个当前类的子类对象实现的。

    2. Hibernate的主键生成策略

    • 自然主键
      • 把具有业务含义的字段作为主键。
    • 代理主键
      • 把不具备业务含义的字段作为主键,与对象本身无关,该字段一般取名"ID".
    img08.png

    我们在映射文件中设置选择native,根据数据库底层的支持来自动生成策略。

    3. 持久化对象的三种状态

    • 瞬时态
      • 没有OID,与Hibernate的Session没有任何关联
    • 持久态
      • 有OID,与Session有关联
    • 托管态
      • 有OID,与Session没有关联

    瞬时态:

    对象创建时,就是瞬时态。
    如: User user = new User();
        user.serUname("悟空");
        user.setUpassword("123");
    这里面的user都是瞬时态,没有OID,与Session没有关联。
    

    持久态:

    session.save(user);
    这里的user与session产生关联,加入到了Session的缓存中,生成了持久化标识OID,是持久态
    
    此外,get/load/find...等方法获取的对象也是持久态对象
    

    托管态:

    session.close();
    System.out.println(user);
    这里的user有了OID,但是与Session没有了关联,是托管态
    

    持久化对象的三种对象之间的互相转换:

    img09.png

    上图:

    1. 一个对象被new出来之后处于瞬时态;当对瞬时态对象执行Session对象的save()/saveOrUpdate()方法后,
    该对象会被放入Session的一级缓存,进入持久态;当对持久态对象执行evict()/close()/clear()操作后,
    对象进入托管态。
    
    2. Session对象直接执行get()/load()/find()/iterate()等方法从数据库查询对象时,查询到的对象也属于
    持久态;
    
    3. 当对托管态对象执行Session的upadte()/saveOrUpadate()方法时,会转为持久态;瞬时态与托管态的对象
    不在Session对象的管理范围内,因此一段时间后会被JVM回收。
    

    持久化对象的特点:能够自动更新数据库

    持久化对象能够自动更新数据库依赖于Hibernate的一级缓存。

    @Test
    //Hibernate持久化对象自动更新数据库
    public void test() {
        //1. 获取Hibernate配置对象加载配置
        //2. 获取SessionFactory对象
        //3. 获取Session对象
        Session session = HibernateUtils.openSession();
        //4. 开启事务
        Transaction transaction = session.beginTransaction();
        //5. 执行业务逻辑
        User user = session.get(User.class, 2);//获得持久化对象
        user.setUname("外星人");
    
        //无需手动调用session.save(user);就可以执行数据库更新了
    
        //6. 提交事务
        transaction.commit();
        //7. 关闭资源
        session.close();
        System.out.println("user:"+user);
    }
    

    二、 Hibernate的一级缓存

    缓存的概念:介于应用程序与永久性存储的数据源之间,作用是降低应用程序直接读写数据源的频率,从而提高应用的运行性能。缓存中的数据是数据源数据的拷贝。

    1. 什么是Hibernate的一级缓存

    • Hibernate的一级缓存就是指Session缓存,Session缓存就是一块内存空间。
    • 在使用Hibernate查询对象时,首先会拿该对象的OID去Session缓存中查找是否有匹配该OID值的对象。如果有就从Session缓存中取出使用,不再查询数据库。如果没有找到,就会去数据库中查询。
    • 当从数据库查询到数据时,也会在Session缓存区放置一份该对象的数据信息,下次再查询就可以在Session缓存中直接查询到。
    
        @Test
        //测试一级缓存的存在
        public void test() {
            //首先获取到一个对象,再重复获取该对象,查看会打印几次sql语句
            
            //1. 获取Hibernate配置对象加载配置
            //2. 获取SessionFactory工厂对象
            //3. 获取Session对象
            Session session = HibernateUtils.openSession();
            //4. 开启事务
            Transaction transaction = session.beginTransaction();
            //5. 实现业务逻辑
            
            User user = session.get(User.class, 1);
            System.out.println(user);
            User user2 = session.get(User.class, 1);
            System.out.println(user2);
            User user3 = session.get(User.class, 1);
            System.out.println(user3);
            
            
            //6. 提交事务
            transaction.commit();
            //7. 释放资源
            session.close();
        }
    
    

    结果只执行了一次查询语句,说明Hibernate只去数据库查询了一次获取到了user对象,然后将该对象的数据信息存入了Session缓存中,在这个Session没有关闭前,都可以从缓存中直接取出与该OID匹配的user对象。

    img10.png

    Hibernate的一级缓存(Session缓存)的特点:

    1. 当应用程序调用Session接口的save()、update()、saveOrUpdate()时,如果缓存区中没有对应的对象,Hibernate会自动的将从数据库查询到的相应的对象信息加入到Session缓存中去。

    2. 当调用Session接口的load()、get()、以及Query接口的list()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库。如果缓存中没有要查询的对象,就去数据库中查询并放入Session缓存中。

    3. 当调用Session对象的close(),Session缓存会被清空。

    2. Session缓存的快照区

    Hibernate向一级缓存放入数据时,同时会将一份数据放到Hibernate快照中,当事务提交时,
    同时会清理Session的一级缓存,这时Hibernate会根据OID比较一级缓存与快照中的对象是否一致,
    如果不一致执行update语句。如果一致就不执行。
    Hibernate的快照确保了一级缓存中的数据与数据库中数据的一致。
    
    这也是Hibernate中持久化对象自动更新数据库的原理。
    

    三、 Hibernate的事务控制

    事务:逻辑上的一组操作,组成这组操作的各个单元,要不全都成功,要不全都失败。

    1. 事务的四个特性:(ACID特性)

    • 原子性(Automic):表示将事务所做的操作捆绑成一个不可分割的单元。对事务所进行的数据修改等操作,要不全都成功,要不全都失败。

    • 一致性(Consistency):表示事务完成后,必须使所有的数据都保持一致状态。

    • 隔离性(Isolation):一个事务的执行不能被其他事务干扰,并发执行的事务之间不能互相干扰。

    • 持久性(Durability):事务一旦提交,它对数据库中数据的修改是永久性的。提交后的其他操作或者故障都不会有任何故障。

    2. 事务的并发问题

    1. 脏读

       一个事务提交到了另一个事务未提交的数据。这是很严重的并发问题,必须解决。
       例如:
           用户购物,付款尚未成功提交之前,卖家就能读到用户付款的数据,误以为已经付款。
      
    2. 不可重复读

       一个事务读到了另一个事务已经提交后的update的数据,导致在同一个事务中的多次查询结果不一致。
      
       例如:
           小王在刷卡的同时,家人用他的银行卡进行了网上购物,导致小王刷卡前后两次查询的结果不一致。
      
    3. 虚读

       一个事务都到了另一个事务已经提交的insert的数据,导致在同一个事务中的多次查询结果不一致。
      

    3. 事务的4个隔离级别

    为了避免事务并发问题的发生,标准sql规范中定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。

    1. 读未提交。(Read Uncommitted,1级),一个事务可以访问其他事务未提交的数据。
    2. 读已提交。(Read Commited,2级),一个事务在执行的过程中,可以访问其他事务成功提交的新插入的数据,也可以访问成功修改的数据。但禁止访问未提交的数据。该隔离级别可以有效的防止脏读。
    3. 可重复读。(Repeatable Read,4级), 一个事务在执行的过程中,可以访问其他事务成功提交的新插入的数据,但不可以访问成功修改的数据。此隔离级别可以有效的防止脏读与不可重复读。
    4. 序列化。(Serializable,8级),提供严格的事务隔离,事务只能序列化,一个一个的执行,不能并发执行。此隔离级别可以防止脏读,不可重复读以及虚读。

    隔离级别越高,安全性越高,性能越低。

    mysql的隔离级别默认是可重复读(Repeatable Read)
    oracle的隔离级别默认是可重复读(Read Committed)

    4. Hibernate的事务管理

    <!-- 
        设置隔离级别:
            hibernate.connection.isolation = 4
            1-Read uncommitted isolation
            2-Read committed isolation
            4-Repeatable read isolation
            8-Serializable isolation 
    -->
    <property name="hibernate.connection.isolation">4</property>
    

    在使用Hibernate进行事务管理时,service层与dao层要保证Session对象的一致,可以采用三种方式确保Session
    对象的一致: Session对象传递,使用ThreadLocal存储与当前线程绑定的Session对象,直接使用Hibernate封装好的getCurrentSession()方法。
    
    
    在这里,前两种方式就省略了,如果有想了解的,可以翻看我以前写的Connection部分JDBCUtils工具类的封装。
    

    直接使用Hibernate封装好的管理Session的方式

    在Hibernate的配置文件中,hibernate.current_session_context_class属性用于指定Session的管理方式:

    • thread : Session对象的生命周期与当前线程绑定
    • jta : Session对象的生命周期与JTA事务绑定
    • managed : Hibernate委托程序来管理Session的生命周期

    hibernate.cfg.xml进行如下配置:

    <property name="hibernate.current_session_context_class">thread</property>
    

    配置后,Hibernate提供了getCurrentThread()方法来创建一个与当前线程绑定的Session对象。

    在HibernateUtils工具类中添加getCurrentSession方法:

    // 获得当前线程中的绑定的Session
    // 注意:必须配置
    // return
    public static Session getCurrentSession() {
        return sf.getCurrentSession();
    }
    

    需要注意的是:必须先在配置中进行配置,而且使用后无需手动关闭Session对象,事务提交后会自动关闭Session对象

    
        Sercice层:
    
        //用户转账的业务
        public void transfer(String sender, String receiver, String amount) throws Exception {
            Transaction transaction = null;
            try {
                //调用dao层
                TransferDAO dao = new TransferDAOImpl();
                
                //由于这里操作了两条sql语句因此应该开启事务
                Session session = HibernateUtils.getCurrentSession();
                transaction = session.beginTransaction();
                
                //转出
                dao.out(sender,amount);
                //转入
                dao.in(receiver,amount);
                
                //没有异常就提交
                transaction.commit();
            } catch (Exception e) {
                //回滚事务
                transaction.rollback();
                e.printStackTrace();
                throw e;
            }
        }
    
    
        dao 层:
    
        // 转出
        public void out(String sender, String amount) throws SQLException {
            Session session = HibernateUtils.getCurrentSession();
            // 转出
            User user = session.get(User.class, 1);
            double amount2 = Double.parseDouble(amount);
            user.setAcount(user.getAcount() - amount2);
        }
    
        @Override
        // 转入
        public void in(String receiver, String amount) throws SQLException {
            Session session = HibernateUtils.getCurrentSession();
            // 转入
            User user = session.get(User.class, 2);
            double amount2 = Double.parseDouble(amount);
            user.setAcount(user.getAcount() + amount2);
        }   
    

    四、 Hibernate的批量查询(概述)

    1. HQL查询 : Hibernate Query Language

    HQL : Hibernate Query Language。Hibernate的独家查询语言,属于面向对象的查询语言。

    适用于:多表查询,但不是很复杂时使用。

    • setXxx : 如果HQL语句中有参数,根据参数的不同类型设置参数的值。
    • setParameter : 上面方法的万金油版本。可以根据参数自动选择参数的类型设置。
    • uniqueResult : 结果只有一条记录的时候使用,用于获取唯一的结果。
    • list : 获取多条记录存储在List集合中。
    • setFirstResult : 分页查询设置第一条记录的位置。表示从第几条数据查询,默认从0开始。
    • setMaxResult : 分页查询设置结果集的最大记录数。即每页显示的最大数据数量。

    使用HQL的步骤:

    1. 获取Session对象,开启事务
    2. 编写HQL语句
    3. 使用Session对象获取查询对象Query
    4. 如果HQL语句包含参数,使用query.setXxx()设置参数。
      • 这里有一个万金油的设置参数的方法. query.setParameter();
    5. 执行HQL查询语句

    HQL查询测试

    
        /**
         * Hibernate的HQL语句练习
         */
        public class HibernateTest {
        
            @Test
            // HQL基本查询
            public void test() {
                // 1. 获取Session对象
                Session session = HibernateUtils.getCurrentSession();
                // 2.开启事务
                Transaction transaction = session.beginTransaction();
                // -----------------------------------------
                // 3. 业务逻辑:HQL的基本查询
        
                // 3.1 创建HQL查询语句。HQL语句格式为:select * from 全类名.
                // HQL语句格式为:select * from 全类名.
                // 在查询所有字段时可以省略select *
                // String hql = "select * from com.itdream.domain.Customer";
                String hql = "from Customer";
                // 3.2 创建查询对象Query
                Query query = session.createQuery(hql);
                // 3.3 执行查询
                List<Customer> list = query.list();
                // 如果确定结果为一条结果,可以使用uniqeResult()
                // Customer customer = (Customer) query.uniqueResult();
        
                System.out.println(list);
        
                // -----------------------------------------
                // 4.提交事务
                transaction.commit();
                // getCurrentSession无需手动关闭
            }
        
            @Test
            // HQL条件查询:问号占位符
            public void test2() {
                // 1. 获取Session对象
                Session session = HibernateUtils.getCurrentSession();
                // 2.开启事务
                Transaction transaction = session.beginTransaction();
                // -----------------------------------------
                // 3. 业务逻辑:HQL条件查询:问号占位符
                // 3.1 编写HQL查询语句,注意:HQL语句中出现的是类中的属性而不是表中的字段
                String hql = "from Customer where cid = ?";
                //3.2 获取HQL的查询对象Query
                Query query = session.createQuery(hql);
                //设置问号占位符处的参数.Hibernate的问号占位付默认从0开始
                //query.setInteger(0, 2);
                //万金油的设置参数方法
                query.setParameter(0, 2);
                //3.3 执行查询
                Customer customer = (Customer) query.uniqueResult();
                
                System.out.println(customer);
                // -----------------------------------------
                // 4.提交事务
                transaction.commit();
                // getCurrentSession无需手动关闭
            }
            
            @Test
            // HQL条件查询:命名占位符
            public void test3() {
                // 1. 获取Session对象
                Session session = HibernateUtils.getCurrentSession();
                // 2.开启事务
                Transaction transaction = session.beginTransaction();
                // -----------------------------------------
                // 3. 业务逻辑:HQL条件查询:命名占位符(冒号:+命名)
                // 3.1 编写HQL查询语句,注意:HQL语句中出现的是类中的属性而不是表中的字段
                String hql = "from Customer where cid = :cid";
                //3.2 获取HQL的查询对象Query
                Query query = session.createQuery(hql);
                //设置问号占位符处的参数.Hibernate的问号占位付默认从0开始
                //万金油的设置参数方法
                query.setParameter("cid", 2);
                //3.3 执行查询
                Customer customer = (Customer) query.uniqueResult();
                
                System.out.println(customer);
                // -----------------------------------------
                // 4.提交事务
                transaction.commit();
                // getCurrentSession无需手动关闭
            }
            
            
            @Test
            // HQL分页查询
            public void test4() {
                // 1. 获取Session对象
                Session session = HibernateUtils.getCurrentSession();
                // 2.开启事务
                Transaction transaction = session.beginTransaction();
                // -----------------------------------------
                // 3. 业务逻辑:HQL条件查询:命名占位符(冒号:+命名)
                // 3.1 编写HQL查询语句,注意:HQL语句中出现的是类中的属性而不是表中的字段
                String hql = "from Customer";
                //3.2 获取查询对象Query
                Query query = session.createQuery(hql);
                
                //3.3 设置分页的参数
                query.setFirstResult(4);//从第(4+1)条数据开始,Hibernate默认从0开始
                query.setMaxResults(2);//每页显示两条数据
                
                //3.4执行hql语句
                List<Customer> list = query.list();
                System.out.println(list);
                // -----------------------------------------
                // 4.提交事务
                transaction.commit();
                // getCurrentSession无需手动关闭
            }
        }
    
    

    分页查询结果:

    img11.png

    2. Critiria查询

    Criteria查询是Hibernate的核心查询对象。它是完全面向对象的无语句查询。在Criteria查询方式中,是绝对不会出现sql语句的。Criteria查询又称为QBC查询(Query By Criteria)。

    适用于单表的条件查询。


    常用API

    //获取Criteria查询对象
    Criteria criteria = session.createCriteria(Customer.class); //传入的参数是持久化类对象
    
    //设置查询条件
    // 参数1:持久化类中的属性 。 参数2:对应的值
    Criterion eq = Restrictions.eq("cid", 1);
    
    //设置分页参数
    criteria.setFirstResult(4);//这里的4代表的是索引,第五条记录开始
    criteria.setMaxResults(2);//每页显示2条数据
    
    //查询总记录数
    criteria.setProjection(Projections.rowCount());
    
    //执行查询
    List<Customer> list = criteria.list();
    Customer customer = (Customer) criteria.uniqueResult();
    

    Criteria查询的步骤:

    1. 获取Session对象,开启事务
    2. 获取查询对象Criteria
    3. 如果有限制条件使用Restrictions添加限制条件
    4. 如果是分页查询使用criteria.setFirstResult或setMaxResult方法设置分页参数
    5. Restrictions设置的限制条件添加到查询对象Criteria
    6. 执行查询

    Criteria查询测试:

    
        @Test
        // Criteria基本查询 :查询所有数据
        public void test() {
            // 1. 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 2.开启事务
            Transaction transaction = session.beginTransaction();
            // -----------------------------------------
            // 3. 业务逻辑:Criteria的基本查询
            // 3.1 获取Criteria查询对象
            Criteria criteria = session.createCriteria(Customer.class);
            // 3.2 执行查询
            List<Customer> list = criteria.list();
            // Customer customer = (Customer) criteria.uniqueResult();
    
            System.out.println(list);
            // -----------------------------------------
            // 4.提交事务
            transaction.commit();
            // getCurrentSession无需手动关闭
        }
    
        @Test
        // Criteria条查询
        public void test2() {
            // 1. 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 2.开启事务
            Transaction transaction = session.beginTransaction();
            // -----------------------------------------
            // 3. 业务逻辑:Criteria的条件查询
            // 3.1 获取Criteria查询对象
            Criteria criteria = session.createCriteria(Customer.class);
            // 3.2 设置查询限制条件
            // 参数1:持久化类中的属性 。 参数2:对应的值
            Criterion eq = Restrictions.eq("cid", 1);
            Criterion eq2 = Restrictions.eq("cname", "唐嫣");
    
            // 添加限制条件到查询对象中(可以添加多个限制条件)
            criteria.add(eq);
            criteria.add(eq2);
    
            // 3.3 执行查询操作
            // List<Customer> list = criteria.list();
            Customer customer = (Customer) criteria.uniqueResult();
            System.out.println(customer);
            // -----------------------------------------
            // 4.提交事务
            transaction.commit();
            // getCurrentSession无需手动关闭
        }
    
        @Test
        // Criteria分页查询
        public void test3() {
            // 1. 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 2.开启事务
            Transaction transaction = session.beginTransaction();
            // -----------------------------------------
            // 3. 业务逻辑:Criteria的分页查询
            // 3.1 获取Criteria查询对象
            Criteria criteria = session.createCriteria(Customer.class);
            //3.2 设置分页参数
            criteria.setFirstResult(4);//这里的4代表的是索引,第五条记录开始
            criteria.setMaxResults(2);//每页显示2条数据
            //3.3 执行查询
            List<Customer> list = criteria.list();
            System.out.println(list);
            // -----------------------------------------
            // 4.提交事务
            transaction.commit();
            // getCurrentSession无需手动关闭
        }
    
    
        @Test
        // Criteria查询总记录数
        public void test4() {
            // 1. 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 2.开启事务
            Transaction transaction = session.beginTransaction();
            // -----------------------------------------
            // 3. 业务逻辑:Criteria的分页查询
            // 3.1 获取Criteria查询对象
            Criteria criteria = session.createCriteria(Customer.class);
            // 3.2 获取总记录数
            criteria.setProjection(Projections.rowCount());
            //3.3 执行查询操作获取总记录数
            Long rows = (Long) criteria.uniqueResult();
            
            System.out.println(rows);
            // -----------------------------------------
            // 4.提交事务
            transaction.commit();
            // getCurrentSession无需手动关闭
        }
    

    附常用Restrictions的静态方法:

    常见的Restrictions的静态方法
        方法                  说明
        Restrictions.eq         =
        Restrictions.allEq      利用Map来进行多个等于的限制
        Restrictions.gt         >(greater than)
        Restrictions.ge         >=(greater than or equal)
        Restrictions.lt         < (less than)
        Restrictions.le         <=(less than or equal)
        Restrictions.between    BETWEEN
        Restrictions.like       LIKE
        Restrictions.in         in
        Restrictions.and        and
        Restrictions.or         or
        Restrictions.sqlRestriction 用SQL限定查询
    

    3. SQLQuery查询(原生的SQL语句查询)

    适用于复杂的多表业务查询

    常用API:

    //创建SQLQuery查询对象
    SQLQuery query = session.createSQLQuery(sql);
    
    //指定将结果集封装到某个对象中
    query.addEntity(Customer.class);
    
    //执行sql语句
    List<Customer> list = query.list();
    Customer customer = (Long)query.uniqueResult();
    

    SQLQuery查询测试:

    
        @Test
        // SQLQuery基本查询 :查询所有数据(返回Object数组的List)
        public void test() {
            // 1. 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 2.开启事务
            Transaction transaction = session.beginTransaction();
            // -----------------------------------------
    
            // 3. 业务逻辑:SQLQuery的基本查询:查询所有数据
            // 3.1 编写sql语句
            String sql = "select * from customer";
    
            // 3.2 获取SQLQuery查询对象
            SQLQuery query = session.createSQLQuery(sql);
    
            // 3.3 执行查询,返回的结果是一个List集合
            // 每条记录的查询结果作为Object数组的元素。将所有的记录Object数组存入List集合
            List<Object[]> list = query.list();
    
            for (Object[] objects : list) {
                System.out.println(Arrays.toString(objects));
            }
    
            // -----------------------------------------
            // 4.提交事务
            transaction.commit();
            // getCurrentSession无需手动关闭
        }
    
    
    
    img12.png
    
        @Test
        // SQLQuery基本查询 :查询所有数据(返回Customer的List)
        public void test2() {
            // 1. 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 2.开启事务
            Transaction transaction = session.beginTransaction();
            // -----------------------------------------
    
            // 3. 业务逻辑:SQLQuery的基本查询:查询所有数据
            // 3.1 编写sql语句
            String sql = "select * from customer";
            // 3.2 创建SQLQuery查询对象
            SQLQuery query = session.createSQLQuery(sql);
            // 3.3 指定将结果集封装到某个对象中
            query.addEntity(Customer.class);
            // 3.4 执行sql语句
            List<Customer> list = query.list();
    
            System.out.println(list);
    
            // -----------------------------------------
            // 4.提交事务
            transaction.commit();
            // getCurrentSession无需手动关闭
        }
    
    img13.png
    
        @Test
        // SQLQuery条件查询
        public void test3() {
            // 1. 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 2.开启事务
            Transaction transaction = session.beginTransaction();
            // -----------------------------------------
    
            // 3. 业务逻辑:SQLQuery的基本查询:查询所有数据
            // 3.1 编写sql语句
            String sql = "select * from customer where cid = ?";
            // 3.2 创建SQLQuery查询对象
            SQLQuery query = session.createSQLQuery(sql);
            // 设置参数的值。参数1:第几个数据。参数2:对应的值
            query.setParameter(0, 1);
            // 3.3 指定将结果集封装到某个对象中
            query.addEntity(Customer.class);
            // 3.4 执行sql语句
            Customer customer = (Customer) query.uniqueResult();
    
            System.out.println(customer);
    
            // -----------------------------------------
            // 4.提交事务
            transaction.commit();
            // getCurrentSession无需手动关闭
        }
    
    
        @Test
        // SQLQuery分页查询:方式1:传统sql语句
        public void test4() {
            // 1. 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 2.开启事务
            Transaction transaction = session.beginTransaction();
            // -----------------------------------------
    
            // 3. 业务逻辑:SQLQuery的基本查询:查询所有数据
            // 3.1 编写sql语句
            String sql = "select * from customer limit ?,?";
            // 3.2 创建SQLQuery查询对象
            SQLQuery query = session.createSQLQuery(sql);
    
            // 设置分页查询的参数
            query.setParameter(0, 4);
            query.setParameter(1, 2);
    
            // 3.3 指定将结果集封装到某个对象中
            query.addEntity(Customer.class);
            // 3.4 执行sql语句
            List<Customer> list = query.list();
    
            System.out.println(list);
    
            // -----------------------------------------
            // 4.提交事务
            transaction.commit();
            // getCurrentSession无需手动关闭
        }
    
    
        @Test
        // SQLQuery分页查询:方式2:setFirstResult()方法
        public void test5() {
            // 1. 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 2.开启事务
            Transaction transaction = session.beginTransaction();
            // -----------------------------------------
    
            // 3. 业务逻辑:SQLQuery的基本查询:查询所有数据
            // 3.1 编写sql语句
            String sql = "select * from customer";
            // 3.2 创建SQLQuery查询对象
            SQLQuery query = session.createSQLQuery(sql);
    
            // 设置分页查询的参数
            query.setFirstResult(4);// 设置从第几个数据开始查询,默认从0开始
            query.setMaxResults(2);// 设置每页显示的数据条数
    
            // 3.3 指定将结果集封装到某个对象中
            query.addEntity(Customer.class);
            // 3.4 执行sql语句
            List<Customer> list = query.list();
    
            System.out.println(list);
    
            // -----------------------------------------
            // 4.提交事务
            transaction.commit();
            // getCurrentSession无需手动关闭
        }
    
    

    4. 简单的CRM查询案例

    步骤:

    1. 搭建工程环境

    1. 创建工程,导入jar包,拷贝页面等文件
    
    2. 创建数据库表cst_customer
    
    3. 创建持久化类Customer
    
    4. 创建持久化类和数据库表的映射文件Customer.hbm.xml
    
    5. 创建Hibernate的核心配置文件hibernate.cfg.xml
    
    6. 完成核心配置文件中c3p0的配置,创建log4j.properties配置文件
    
    7. 编写工具类HibernateUtils,环境搭建完毕
    
    1.1.导入jar包

    包含有:

    • 数据库驱动
    • Hibernate必需包下的所有jar包
    • Hibernate的c3p0中的3个jar包
    • 日志集成slf4j和log4j的3个jar包
    • jstl.jar与standard.jar的jstl标签库jar包
    img14.png
    1.2 创建数据库表
    
        CREATE DATABASE hibernate_crm;
        
        USE hibernate_crm;
        
        CREATE TABLE `cst_customer` (
          `cust_id` BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
          `cust_name` VARCHAR(32) NOT NULL COMMENT '客户名称(公司名称)',
          `cust_user_id` BIGINT(32) DEFAULT NULL COMMENT '负责人id',
          `cust_create_id` BIGINT(32) DEFAULT NULL COMMENT '创建人id',
          `cust_source` VARCHAR(32) DEFAULT NULL COMMENT '客户信息来源',
          `cust_industry` VARCHAR(32) DEFAULT NULL COMMENT '客户所属行业',
          `cust_level` VARCHAR(32) DEFAULT NULL COMMENT '客户级别',
          `cust_linkman` VARCHAR(64) DEFAULT NULL COMMENT '联系人',
          `cust_phone` VARCHAR(64) DEFAULT NULL COMMENT '固定电话',
          `cust_mobile` VARCHAR(16) DEFAULT NULL COMMENT '移动电话',
          PRIMARY KEY (`cust_id`)
        ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
        
        INSERT INTO cst_customer(cust_id,cust_name) 
        VALUES(NULL,'唐嫣'),(NULL,'钟汉良'),(NULL,'林心如'),
        (NULL,'霍建华'),(NULL,'杨幂'),(NULL,'刘恺威'),
        (NULL,'高圆圆'),(NULL,'赵又廷');
    
    1.3 创建持久化类

    与数据库表基本保持一致,按照持久化类的编写规则编写。

    img15.png
    1.4 创建持久化类和数据库表的映射文件Customer.hbm.xml

    步骤:

    1. 在持久化类的同级目录下,创建xml文件,取名类名.hbm.xml。这里名字为:Customer.hbm.xml
    2. 导入DTD约束。从jar包中可以找到DTD的约束描述,copy过来
    3. 创建持久化类与数据库表的映射
    4. 创建持久化类中标识符OID与数据库表的主键的映射
    5. 创建类中普通属性与数据库表的字段的映射
    
        <?xml version="1.0" encoding="UTF-8"?>
        
        <!-- 建立持久化类Customer与数据库表customer的映射关系的配置文件 -->
        
        <!-- 引入DTD约束 -->
        <!DOCTYPE hibernate-mapping PUBLIC 
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
            
         <hibernate-mapping>
            <!-- 建立持久化类与表的映射关系 -->
            <class name="com.itdream.domain.Customer" table="cst_customer">
                <!-- 配置持久化类OID与表的主键的映射 -->
                <id name="cust_id" column="cust_id">
                    <!-- 配置主键生成策略 -->
                    <generator class="native"></generator>
                </id>
                
                <!-- 配置普通字段的映射关系 -->    
                <property name="cust_name" column="cust_name"></property>
            </class>
         </hibernate-mapping>
    
    1.5 创建Hibernate的核心配置文件hibernate.cfg.xml

    步骤:

    1. 在src目录下创建文件名为hibernate.cfg.xml的配置文件
    2. 导入DTD约束
    3. 配置Hibernate连接数据库的基本配置
    4. 配置Session与当前线程绑定
    5. 配置Hibernate基本属性,包括:
      1. Hibernate方言,根据这个配置Hiberante使用对应的sql语句,实现了跨数据库
      2. 显示SQL语句
      3. 格式化SQL语句
      4. 配置Hibernate对应数据库表的生成方式hbm2ddl
      5. 配置数据库的隔离级别
      6. c3p0连接池的配置
    6. 加载映射文件
    
        <?xml version="1.0" encoding="UTF-8"?>
        
        <!-- 导入Hibernate核心配置文件的DTD约束 -->
        <!DOCTYPE hibernate-configuration PUBLIC
            "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
        
        <!-- 配置Hibernate的核心配置文件 -->
        <hibernate-configuration>
            <session-factory>
                <!--1. Hibernate连接数据库的基本配置 -->
                <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
                <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_crm</property>
                <property name="hibernate.connection.username">root</property>
                <property name="hibernate.connection.password">root</property>
        
                <!--2. Session与本地线程绑定 -->
                <property name="hibernate.current_session_context_class">thread</property>
        
                <!--3. Hibernate的基本属性配置 -->
                <!-- Hibernate的方言配置,Hibernate根据这个配置生成对应的SQL语句,从而实现跨数据库 -->
                <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        
                <!-- 显示SQL语句 -->
                <property name="hibernate.show_sql">true</property>
                <!-- 格式化SQL语句 -->
                <property name="hibernate.format_sql">true</property>
                <!-- Hibernate对数据库表的生成方式,hbm2ddl -->
                <property name="hibernate.hbm2ddl.auto">none</property>
        
                <!-- 配置隔离级别 :4 读可重复(Repeatable read) -->
                <property name="hibernate.connection.isolation">4</property>
        
                <!-- C3P0连接池的配置 -->
                <property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
                <!-- 最小连接 -->
                <property name="hibernate.c3p0.min_size">5</property>
                <!-- 最大连接数 -->
                <property name="hibernate.c3p0.max_size">20</property>
                <!-- 连接超时时长 -->
                <property name="hibernate.c3p0.timeout">120</property>
                <!-- 每120秒检查空闲连接 -->
                <property name="hibernate.c3p0.idle_test_period">120</property>
                <!-- 最大statments数量 -->
                <property name="hibernate.c3p0.max_statements">120</property>
                <!-- 连接用完后,每次增加的连接数 -->
                <property name="hibernate.c3p0.acquire_increment">2</property>
                <!-- 每次都验证连接是否可用 -->
                <property name="hibernate.c3p0.validate">false</property>
        
                <!--4. 加载映射文件 -->
                <mapping resource="com/itdream/domain/Customer.hbm.xml" />
            </session-factory>
        </hibernate-configuration>  
    
    1.6 完成核心配置文件中c3p0的配置,创建log4j.properties配置文件

    1. c3p0的配置见上一条1.5中核心配置文件.

    2. log4j日志集成的配置文件log4j.properties

    在src目录下创建log4j.properties:

    
        ### direct log messages to stdout ###
        log4j.appender.stdout=org.apache.log4j.ConsoleAppender
        log4j.appender.stdout.Target=System.err
        log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
        log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
        
        ### direct messages to file mylog.log ###
        log4j.appender.file=org.apache.log4j.FileAppender
        log4j.appender.file.File=d:\\mylog.log
        log4j.appender.file.layout=org.apache.log4j.PatternLayout
        log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
        
        ### set log levels - for more verbose logging change 'info' to 'debug' ###
        log4j.rootLogger=info, stdout,file
    
    1.7 编写工具类HibernateUtils
    
        /**
         * 初始化SessionFactory,提供Session对象的Hibernate工具类
         */
        public class HibernateUtils {
        
            private static SessionFactory sf = null;
        
            static {
                // 创建Hibernate配置对象,加载src目录下默认的hibernate.cfg.xml配置文件
                Configuration configuration = new Configuration().configure();
                // 创建SessionFactory工厂对象
                sf = configuration.buildSessionFactory();
        
                // 程序关闭销毁SessionFactory对象
                Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        
                    @Override
                    public void run() {
                        System.out.println("程序关闭销毁SessionFactory对象");
                        sf.close();
                    }
                }));
            }
        
            /**
             * 获取Session对象的方法
             */
            public static Session openSession() {
                return sf.openSession();
            }
        
            /**
             * 获取与当前线程绑定的Session对象
             */
            public static Session getCurrentSession() {
                return sf.getCurrentSession();
            }
        }
    
    

    2. 代码实现,使用Hibernate的查询语句查询所有Customer

    2.1 mennu.jsp修改a标签跳转
        <TD class=menuSmall><A class=style2 href="customer?method=list"target=main>- 客户列表</A></TD>
    
    2.2 创建Servlet,映射url-pattern为servlet
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            // 获取传递过来的method参数
            String method = request.getParameter("method");
    
            if ("list".equals(method)) { // 如果是list,就查询所有Customer客户
    
                try {
                    // 调用service层
                    CustomerService service = new CustomerServiceImpl();
                    List<Customer> list = service.findAllCustomers();
    
                    // 存入request域
                    request.setAttribute("list", list);
                    System.out.println(list);
                    
                    // 转发
                    request.getRequestDispatcher("/jsp/customer/list.jsp").forward(request, response);;
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
        }
    
    2.3 CustomerService层,调用dao层查询所有Customer
    
        @Override
        public List<Customer> findAllCustomers() throws SQLException {
            //调用dao层查询所有Customer用户
            CustomerDAO dao = new CustomerDAOImpl();
            return dao.findAllCustomers();
        }
    
    2.4 CustomerDAO层,查询所有Customer返回

    使用Criteria与HQL两种查询方式,单表查询推荐使用Criteria查询

    
        @Override
        // 方式1:显示所有Customer的方法:Criteria查询(单表查询简单)
        public List<Customer> findAllCustomers() throws SQLException {
            // 1. 获取配置对象加载配置文件
            // 2. 获取SessionFactory对象
            // 3. 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 4. 开启事务
            Transaction transaction = session.beginTransaction();
            // 5. 业务逻辑 :查询所有Customer对象,单表查询使用Criteria较为简单
            // 5.1 创建Criteria查询对象
            Criteria criteria = session.createCriteria(Customer.class);
            // 5.2 如果有条件就添加限制条件Restrictions,然后添加到查询对象中
            // 5.3 执行查询
            List<Customer> list = criteria.list();
    
            // 6. 提交事务
            transaction.commit();
            // 7. getCurrentSession()无需手动关闭Session对象
            return list;
        }
         
    
        /*@Override
        // 方式2:显示所有Customer的方法:HQL查询(常用于不是很复杂的多表查询)
        public List<Customer> findAllCustomers() throws SQLException {
    
            // 1. 获取配置对象加载配置文件
            // 2. 获取SessionFactory对象
            // 3. 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 4. 开启事务
            Transaction transaction = session.beginTransaction();
            // 5. 业务逻辑 : 查询所有Customer对象,单表查询使用Criteria较为简单
            // 5.1  编写HQL语句,HQL语句中出现的都是持久化类中的属性,而不是数据库表中的字段
            String hql = "from Customer";
            // 5.2 创建HQL的Query对象
            Query query = session.createQuery(hql);
            // 5.3  如果hql语句中有参数使用setParameter()设置参数
            // 5.4  执行hql语句
            List<Customer> list = query.list();
            // 6. 提交事务
            transaction.commit();
            // 7. getCurrentSession()无需手动关闭Session对象
            return list;
        }*/
    
    2.5 查询结果,list页面解析(这里省略)

    查询结果图:

    img16.png

    相关文章

      网友评论

        本文标题:二、加深理解Hibernate框架

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