【Java中级】8.0 SSH之Hibernate框架(四)——持久化类的编写规则、一级缓存、事务管理及其他API...

1.0 什么是持久化类
  1. 持久化:将内存的一个对象持久化到数据库中的过程。Hibernate框架就是用来进行持久化的框架。
  2. 持久化类:将内存中的数据永久存储到关系型数据库中。
  3. 其实,所谓的持久化类指的是一个Java类与数据库表建立了映射关系,那么这个类称为持久化类。可以理解为,持久化类就是一个Java类有了一个映射文件与数据库的表建立了关系。
  4. 持久化类 = Java类 + 映射文件
2.0 持久化类的编写规则
    1. 对持久化类提供一个无参的构造方法: Hibernate底层需要使用反射生成实例。
    1. 属性需要私有,对私有属性提供public的get和set方法: Hibernate中,获取设置对象的值。
    1. 对持久化类提供一个唯一标识OID与数据库主键对应: 在Java中,通过对象的地址区分是否是同一对象。数据库中通过主键确定是否是同一条记录。在hibernate中,通过持久化的OID的属性区分是否是同一对象。
    1. 持久化类中,属性尽量使用包装类类型: 今晚基本的数据类型默认值是0,0会有很多的歧义。包装类的类型默认值是null,如果数据库中的数据,没有插入数据,则会显示默认值null,如果输入数据值就是0,在我们查阅数据库时,如果使用基本类型,就无法判断,是否是本身值为0,还是没有插入数据。
    1. 持久化类不要使用final进行修饰: 延迟加载本身是hibernate一个优化的手段,返回的是一个代理对象(javassist),可以对没有实现接口的类产生代理,使用了非常底层字节码增强技术,继承这个类进行代理。如果不能被继承,不能产生代理对象,延迟加载也会失效。Load方法和get方法一致。

针对3小点,相当于在Customer.java类中(Javabean类)和Customer.hbm.xml映射文件中主键的绑定。

public class Customer {
    private Long cust_id;//客户id
    ……
<!-- Id:建立类中的属性与表中的主键对应 -->
        <id name="cust_id" column="cust_id">
            <!-- name:在类中的名字 column:表中的字段;此处为一样的名称 -->
            <generator class="native" />
            <!-- 组件都有一种生成策略,此处使用一种本地策略。 -->
        </id>

针对4小点,我们在Customer.java类中(Javabean类),尽量使用Long、Integer、Float、Double、Boolean等包装类类型,不要使用int、long、float、double、boolean。

public class Customer {
    private Long cust_id;//客户id
    private String cust_name;//客户的名称 
    private String cust_source;//客户的来源
    private String cust_industry;//客户所属行业
    private String cust_level;//客户的级别
    private String cust_phone;//客户的固定电话
    private String cust_mobile;//客户的移动电话
    ……

针对5小点,如下代码不建议:

public final class Customer {
    private Long cust_id;//客户id
    private String cust_name;//客户的名称 
    private String cust_source;//客户的来源
    private String cust_industry;//客户所属行业
    private String cust_level;//客户的级别
    private String cust_phone;//客户的固定电话
    private String cust_mobile;//客户的移动电话
    ……

当JavaBean类用 final关键词修饰时,Load方法和get方法一致(延迟加载失效)。

3.0 主键生成策略

在之前的文章中,针对Hibernate的使用进行大量的讲解,其中在映射文件Customer.hbm.xml中,有如下代码:


        <!-- Id:建立类中的属性与表中的主键对应 -->
        <id name="cust_id" column="cust_id">
            <!-- name:在类中的名字 column:表中的字段;此处为一样的名称 -->
            <generator class="native" />
            <!-- 组件都有一种生成策略,此处使用一种本地策略。 -->
        </id>

其中<generator class="native" />语句,并没有做过多说明。这里设置主键的生成策略。

2.1 主键的分类
  • 我们在建立数据库的时候必须设置主键,主键是对数据库的一种约束。
  • 主键分为两种。一种是自然主键,一种叫代理主键。
1. 自然主键

自然主键:主键的本身就是表中的一个字段。(实体中的一个具体的属性)

  • 比如,创建一个人员表,人员都会有一个身份证号码(唯一的不可重复),使用身份证号作为主键。这种主键就称为自然主键。再比如学生表学生的学号,也是同样如此。
. 代理主键
  • 代理主键:组件的本身不是表中必须的一个字段(不是实体中的某个具体的属性)
  • 比如,创建一个人员表,没有使用人员的身份证号,用了一个与这个表不相关的字段ID(PNO:Parent Number),这种主键称为代理主键。
  • 在实际开发中,尽量使用代理主键。
  • 一旦自然主键参与到业务逻辑中后期,有可能需要修改源代码。
  • 好的程序设计满足OCP原则:对程序的扩展是open的,对修改源码是close的。

在实际开发过程中一般不允许用户手动设置主键。一般将主键交给数据库手动编写程序进行设置。在hibernate中,为了减少程序的编写,提供了很多主键的生成策略。

