二、加深理解Hibernate框架

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容

  • Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库...
    兰缘小妖阅读 1,195评论 1 18
  • 本文包括:1、Hibernate的持久化类2、Hibernate 持久化对象的三个状态(难点)3、Hibernat...
    廖少少阅读 1,435评论 0 13
  • 1.主键生成策略 1.1 主键的两种类型 自然主键:把数据表中的某一业务字段作为表的主键。如一张用户表中,把用户的...
    Wizey阅读 538评论 0 3
  • Hibernate: 一个持久化框架 一个ORM框架 加载:根据特定的OID,把一个对象从数据库加载到内存中OID...
    JHMichael阅读 1,958评论 0 27
  • 记得刚来到中传还是2015年的夏天,兴奋的我来到孔子雕像前遐想,两年后我的母校就是中国传媒大学了,想到这里心里不免...
    新媒体小业阅读 198评论 0 1