二、加深理解Hibernate框架

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

一、 Hibernate的持久化类

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

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

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

2. Hibernate的主键生成策略

  • 自然主键
    • 把具有业务含义的字段作为主键。
  • 代理主键
    • 把不具备业务含义的字段作为主键,与对象本身无关,该字段一般取名"ID".
二、加深理解Hibernate框架_第1张图片
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没有了关联,是托管态

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

二、加深理解Hibernate框架_第2张图片
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对象。

二、加深理解Hibernate框架_第3张图片
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的事务管理


4

在使用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进行如下配置:

thread

配置后,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 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 list = query.list();
            System.out.println(list);
            // -----------------------------------------
            // 4.提交事务
            transaction.commit();
            // getCurrentSession无需手动关闭
        }
    }

分页查询结果:

二、加深理解Hibernate框架_第4张图片
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 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 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 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 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 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 list = query.list();

        for (Object[] objects : list) {
            System.out.println(Arrays.toString(objects));
        }

        // -----------------------------------------
        // 4.提交事务
        transaction.commit();
        // getCurrentSession无需手动关闭
    }


二、加深理解Hibernate框架_第5张图片
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 list = query.list();

        System.out.println(list);

        // -----------------------------------------
        // 4.提交事务
        transaction.commit();
        // getCurrentSession无需手动关闭
    }
二、加深理解Hibernate框架_第6张图片
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 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 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包
二、加深理解Hibernate框架_第7张图片
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 创建持久化类

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

二、加深理解Hibernate框架_第8张图片
img15.png
1.4 创建持久化类和数据库表的映射文件Customer.hbm.xml

步骤:

  1. 在持久化类的同级目录下,创建xml文件,取名类名.hbm.xml。这里名字为:Customer.hbm.xml
  2. 导入DTD约束。从jar包中可以找到DTD的约束描述,copy过来
  3. 创建持久化类与数据库表的映射
  4. 创建持久化类中标识符OID与数据库表的主键的映射
  5. 创建类中普通属性与数据库表的字段的映射

    
    
    
    
    
    
        
     
        
        
            
            
                
                
            
            
                
            
        
     
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. 加载映射文件

    
    
    
    
    
    
    
        
            
            com.mysql.jdbc.Driver
            jdbc:mysql://localhost:3306/hibernate_crm
            root
            root
    
            
            thread
    
            
            
            org.hibernate.dialect.MySQLDialect
    
            
            true
            
            true
            
            none
    
            
            4
    
            
            org.hibernate.c3p0.internal.C3P0ConnectionProvider
            
            5
            
            20
            
            120
            
            120
            
            120
            
            2
            
            false
    
            
            
        
      
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标签跳转
    - 客户列表
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 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 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 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 list = criteria.list();

        // 6. 提交事务
        transaction.commit();
        // 7. getCurrentSession()无需手动关闭Session对象
        return list;
    }
     

    /*@Override
    // 方式2:显示所有Customer的方法:HQL查询(常用于不是很复杂的多表查询)
    public List 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 list = query.list();
        // 6. 提交事务
        transaction.commit();
        // 7. getCurrentSession()无需手动关闭Session对象
        return list;
    }*/
2.5 查询结果,list页面解析(这里省略)

查询结果图:

二、加深理解Hibernate框架_第9张图片
img16.png

你可能感兴趣的