  1. increment : Hibernate中提供的自动增长机制,适用于short、int、long类型主键。
  • 只有在没有其他进程网同一张表中插入数据时才能用。在集群下不要使用。(换句话说:在单线程程序中使用,不要在多线程程序中使用。)
  • 原理:Hibernate首先发送一条语句,set max(id) from表;然后让id+1作为下一条记录的主键。这样就存在线程问题。
  1. identity:适用short、int、long类型的主键,使用的是数据库底层的自动增长机制,适用于有自动增强机制的数据库(MySQL、MSSQL),但是Oracle没有自动增长。
  2. sequence:适用short、int、long类型的主键,采用的是序列的方式(Oracle、DB2、PostgreSQL、SAP、DB、Mckoi支持序列)。MySQL就不能使用sequence。
  3. uuid:适用于字符串类型主键。使用hibernate中的随机方式生成字符串主键。
  4. native:本地策略。可以在identity和sequence之间进行自动切换。意思就是如果底层采用的是MySQL,配置native相当于identity;如果底层使用的是Oracle,配置native相当于sequence。也就是说设置成native程序员就不用去管使用的MySQL还是Oracle。
  5. assigned:Hibernate放弃外键的管理,需要通过手动编写程序或者用户自己设置。
  6. foreign(了解即可):外部的。在一对一的一种关联映射的情况下使用。
  • 这种策略用的很少,但是当我们主键一对一时,可以设置为foreign。比如,这个表的主键是另外一个表的主键,外部的一个表拿过来的,主键和主键一一对应,不然用不上foreign策略。
3.0 持久化类的三种状态

Hibernate是持久层框架,通过持久化内完成ORM操作。Hibernate为了更好地管理持久化类,将持久化类分成三种状态——瞬时态(transient)、 持久态(persistent)、 托管态(游离态、离线态)(detached)。

3.1 瞬时态(transient)

这种对象没有唯一的OID,没有被session管理称为瞬时态对象。

3.2 持久态(persistent)

这种对象有唯一的OID,被session管理,称之为持久态对象。

持久化类的持久态的对象,可以自动更新数据。

3.3 托管态(游离态、离线态)(detached)

这种对象有唯一的标识OID,没有被session管理,称之为托管态对象。

3.4 怎么区分不同态

在前几篇文章的基础上,新建一个项目,导入相应的配置文件,最终目录如下:


16102290-2a4fb13679a65d9f.png
image.png

在我们的主测试文件中,先设置好模板:

package com.edp.hibernate.demo1;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.edp.hibernate.utils.HibernateUtils;

/**
 * 
 * @Title: HibernateDemo1.java
 * @Package com.edp.hibernate.demo1
 * @author EdPeng
 * @version 创建时间 2020年1月24日上午9:28:07
 * @Description Hibernate主键生成策略
 * @version V1.0
 */

public class HibernateDemo1 {
    @Test
    //保存一条记录
    public void demo1() {
        Session session = HibernateUtils.openSession();
        Transaction transaction = session.beginTransaction();
        //编写操作代码。
        transaction.commit();
        session.close();
    }
}

为了与之前的项目相隔开,这里新建一个数据库hibernate0124,并修改好相应的配置文件,在数据库手动新建新的数据库。
怎么区分不同态,如下代码:

package com.edp.hibernate.demo1;

import java.io.Serializable;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.edp.hibernate.utils.HibernateUtils;

/**
 * 
 * @Title: HibernateDemo1.java
 * @Package com.edp.hibernate.demo1
 * @author EdPeng
 * @version 创建时间 2020年1月24日上午9:28:07
 * @Description Hibernate的持久化类的三种状态。
 * @version V1.0
 * 
 */

public class HibernateDemo1 {
    @Test
    //三种状态的区分
    public void demo1() {
        Session session = HibernateUtils.openSession();
        Transaction transaction = session.beginTransaction();
        
        Customer customer = new Customer();//瞬时态对象:没有唯一标识OID,没有被session管理
        customer.setCust_name("李走鸟");  
        Serializable id = session.save(customer);//持久态对象:有唯一标识OID,被session管理
        
        session.get(Customer.class, id);
        transaction.commit();
        session.close();
        System.out.println("客户名称:"+customer.getCust_name());//托管态对象:有唯一标识OID,没有被session管理
    }
}
3.5 持久化类的状态转换
对象名称获得方法转换方式转换代码
瞬时态对象Customer customer = new Customer();瞬时→持久save(Object obj)、saveOrUpdate(Object obj)
瞬时→托管Customer.setCust_id(1l)
持久态对象get()、load()、find()、iterate()持久→瞬时delete(Object obj)
Customer customer = session.get(Customer.class, 1l);持久→托管close()、clear()、evict(Object obj)
托管态对象Customer customer = new Customer(); customer.setCust_id(1l);托管→持久update(Object obj)、saveOrUpdate(Object obj)
托管→瞬时Customer.setCust_id(null);

在我们之前的代码中,直接修改数据库表中的具体内容,就是托管→持久:

