1.0 什么是持久化类
- 持久化:将内存的一个对象持久化到数据库中的过程。Hibernate框架就是用来进行持久化的框架。
- 持久化类:将内存中的数据永久存储到关系型数据库中。
- 其实,所谓的持久化类指的是一个Java类与数据库表建立了映射关系,那么这个类称为持久化类。可以理解为,持久化类就是一个Java类有了一个映射文件与数据库的表建立了关系。
- 持久化类 = Java类 + 映射文件
2.0 持久化类的编写规则
- 对持久化类提供一个无参的构造方法: Hibernate底层需要使用反射生成实例。
- 属性需要私有,对私有属性提供public的get和set方法: Hibernate中,获取设置对象的值。
- 对持久化类提供一个唯一标识OID与数据库主键对应: 在Java中,通过对象的地址区分是否是同一对象。数据库中通过主键确定是否是同一条记录。在hibernate中,通过持久化的OID的属性区分是否是同一对象。
- 持久化类中,属性尽量使用包装类类型: 今晚基本的数据类型默认值是0,0会有很多的歧义。包装类的类型默认值是null,如果数据库中的数据,没有插入数据,则会显示默认值null,如果输入数据值就是0,在我们查阅数据库时,如果使用基本类型,就无法判断,是否是本身值为0,还是没有插入数据。
- 持久化类不要使用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中,为了减少程序的编写,提供了很多主键的生成策略。
- increment : Hibernate中提供的自动增长机制,适用于short、int、long类型主键。
- 只有在没有其他进程网同一张表中插入数据时才能用。在集群下不要使用。(换句话说:在单线程程序中使用,不要在多线程程序中使用。)
- 原理:Hibernate首先发送一条语句,set max(id) from表;然后让id+1作为下一条记录的主键。这样就存在线程问题。
- identity:适用short、int、long类型的主键,使用的是数据库底层的自动增长机制,适用于有自动增强机制的数据库(MySQL、MSSQL),但是Oracle没有自动增长。
- sequence:适用short、int、long类型的主键,采用的是序列的方式(Oracle、DB2、PostgreSQL、SAP、DB、Mckoi支持序列)。MySQL就不能使用sequence。
- uuid:适用于字符串类型主键。使用hibernate中的随机方式生成字符串主键。
- native:本地策略。可以在identity和sequence之间进行自动切换。意思就是如果底层采用的是MySQL,配置native相当于identity;如果底层使用的是Oracle,配置native相当于sequence。也就是说设置成native程序员就不用去管使用的MySQL还是Oracle。
- assigned:Hibernate放弃外键的管理,需要通过手动编写程序或者用户自己设置。
- 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](https://i-blog.csdnimg.cn/blog_migrate/3452af91905c27b21c78237aee3cbf17.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](https://i-blog.csdnimg.cn/blog_migrate/837b36c250c30d0bf917a682a0dc110c.png)
执行后的数据库:
![16102290-a4ff25beaa3c9b11.png](https://i-blog.csdnimg.cn/blog_migrate/718573cbe74fe18db9e2f9c685872d32.png)
持久化类持久态对象之所以能自动更新数据库,在于 底层原理依赖于Hibernate的一级缓存。
4.0 Hibernate的一级缓存
4.1 基本概念
- 缓存:是一种优化的方式,将数据存入到内存中,使用的时候直接从缓存(内存)中获取,不用通过存储源。
- Hibernate主要提供两个级别的缓存。
- Hibernate框架中提供了两种优化手段:缓存、抓取策略。
- Hibernate的一级缓存: 称为Session级别的缓存,一级缓存生命周期与Session一致(一级缓存是由Session中的一系列Java集成构成),一级缓存是自带、不可卸载的。
- Hibernate的二级缓存:是SessionFactory级别的,需要配置的缓存。目前在企业开发中基本不用,有其他替代方案。
4.2 对一级缓存的说明
只要用Hibernate,就会使用到一级缓存,Hibernate的一级缓存的作用就是减少对数据库的访问次数。
4.3 Hibernate一级缓存的特点
- 当应用程序调用Session接口的save()、update()、saveOrUpdate()时,如果Session缓存中没有相应的对象。Hibernate就会自动把从数据库中查询到的相应对象信息加入到一级缓存中去。
- 当调用Session接口的load()、get()方法,以及Query接口的list()、iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询的对象,再去数据库中查询对应对象,并添加到一级缓存中。
- 当调用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](https://i-blog.csdnimg.cn/blog_migrate/77aba2144b5bf4223165bdd7d4b0237e.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](https://i-blog.csdnimg.cn/blog_migrate/91499705f638697a52ed2d24ae814ba8.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](https://i-blog.csdnimg.cn/blog_migrate/5bb372d4faa717009b003f0544387f23.png)
![16102290-959e1275ed93b1ca.png](https://i-blog.csdnimg.cn/blog_migrate/432eab2f736215165583dbb24906d04b.png)
![16102290-532eb6533d642225.png](https://i-blog.csdnimg.cn/blog_migrate/ba0c9fab84b6d908b69541c74d187c10.png)
可以看到,actionQueue(活动队列)中,各项参数都为0(size=0),当手动进行插入、更新、删除时,这其中才会有数据。
![16102290-a586fce1612cc4d0.png](https://i-blog.csdnimg.cn/blog_migrate/321054f4fd26aaabcff6b7167afa8ea3.png)
persistenceContext(持久化的上下文)。当调试执行到如下语句时:
// 发送SQL语句,同时放入到一级缓存中
Customer customer = session.get(Customer.class, 2l);//向数据库查询, 即发送SQL语句,同时放入到一级缓存当中。
向数据库查询, 即发送SQL语句,同时放入到一级缓存当中。
调试到下一句:
![16102290-ef32b56d43c404f9.png](https://i-blog.csdnimg.cn/blog_migrate/ef05b94ad712a988398ba859e748745e.png)
在 persistenceContext中可以看到,一级缓存中已经有数据了。Hibernate会对缓存 “拍个照片”,存起来。
通知会存储快照区一份。
![16102290-fbe51ba723327540.png](https://i-blog.csdnimg.cn/blog_migrate/cbe69fab446b471f3eeb622a795c9262.png)
![16102290-e3b61c58a54f6e72.png](https://i-blog.csdnimg.cn/blog_migrate/4860b08c62dfe792d6ba82ce5dac64b4.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](https://i-blog.csdnimg.cn/blog_migrate/fa1cd0288a2fcdab285079b60a195d3c.png)
![16102290-05592db7fe362290.png](https://i-blog.csdnimg.cn/blog_migrate/1f0efccf4141d781f4ae5bf1fe537830.png)
![16102290-e3c14456551de8b9.png](https://i-blog.csdnimg.cn/blog_migrate/f16954e5b77a3a0bb20c4f82e1874ada.png)
5.4 设置隔离级别
![16102290-b61de020749b5fb3.png](https://i-blog.csdnimg.cn/blog_migrate/d683e887e76205718b7f4740aefc6002.png)
![16102290-0af0536875acdf66.png](https://i-blog.csdnimg.cn/blog_migrate/f7ee274bccb23fe4dacc420dc991e73e.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](https://i-blog.csdnimg.cn/blog_migrate/504d3fc99c851f58f9ede47cfd49e972.png)
- 在DAO层,数据操作是一个一个执行。
- 但是一个事物涉及,多个数据操作,同时必须保证,多个操作都是由同一个JDBC中的connection对象(Hibernate中是Session对象)完成。
-
这时候就需要进行事务管理。
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](https://i-blog.csdnimg.cn/blog_migrate/dd3fc42c7d6a7ce973778f344ffc923f.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](https://i-blog.csdnimg.cn/blog_migrate/bf362ee41719b3f6ab4b49785b7b8eec.png)
- 查询所有:
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](https://i-blog.csdnimg.cn/blog_migrate/89faf5e4eb81a7f87424e4030e50dedb.png)
- 查所有姓王的:
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](https://i-blog.csdnimg.cn/blog_migrate/831d9ec14bf1e2372fdc6d331234c7a3.png)
- 分页查询
@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](https://i-blog.csdnimg.cn/blog_migrate/ee9e9627a7d34db57da8b0331c1c6434.png)
执行结果为:
![16102290-df5f4e9305b118b4.png](https://i-blog.csdnimg.cn/blog_migrate/4cba551a1b019d74dfba2f61b01dc2f2.png)
可见,查询语句变成了含limit的语句。如果想查询第二页,只需要修改代码:
//分页查询:从哪开始,每页显示几条
query.setFirstResult(3);//从哪开始查询
query.setMaxResults(3);//每页显示多少条记录
执行结果为:
![16102290-193a7f1c1ab5238a.png](https://i-blog.csdnimg.cn/blog_migrate/804ee9e04637ea10d2ca53a88f72c93e.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.EntityManagerFactory或javax.persistence.EntityManager的getCriteriaBuilder()方法,可以获得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](https://i-blog.csdnimg.cn/blog_migrate/0b6202981617b676cc6db7069f1b9700.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)过时的补充
- 分页查询
同样分页查询:
@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](https://i-blog.csdnimg.cn/blog_migrate/f1ad1a0c0d108c4ad200206ffebce977.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](https://i-blog.csdnimg.cn/blog_migrate/ee036947acb07dfa5924d9757eece586.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