    @Test
    // 修改操作
    public void demo3() {
        Session session = HibernateUtils.openSession();
        Transaction txTransaction = session.beginTransaction();

//      
        // 直接创建对象,进行修改
        Customer customer = new Customer();//瞬时态对象
        customer.setCust_id(1l);//托管态对象
        customer.setCust_name("张三丰");
        session.update(customer);//持久态对象

        txTransaction.commit();
        session.close();
    }
3.6 持久态对象特性

持久化类持久态对象自动更新数据库

    @Test
    //持久态对象自动更新数据库
    public void demo2() {
        Session session = HibernateUtils.openSession();
        Transaction transaction = session.beginTransaction();
        
        //获得持久态对象
        Customer customer = session.get(Customer.class, 1l);
        customer.setCust_name("不知火舞");
//      session.update(customer);这句代码可以不用编写。持久态对象有自动更新数据库的能力。
        
        transaction.commit();
        session.close();
    }
}

执行结果:

16102290-2024238665352552.png
image.png

执行后的数据库:
16102290-a4ff25beaa3c9b11.png
image.png

持久化类持久态对象之所以能自动更新数据库,在于 底层原理依赖于Hibernate的一级缓存。

4.0 Hibernate的一级缓存
4.1 基本概念
  • 缓存:是一种优化的方式,将数据存入到内存中,使用的时候直接从缓存(内存)中获取,不用通过存储源。
  • Hibernate主要提供两个级别的缓存。
  • Hibernate框架中提供了两种优化手段:缓存、抓取策略。
  • Hibernate的一级缓存: 称为Session级别的缓存,一级缓存生命周期与Session一致(一级缓存是由Session中的一系列Java集成构成),一级缓存是自带、不可卸载的。
  • Hibernate的二级缓存:是SessionFactory级别的,需要配置的缓存。目前在企业开发中基本不用,有其他替代方案。
4.2 对一级缓存的说明

只要用Hibernate,就会使用到一级缓存,Hibernate的一级缓存的作用就是减少对数据库的访问次数。

4.3 Hibernate一级缓存的特点
  1. 当应用程序调用Session接口的save()、update()、saveOrUpdate()时,如果Session缓存中没有相应的对象。Hibernate就会自动把从数据库中查询到的相应对象信息加入到一级缓存中去。
  1. 当调用Session接口的load()、get()方法,以及Query接口的list()、iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询的对象,再去数据库中查询对应对象,并添加到一级缓存中。
  1. 当调用Session的close()方法时,Session缓存会被清空。
4.4 代码验证一级缓存的存在
package com.edp.hibernate.demo1;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.edp.hibernate.utils.HibernateUtils;

public class HibernateDemo2 {
    @Test
    // 证明一级缓存的存在
    public void demo1() {
        Session session = HibernateUtils.openSession();
        Transaction txTransaction = session.beginTransaction();

        Customer customer = session.get(Customer.class, 1l);// 发送SQL语句
        System.out.println(customer);
        // 正常情况下执行两次查询,应该会发送两次查询语句。
        Customer customer2 = session.get(Customer.class, 1l);// 缓存中读取数据
        System.out.println(customer2);
        
        System.out.println(customer == customer2);

        txTransaction.commit();
        session.close();
    }
}

如以下执行结果可知,查询一次之后第二次查询并没有执行。


16102290-8182d443c75f1307.png
image.png

再举一个例子

package com.edp.hibernate.demo1;

import java.io.Serializable;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.edp.hibernate.utils.HibernateUtils;

public class HibernateDemo2 {
    @Test
    // 证明一级缓存的存在
    public void demo1() {
        Session session = HibernateUtils.openSession();
        Transaction txTransaction = session.beginTransaction();

//      Customer customer = session.get(Customer.class, 1l);// 发送SQL语句
//      System.out.println(customer);
//      // 正常情况下执行两次查询,应该会发送两次查询语句。
//      Customer customer2 = session.get(Customer.class, 1l);// 缓存中读取数据
//      System.out.println(customer2);
//      
//      System.out.println(customer == customer2);

        Customer customer = new Customer();
        customer.setCust_name("猪八戒");
        Serializable idSerializable=session.save(customer);
        
        Customer customer2 = session.get(Customer.class, idSerializable);//不发送SQL语句
        System.out.println(customer2);
        
        txTransaction.commit();
        session.close();
    }
}

执行结果,可见第二次查询,不发送查询语句。


16102290-a38f7f57a51675d2.png
image.png
4.5 Hibernate一级缓存的内部结构
1. 一级缓存中特殊区域:快照区

Hibernate一级缓存主要由Session对象中actionQueue方法和persistenceContext方法参与。

   @Test
   // 一级缓存的快照区
   public void demo2() {
       Session session = HibernateUtils.openSession();
       Transaction txTransaction = session.beginTransaction();

       Customer customer = session.get(Customer.class, 1l);// 发送SQL语句,同时放入到一级缓存中
       customer.setCust_name("王陆");

       txTransaction.commit();
       session.close();
   }
2. 验证一级缓存的内部原理

当我们断点调试时:


16102290-cee4d92ffca1d83d.png
image.png

16102290-959e1275ed93b1ca.png
image.png

16102290-532eb6533d642225.png
image.png

可以看到,actionQueue(活动队列)中,各项参数都为0(size=0),当手动进行插入、更新、删除时,这其中才会有数据。


16102290-a586fce1612cc4d0.png
image.png

persistenceContext(持久化的上下文)。当调试执行到如下语句时:
// 发送SQL语句,同时放入到一级缓存中
        Customer customer = session.get(Customer.class, 2l);//向数据库查询, 即发送SQL语句,同时放入到一级缓存当中。

向数据库查询, 即发送SQL语句,同时放入到一级缓存当中。
调试到下一句:

16102290-ef32b56d43c404f9.png
image.png

persistenceContext中可以看到,一级缓存中已经有数据了。Hibernate会对缓存 “拍个照片”,存起来。
通知会存储快照区一份。
16102290-fbe51ba723327540.png
image.png

16102290-e3b61c58a54f6e72.png
image.png

完整代码:

package com.edp.hibernate.demo1;

import java.io.Serializable;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.edp.hibernate.utils.HibernateUtils;

public class HibernateDemo2 {
    @Test
    // 证明一级缓存的存在
    public void demo1() {
        
        Session session = HibernateUtils.openSession();
        Transaction txTransaction = session.beginTransaction();

//      Customer customer = session.get(Customer.class, 1l);// 发送SQL语句
//      System.out.println(customer);
        
//      // 正常情况下执行两次查询,应该会发送两次查询语句。
//      Customer customer2 = session.get(Customer.class, 1l);// 缓存中读取数据
//      System.out.println(customer2);
        
//      System.out.println(customer == customer2);

        Customer customer = new Customer();
        customer.setCust_name("猪八戒");
        Serializable idSerializable = session.save(customer);

        Customer customer2 = session.get(Customer.class, idSerializable);
        System.out.println(customer2);

        txTransaction.commit();
        session.close();
    }

    @Test
    // 一级缓存的快照区
    public void demo2() {

        Session session = HibernateUtils.openSession();
        Transaction txTransaction = session.beginTransaction();

        // 发送SQL语句,同时放入到一级缓存中
        Customer customer = session.get(Customer.class, 2l);
        customer.setCust_name("王陆");

        txTransaction.commit();
        session.close();

    }
    
    @Test
    // 一级缓存的清空
    public void demo3() {

        Session session = HibernateUtils.openSession();
        Transaction txTransaction = session.beginTransaction();

        // 发送SQL语句,同时放入到一级缓存中
        Customer customer = session.get(Customer.class, 2l);//发送SQL语句
        Customer customer2 = session.get(Customer.class, 2l);//不发送SQL语句
        session.clear();//在此句执行时,清空一级缓存
        Customer customer3 = session.get(Customer.class, 2l);//发送SQL语句

        txTransaction.commit();
        session.close();//在此句执行时,清空一级缓存
    }
}
5.0 Hibernate的事务管理
5.1 什么是事务

事务:事务指的是逻辑上的一组操作,组成这组操作的各个逻辑单元要么全部成功,要么全部失败

5.2 事务的特性

原子性:最小单位,不可分割。
一致性:事务执行前后,数据完整性保持一致。
持久性:一个事物执行完毕,数据就持久到数据库中(提交回滚)。
隔离性:事务执行过程中,不应该受到其他事物干扰。

5.3 如果不考虑隔离性,引发安全性问题

写的问题(了解)

  • 引发两类丢失更新
    读的问题
  • 脏读:一个事务读到另一个事务未提交的数据。
  • 不可重复读:一个事物读到另一个事务已经提交update数据,导致在前一个事务多次查询结果不一致。
  • 虚读:一个事物读到另一个事物已经提交的insert数据,导致在前一个事务多次查询结果不一致。
16102290-6574330570b9c1cb.png
image.png

16102290-05592db7fe362290.png
image.png

16102290-e3c14456551de8b9.png
image.png
5.4 设置隔离级别
16102290-b61de020749b5fb3.png
image.png
16102290-0af0536875acdf66.png
image.png

Serializable:安全性最高,但效率最低。
Oracle默认用Read committed
MySQL默认用Repeatable read

5.4 Hibernate怎么设置事务隔离级别

hibernate.cfg.xml文件配置:

        <!-- 事务隔离级别 
             hibernate.connection.isolation 
             1->Read uncommitted isolation 
             2->read committed isolation 
             4->Repeatable read isolation(MySQL默认级别)
             8->Serializable isolation -->
        <property name="hibernate.connection.isolation">4</property>
6.0 Hibernate解决Service的事务管理—— Session的线程绑定问题

事务就是加载Service层,所以事务又被称为Service层事务。

6.1 为什么把事务加载在业务层 ?
16102290-590d0f99a43578f0.png
image.png
  • 在DAO层,数据操作是一个一个执行。
  • 但是一个事物涉及,多个数据操作,同时必须保证,多个操作都是由同一个JDBC中的connection对象(Hibernate中是Session对象)完成。
  • 这时候就需要进行事务管理。


    16102290-9be2a7386f53dd63.png
    image.png
  • 保证连接对象是同一个有两种办法——一种是向下传递;一种是使用ThreadLocal对象,即线程绑定
  • getCurrentSession()方法默认不可用,必须通过配置之后,才能使用。
6.2 改写工具类
package com.edp.hibernate.utils;

import org.hibernate.Session;

/**
 * 
 * @author EdPeng
 * @version 创建时间 2020年1月23日上午12:39:24
 * 类说明 Hibernate工具类
 */

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {
    public static final Configuration cfg;
    public static final SessionFactory sf;
    
    static {
        cfg = new Configuration().configure();
        sf = cfg.buildSessionFactory();
    }

    public static Session openSession() {
        return sf.openSession();
    }
    
    public static Session getCurrentSession() {
        return sf.getCurrentSession();
    }
}

增加如下代码:


16102290-b1e7d33dfaeff1c0.png
image.png
6.2 配置hibernate.cfg.xml文件
        <!-- 配置当前线程绑定的Session -->
        <!-- 用于指定Session的管理方式
             thread:Session对象的生命周期与本地线程绑定
             jta:Session对象的生命周期与JTA事务(跨数据库的事务)绑定
             managed:Hibernate委托程序来管理Session对象的生命周期-->
        <property name="hibernate.current_session_context_class">thread</property>
  • 这样配置就会创建一个Session绑定到线程当中。
  • Session.getCurrentSession();不需要再创建Session.close();线程结束会自动销毁Session,而且写Session.close();会报错。
7.0 Hibernate的其他API
7.1 Query :HQL面向对象的查询。
16102290-e88d795e692941c2.png
image.png
  1. 查询所有:
package com.edp.hibernate.demo1;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
import org.junit.Test;

import com.edp.hibernate.utils.HibernateUtils;

public class HibernateDemo4 {
    @Test
    //Query查询
    public void demo1() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction txTransaction = session.beginTransaction();
        
        //通过Session获得Query接口
        //查询所有数据
        String hqlString= "from Customer";
        Query query = session.createQuery(hqlString);
        List<Customer> list= query.list();
        for (Customer customer : list) {
            System.out.println(customer);
        }
        txTransaction.commit();
    }
}

执行结果:


16102290-b929b682b524be4f.png
image.png
  1. 查所有姓王的:
package com.edp.hibernate.demo1;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
import org.junit.Test;

import com.edp.hibernate.utils.HibernateUtils;

public class HibernateDemo4 {
    @Test
    // Query查询
    public void demo1() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction txTransaction = session.beginTransaction();

        // 通过Session获得Query接口
        // 1.查询所有数据
//      String hqlString= "from Customer";//简单查询
//      Query query = session.createQuery(hqlString);
        
        // 2.查询所有姓王的
        // cust_name是类的属性名称,不是表中的字段名称
        // (1) hibernate4.1之前语句
//      String hqlString= "from Customer where cust_name like ?";//条件查询
//      Query query = session.createQuery(hqlString);
        // setParameter()方法就不需要管数据说明类型,Hibernate数据下标从0开始的。
//      query.setParameter(0, "王%");
        
        // (2) hibernate4.1之后语句(1)
//      String hqlString = "from Customer where cust_name like :mycust_name";
//      Query query = session.createQuery(hqlString);
//      query.setParameter("mycust_name", "王%");
        
        // (3) hibernate4.1之后语句(2)
        String hqlString = "from Customer where cust_name like ?0";
        Query query = session.createQuery(hqlString);
        // setParameter()方法就不需要管数据说明类型,Hibernate数据下标从0开始的。
        query.setParameter(0, "王%");
        
        List<Customer> list = query.list();
        for (Customer customer : list) {
            System.out.println(customer);
        }

        txTransaction.commit();
    }
}

运行结果:


16102290-60929b5769c8995d.png
image.png
  1. 分页查询

    @Test
    // Query查询
    public void demo2() {
        //分页查询
        Session session = HibernateUtils.getCurrentSession();
        Transaction txTransaction = session.beginTransaction();
    
        String hqlString = "from Customer";
        Query query = session.createQuery(hqlString);
        //分页查询:从哪开始,每页显示几条
        query.setFirstResult(0);//从哪开始查询
        query.setMaxResults(3);//每页显示多少条记录
        
        List<Customer> list = query.list();
        for (Customer customer : list) {
            System.out.println(customer);
        }
        
        txTransaction.commit();
    }

数据库数据:


16102290-d65a520f3739e63c.png
image.png

执行结果为:


16102290-df5f4e9305b118b4.png
image.png

可见,查询语句变成了含limit的语句。如果想查询第二页,只需要修改代码:
        //分页查询:从哪开始,每页显示几条
        query.setFirstResult(3);//从哪开始查询
        query.setMaxResults(3);//每页显示多少条记录

执行结果为:


16102290-193a7f1c1ab5238a.png
image.png

此时含limit的语句,limit后面有2个问号。而且,Hibernate查询和底层数据库无关,当换数据库时不需要修改查询代码。

7.2 Criteria:更适合条件检索

Criteria:QBC(Query By Criteria)
更加面向对象的一种查询方式(基本没有查询语句)。
Criteria查询为HQL、JPQL和本地SQL查询提供了一种类型安全的替代方法。

    @Test
    // Criteria查询:更加面向对象的一种查询方式
    public void demo3() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction txTransaction = session.beginTransaction();
        
        //查询所有
        //通过session获得Criteria的对象
//      Criteria criteria = session.createCriteria(Customer.class);
//      List<Customer> list = criteria.list();
        
        //条件查询,Hibernate5.2之前
        Criteria criteria = session.createCriteria(Customer.class);
        criteria.add(Restrictions.like("cust_name", "孙%"));
//      criteria.add(Restrictions.like("cust_name", "孙",MatchMode.START));
        //MatchMode.ANYWHERE 相当于 %孙%
        //MatchMode.EXACT 相当于 孙
        //MatchMode.END 相当于 %孙
        //MatchMode.START 相当于 孙%
        List<Customer> list = criteria.list();
        
        for (Customer customer : list) {
            System.out.println(customer);
        }
        
        txTransaction.commit();
    }

Hibernate5.2之后废弃了org.Hibernate.Criteria API,转移到javax.persistence.CriteriaQuery包(JPA API)下。

  • 第一步,需要实现javax.persistence.criteria.CriteriaBuilder接口才能进行条件查询。它的作用是一个工厂,为所有的个体制定标准。
  • 第二步,通过调用javax.persistence.EntityManagerFactoryjavax.persistence.EntityManagergetCriteriaBuilder()方法,可以获得javax.persistence.CriteriaBuilder实例。
  • 第三步,获取javax.persistence.CriteriaQuery
    • 有以下三种方法:
<T> CriteriaQuery<T> createQuery( Class<T> resultClass )
CriteriaQuery<Tuple> createTupleQuery()
CriteriaQuery<Object> createQuery()

根据查询结果的预期类型,每种方法都有不同的用途。
1.查询所有

        //2. Hibernate5.2之后
        //2.1查询所有
        CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        //1) 获取CriteriaQuery对象
        CriteriaQuery<Customer> createQuery = criteriaBuilder.createQuery(Customer.class);
        //2) 指定根条件
//      createQuery.from(Customer.class);
        //3) 通过session执行查询
        List<Customer> list = session.createQuery(createQuery).list();

2.条件查询

    public void demo3() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction txTransaction = session.beginTransaction();

        //1. Hibernate5.2之前
        // 通过session获得Criteria的对象
        // 1.1 查询所有
//      Criteria criteria = session.createCriteria(Customer.class);
//      List<Customer> list = criteria.list();

        // 1.2 条件查询
//      Criteria criteria = session.createCriteria(Customer.class);
//      criteria.add(Restrictions.like("cust_name", "孙%"));
//      criteria.add(Restrictions.like("cust_name", "孙",MatchMode.START));
        // MatchMode.ANYWHERE 相当于 %孙%
        // MatchMode.EXACT 相当于 孙
        // MatchMode.END 相当于 %孙
        // MatchMode.START 相当于 孙%
//      List<Customer> list = criteria.list();

        //2. Hibernate5.2之后
        //2.1查询所有
//      CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        //1) 获取CriteriaQuery对象
//      CriteriaQuery<Customer> createQuery = criteriaBuilder.createQuery(Customer.class);
        //2) 指定根条件
//      createQuery.from(Customer.class);
        //3) 通过session执行查询
//      List<Customer> list = session.createQuery(createQuery).list();
    
        //2.2条件查询
        CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        CriteriaQuery<Customer> createQuery = criteriaBuilder.createQuery(Customer.class);
        //2. 指定根条件,获得实体的查询根对象
        Root<Customer> root = createQuery.from(Customer.class);
        //3. 创建两个查询条件,Root 定义查询的From子句中能出现的类型
        Predicate predicate = criteriaBuilder.like(root.get("cust_name"), "王%");
        //4. 将查询条件设置到where方法中
        createQuery.where(predicate);
        //5. 通过session执行查询
        List<Customer> list = session.createQuery(createQuery).getResultList();
        
        for (Customer customer : list) {
            System.out.println(customer);
        }

        txTransaction.commit();
    }

执行结果为:

16102290-1944c6b8fd2e7a58.png
image.png

详细参考文章: Hibernate5.2之后QBC查询——createCriteria()等方法过时的解决方法
官方Hibernate5.2.18 API说明文档: Hibernate ORM 5.2.18.Final User Guide
官方Hibernate5.2.18 API说明文档关于Criteria方面说明的中文转译: 【Java中级】8.5 SSH之Hibernate框架(五)——关于Criteria(QBC)过时的补充

  1. 分页查询
    同样分页查询:
    @Test
    // Criteria查询
    public void demo4() {
        // 分页查询
        Session session = HibernateUtils.getCurrentSession();
        Transaction txTransaction = session.beginTransaction();

        // 1. Hibernate5.2之前
        Criteria criteria = session.createCriteria(Customer.class);
        // 分页查询:从哪开始,每页显示几条
        criteria.setFirstResult(3);
        criteria.setMaxResults(3);

        List<Customer> list = criteria.list();
        for (Customer customer : list) {
            System.out.println(customer);
        }

        txTransaction.commit();
    }

执行结果:


16102290-0e1c1d1dedfb975e.png
image.png

Hibernate5.2之后的查询:

    @Test
    // Criteria查询
    public void demo4() {
        // 分页查询
        Session session = HibernateUtils.getCurrentSession();
        Transaction txTransaction = session.beginTransaction();

        // 1. Hibernate5.2之前
//      Criteria criteria = session.createCriteria(Customer.class);
        // 分页查询:从哪开始,每页显示几条
//      criteria.setFirstResult(3);
//      criteria.setMaxResults(3);
//      List<Customer> list = criteria.list();
        
        // 2. Hibernate5.2之后
        CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        CriteriaQuery<Customer> createQuery = criteriaBuilder.createQuery(Customer.class);
        // 4. 将查询条件设置到where方法中
        int pageIndex = 0;
        int pageSize = 3;
        List<Customer> list =  session.createQuery(createQuery).setFirstResult(pageIndex).setMaxResults(pageSize).list();
        for (Customer customer : list) {
            System.out.println(customer);
        }

        txTransaction.commit();
    }

执行结果为:


16102290-9803063f0035419e.png
image.png
7.3 SQLQuery

用于接收SQL,用于特别复杂的情况下使用SQL(比如八九个表关联)。实际开发上面两种都可以搞定。
在之前几篇Hibernate框架已经介绍,链接如下:
【Java中级】7.0 SSH之Hibernate框架(三)——Log4j的使用及Hibernate核心API
在这篇文章最后。关键代码如下:

 @Test
    // 查询所有
    public void demo6() {
        Session session = HibernateUtils.openSession();
        Transaction txTransaction = session.beginTransaction();

        // 接收HQL:Hibernate Query Language 面向对象的查询语言
        // 需要向createQuery方法传递一个HQL参数
//      Query query = session.createQuery("from Customer");
//      List<Customer> list = query.list();
//      for (Customer customer : list) {
//          System.out.println(customer);
//      }

        // 接收SQL
        NativeQuery nativeQuery= session.createSQLQuery("select * from cst_customer");
        List<Object[]> list2 = nativeQuery.list();
        for (Object[] objects : list2) {
            System.out.println(Arrays.toString(objects));
        }

        txTransaction.commit();
        session.close();
    }
8.0 本文测试完整代码
package com.edp.hibernate.demo1;

import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.hibernate.query.Query;
import org.junit.Test;

import com.edp.hibernate.utils.HibernateUtils;

public class HibernateDemo4 {
    @Test
    // Query查询
    public void demo1() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction txTransaction = session.beginTransaction();

        // 通过Session获得Query接口
        // 1.查询所有数据
//      String hqlString= "from Customer";//简单查询
//      Query query = session.createQuery(hqlString);

        // 2.查询所有姓王的
        // cust_name是类的属性名称,不是表中的字段名称
        // (1) hibernate4.1之前语句
//      String hqlString= "from Customer where cust_name like ?";//条件查询
//      Query query = session.createQuery(hqlString);
        // setParameter()方法就不需要管数据说明类型,Hibernate数据下标从0开始的。
//      query.setParameter(0, "王%");

        // (2) hibernate4.1之后语句(1)
//      String hqlString = "from Customer where cust_name like :mycust_name";
//      Query query = session.createQuery(hqlString);
//      query.setParameter("mycust_name", "王%");

        // (3) hibernate4.1之后语句(2)
        String hqlString = "from Customer where cust_name like ?0";
        Query query = session.createQuery(hqlString);
        // setParameter()方法就不需要管数据说明类型,Hibernate数据下标从0开始的。
        query.setParameter(0, "王%");

        List<Customer> list = query.list();
        for (Customer customer : list) {
            System.out.println(customer);
        }

        txTransaction.commit();
    }

    @Test
    // Query查询
    public void demo2() {
        // 分页查询
        Session session = HibernateUtils.getCurrentSession();
        Transaction txTransaction = session.beginTransaction();

        String hqlString = "from Customer";
        Query query = session.createQuery(hqlString);
        // 分页查询:从哪开始,每页显示几条
        query.setFirstResult(3);// 从哪开始查询
        query.setMaxResults(3);// 每页显示多少条记录

        List<Customer> list = query.list();
        for (Customer customer : list) {
            System.out.println(customer);
        }

        txTransaction.commit();
    }

    @Test
    @SuppressWarnings("ucd")
    // Criteria查询:更加面向对象的一种查询方式
    public void demo3() {
        Session session = HibernateUtils.getCurrentSession();
        Transaction txTransaction = session.beginTransaction();

        // 1. Hibernate5.2之前
        // 通过session获得Criteria的对象
        // 1.1 查询所有
//      Criteria criteria = session.createCriteria(Customer.class);
//      List<Customer> list = criteria.list();

        // 1.2 条件查询
//      Criteria criteria = session.createCriteria(Customer.class);
//      criteria.add(Restrictions.like("cust_name", "孙%"));
//      criteria.add(Restrictions.like("cust_name", "孙",MatchMode.START));
        // MatchMode.ANYWHERE 相当于 %孙%
        // MatchMode.EXACT 相当于 孙
        // MatchMode.END 相当于 %孙
        // MatchMode.START 相当于 孙%
//      List<Customer> list = criteria.list();

        // 2. Hibernate5.2之后
        // 2.1查询所有
//      CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        // 1) 获取CriteriaQuery对象
//      CriteriaQuery<Customer> createQuery = criteriaBuilder.createQuery(Customer.class);
        // 2) 指定根条件
//      createQuery.from(Customer.class);
        // 3) 通过session执行查询
//      List<Customer> list = session.createQuery(createQuery).list();

        // 2.2条件查询
        CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        CriteriaQuery<Customer> createQuery = criteriaBuilder.createQuery(Customer.class);
        // 2. 指定根条件,获得实体的查询根对象
        Root<Customer> root = createQuery.from(Customer.class);
        // 3. 创建两个查询条件,Root 定义查询的From子句中能出现的类型
        Predicate predicate = criteriaBuilder.like(root.get("cust_name"), "王%");
        // 4. 将查询条件设置到where方法中
        createQuery.where(predicate);
        // 5. 通过session执行查询
        List<Customer> list = session.createQuery(createQuery).getResultList();

        for (Customer customer : list) {
            System.out.println(customer);
        }

        txTransaction.commit();
    }

    @Test
    // Criteria查询
    public void demo4() {
        // 分页查询
        Session session = HibernateUtils.getCurrentSession();
        Transaction txTransaction = session.beginTransaction();

        // 1. Hibernate5.2之前
//      Criteria criteria = session.createCriteria(Customer.class);
        // 分页查询:从哪开始,每页显示几条
//      criteria.setFirstResult(3);
//      criteria.setMaxResults(3);
//      List<Customer> list = criteria.list();
        
        // 2. Hibernate5.2之后
        CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        CriteriaQuery<Customer> createQuery = criteriaBuilder.createQuery(Customer.class);
        // 4. 将查询条件设置到where方法中
        int pageIndex = 0;
        int pageSize = 3;
        List<Customer> list =  session.createQuery(createQuery).setFirstResult(pageIndex).setMaxResults(pageSize).list();
        for (Customer customer : list) {
            System.out.println(customer);
        }

        txTransaction.commit();
    }
}

END

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值