第一节:Hibernate的检索方式
在实际开发项目中,对数据进行最多的操作就是查询,数据的查询在所有的ORM框架中都占有极其重要的地位。那么,如何利用Hibernate查询数据呢?接下来就来学习Hibernate的检索方式。
Hibernate的检索方式主要有5种,分别为导航对象图检索方式、OID检索方式、HQL检索方式、QBC检索方式和SQL检索方式。
- 1.1 对象图导航检索:
对象图导航检索方式是根据已加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。譬如要查找一个联系人对应的客户,就可以由联系人对象自动导航找到联系人所属的客户对象。当然,前提是必须在对象关系映射文件上,配置了多对一的关系。其检索方式如下:
LinkMan linkMan = (LinkMan)session.get(LinkMan.class,1L);
Customer customer = linkMan.getCustomer();
- 1.2 OID检索方式
OID检索方式主要是指用Session对象的get()和load()方法加载某条记录所对应的对象。如下面两种加载客户对象的方式,就是OID检索方式,具体如下:
Customer customer = (Customer) session.get(Customer.class,1L);
Customer customer = (Customer) session.load(Customer.class,1L);
1.3 HQL检索
HQL(Hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相似,但它使用的是类、对象和属性的概念,而没有表和字段的概念。在Hibernate提供的各种检索方式中,HQL是官方推荐的查询语言,也是使用最广泛的一种检索方式。1.4 QBC检索
QBC(Query By criteria)是Hibernate提供的另一种检索对象的方式,它主要由Criteria接口、Criterion接口和Expression类组成。Criteria接口是Hibernate API中的一个查询接口,它需要由session进行创建。Criterion是Criteria的查询条件,在Criteria中提供了add(Criterion criterion)方法来添加查询条件。
第二节:HQL检索详解
HQL(Hibernate Query Language)的具体功能如下:
- 在查询语句中设定各种查询条件
- 支持投影查询,即仅检索出对象的部分属性
- 支持分页查询
- 支持分组查询,允许使用group by 和 having关键字
- 提供内置聚会函数,如sum()、min()和max()等
- 能够调用用户定义的SQL函数。
- 支持子查询,即嵌套查询。
- 支持动态绑定参数。
Hibernate提供的Query接口是专门的HQL查询接口,它能执行各种复杂的HQL查询语句。完整的HQL语句结构如下:
select...from...where...group by...having...order by...asc/desc
可见HQL查询非常类似于标准SQL查询。通常情况下,当检索数据表中的所有记录时,查询语句中可以省略select关键字,示例如下:
String hql = "from Customer";
如果执行该查询语句,则会返回应用程序中的所有的Customer对象,需要注意的是Customer是类名,而不是表名,类名需要区分大小写,而关键字from不区分大小写。
2.1 基础查询
@Test
/**
* HQL的基本查询语句
*/
public void findAll(){
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
String HQL = "from Customer";
//设置别名查询
// String aliseHql = "from Customer c";
String aliseHql = "SELECT c from Customer c";
Query query = currentSission.createQuery(aliseHql);
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer.toString());
}
transaction.commit();
}
2.2 排序查询
@Test
/**
* 排序检索
*/
public void finaAllByOrder(){
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//实现数据的降序查询
String Hql = "from Customer ORDER BY cust_id desc";
Query query = currentSission.createQuery(Hql);
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer.toString());
}
transaction.commit();
}
2.3 条件查询
@Test
/**
* 依据条件来查询指定数据
*/
public void findByName(){
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//实现数据的降序查询(按名称绑定参数)
String Hql = "from Customer where cust_name = :name ";
Query query = currentSission.createQuery(Hql);
query.setParameter("name","王千万");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer.toString());
}
transaction.commit();
}
2.4 分页查询
@Test
/**
* 分页查询
*/
public void findByPage(){
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//实现数据的降序查询(按名称绑定参数)
String Hql = "from Customer";
Query query = currentSission.createQuery(Hql);
//设置分页查询的起始位置(index)
query.setFirstResult(0);
//设置每页显示的最大条目数量
query.setMaxResults(5);
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer.toString());
}
transaction.commit();
}
2.5 统计检索
@Test
/**
* 统计查询
*/
public void findCount(){
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//查询当前条目数
String Hql = "select count(*) from Customer";
Query query = currentSission.createQuery(Hql);
//获取结果
Long count = (Long)query.uniqueResult();
System.out.println(count);
transaction.commit();
}
2.6 投影检索
@Test
/**
* 投影检索
*/
public void findProjection(){
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//投影查询:查询一列
/* String Hql = "SELECT cust_name from Customer";
Query query = currentSission.createQuery(Hql);
List<String> list = query.list();
for (String name : list) {
System.out.println(name);
}*/
//投影查询:查询多列
// String Hql = "SELECT cust_name,cust_id from Customer";
// Query query = currentSission.createQuery(Hql);
// List<Object[]> list = query.list();
// for (Object[] o : list) {
// System.out.println(Arrays.toString(o));
// }
//投影查询:按构造方法查询
String Hql = "SELECT new Customer(cust_id,cust_name) from Customer";
Query query = currentSission.createQuery(Hql);
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer.toString());
}
transaction.commit();
}
第三节:QBC检索详解
3.1 基础查询方式
@Test
/**
* 基础查询
*/
public void findAll(){
//1.获取数据库连接对象以及开启事务
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//2.QBC查询全部
//2.1 创建CriteriaBuilder对象
CriteriaBuilder criteriaBuilder = currentSission.getCriteriaBuilder();
//2.2 获取CriteriaQuery对象
CriteriaQuery<Customer> criteriaQuery = criteriaBuilder.createQuery(Customer.class);
//2.3 指定根条件
criteriaQuery.from(Customer.class);
//2.4 执行查询
Query<Customer> query = currentSission.createQuery(criteriaQuery);
List<Customer> resultList = query.getResultList();
//2.5 循环遍历输出
for (Customer customer : resultList){
System.out.println(customer.toString());
}
//事务提交
transaction.commit();
}
3.2 模糊查询
@Test
/**
* 模糊查询
*/
public void findFuzzy(){
//1.获取数据库连接对象以及开启事务
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//2.QBC查询全部
//2.1 创建CriteriaBuilder对象
CriteriaBuilder criteriaBuilder = currentSission.getCriteriaBuilder();
//2.2 获取CriteriaQuery对象
CriteriaQuery<Customer> criteriaQuery = criteriaBuilder.createQuery(Customer.class);
//2.3 指定根条件
Root<Customer> customerRoot = criteriaQuery.from(Customer.class);
//2.3.1 设定模糊查询的条件
criteriaQuery.where(criteriaBuilder.like(customerRoot.get("cust_name"),"%万%"));
//2.4 执行查询
Query<Customer> query = currentSission.createQuery(criteriaQuery);
List<Customer> resultList = query.getResultList();
//2.5 循环遍历输出
for (Customer customer : resultList){
System.out.println(customer.toString());
}
//事务提交
transaction.commit();
}
3.3 分页检索
@Test
/**
* 分页检索
*/
public void findBypage(){
//1.获取数据库连接对象以及开启事务
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//2.QBC查询全部
//2.1 创建CriteriaBuilder对象
CriteriaBuilder criteriaBuilder = currentSission.getCriteriaBuilder();
//2.2 获取CriteriaQuery对象
CriteriaQuery<Customer> criteriaQuery = criteriaBuilder.createQuery(Customer.class);
//2.3 指定根条件
Root<Customer> customerRoot = criteriaQuery.from(Customer.class);
//2.4 执行查询
Query<Customer> query = currentSission.createQuery(criteriaQuery);
//2.4.1 指定分页查询的index
query.setFirstResult(0);
//2.4.2 指定分页查询每页显示的最大条目数
query.setMaxResults(5);
List<Customer> resultList = query.getResultList();
//2.5 循环遍历输出
for (Customer customer : resultList){
System.out.println(customer.toString());
}
//事务提交
transaction.commit();
}
第四节:离线条件检索
DetachedCriteria翻译为离线条件查询,因为它是可以脱离Session来使用的一种条件查询对象,我们都知道Criteria对象必须由Session对象来创建。那么也就是说必须先有Session才可以生成Criteria对象。而DetachedCriteria对象可以在其他层对条件进行封装。
这个对象在SSH整合以后是经常会使用。它的主要优点是做一些特别复杂的条件查询的时候,往往会在WEB层向业务层传递很多参数,业务层又会将这些参数传递给DAO层。最会在DAO中拼接SQL完成查询。有了离线条件查询对象后,那么这些工作都可以不用关心了,我们可以在WEB层将数据封装好,传递到业务层,再由业务层传递给DAO完成查询。
@Test
/**
* 离线查询
*/
public void detachedCriteria(){
//离线查询条件的封装
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
detachedCriteria.add(Restrictions.eq("cust_name","王者1"));
//1.获取数据库连接对象以及开启事务
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
Criteria executableCriteria = detachedCriteria.getExecutableCriteria(currentSission);
List<Customer> list = executableCriteria.list();
for (Customer customer : list){
System.out.println(customer.toString());
}
//事务提交
transaction.commit();
}
第五节:SQL中的多表联合查询
4.1 连接查询
- 交叉连接
交叉连接返回的结果是被连接的两个表中所有数据行的笛卡尔积,也就是返回第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数,例如department表中有4个部门,employee表中有4个员工,那么交叉连接的结果就有4*4=16条数据。
交叉连接的语法格式如下:
SELECT * FROM 表1 CROSS JOIN 表2;
//也可以写为如下格式:
SELECT * FROM 表1,表2;
从上述描述情况可以看出,交叉连接的结果就是表中所有数据的组合。需要注意的是,在实际开发中这种业务需要时很少见的,一般不会使用交叉连接,而是使用具体的条件对数据进行有目的的查询。
- 内连接
内连接(INNER JOIN)又称简单连接或自然连接,是一种常见的连接查询。内连接使用比较运算符对两个表中的数据进行比较,并列出与连接条件匹配的数据行,组合成新的记录,也就是说在内连接查询中,只有满足条件的记录才能出现在查询结果中。内连接查询的语法格式如下:
SELECT 查询字段 FROM 表1 [INNER] JOIN 表2 ON 表1.关系字段 = 表2.关系字段
在上述的语法格式中,INNER JOIN用于连接两个表,ON来指定连接条件,其中INNER可以省略。内连接其实还可以细分为如下两类:
- 隐式内连接:顾名思义隐式的就是我们看不到INNER join 的关键字。而使用WHERE关键字替代:
SELECT * FROM 表1,表2 where 表1.关系字段 = 表2.关系字段;
- 显示内连接:显示的就是在语句中明显的调用了 inner join的关键字
SELECT * FROM 表1 inner join 表2 on 表1.关系字段 = 表2.关系字段;
SELECT * FROM 表1 join 表2 on 表1.关系字段 = 表2.关系字段;
- 外连接:
前面讲解的内连接查询中,返回的结果只包含符合查询条件和连接条件的数据,然而有时还需要包含没有关联的数据,即返回查询结果中不仅包含符合条件的数据,而且还包括左表(左连接或左外连接)、右表(右连接或右外连接)或两个表(全外连接)中的所有数据,此时就需要使用外连接查询,外连接查询分为左连接和右连接。
外连接的语法格式如下:
SELECT 所查字段 FROM 表1 LEFT|RIGHT [OUTER] JOIN 表2 ON 表1.关系字段 = 表2.关系字段 WHERE 条件
外连接的语法格式和内连接类似,只不过使用的是LEFT JOIN 、RIGHT JOIN关键字,其中关键字左边称为左表,关键字右边的表被称作右表。
在使用左连接和右连接查询时,查询结果是不一致的,具体如下:
//LEFT JOIN(左连接):返回包括左表中的所有记录和右表中符合连接条件的记录
SELECT * FROM 表1 LEFT OUTER JOIN 表2 on 表1.关系字段 = 表2.关系字段;
SELECT * FROM 表1 LEFT JOIN 表2 ON 表1.关系字段 = 表2.关系字段;
//RIGHT JOIN(右连接):返回包括右表中的所有记录和左表中符合连接条件的记录
SELECT * FROM 表1 RIGHT OUTER JOIN 表2 on 表1.关系字段 = 表2.关系字段;
SELECT * FROM 表1 RIGHT JOIN 表2 ON 表1.关系字段 = 表2.关系字段;
4.2 HQL的多表联合查询
Hibernate进行多表查询与SQL其实是很相似的,但是HQL会在原来SQL分类的基础上又多出来一些操作。
HQL的多表连接查询的分类如下:
/******************************************************
交叉连接
* 内连接
显示内连接
隐式内连接
迫切内连接
* 外连接
左外连接
迫切左外连接
右外连接
************************************************************/
其实这些连接查询语法大致都是一致的,就是HQL查询的是对象而SQL查询的是表。那么我们来比较一下SQL和HQL的连接查询:
//SQL连接查询
SELECt * FROM cst_customer c INNER JOIN cst_linkman l ON c.cust_id = l.lkm_cust_id;
//HQL连接的查询
from Customer c inner join c.linkMans
在HQL中,我们不用写关联字段了,因为客户中的联系人的集合其实对应的就是外键,所以我们在 inner join的后面直接可以写入c.linkMans。
//hibernate中生成的SQL语句
Hibernate:
select
customer0_.cust_id as cust_id1_0_0_,
linkmans1_.lkm_id as lkm_id1_1_1_,
customer0_.cust_name as cust_nam2_0_0_,
customer0_.cust_source as cust_sou3_0_0_,
customer0_.cust_industry as cust_ind4_0_0_,
customer0_.cust_level as cust_lev5_0_0_,
customer0_.cust_phone as cust_pho6_0_0_,
customer0_.cust_mobile as cust_mob7_0_0_,
linkmans1_.lkm_name as lkm_name2_1_1_,
linkmans1_.lkm_gender as lkm_gend3_1_1_,
linkmans1_.lkm_phone as lkm_phon4_1_1_,
linkmans1_.lkm_mobile as lkm_mobi5_1_1_,
linkmans1_.lkm_email as lkm_emai6_1_1_,
linkmans1_.lkm_qq as lkm_qq7_1_1_,
linkmans1_.lkm_position as lkm_posi8_1_1_,
linkmans1_.lkm_memo as lkm_memo9_1_1_,
linkmans1_.lkm_cust_id as lkm_cus10_1_1_
from
cst_customer customer0_
inner join
cst_linkman linkmans1_
on customer0_.cust_id=linkmans1_.lkm_cust_id
迫切内连接和内连接写法和使用上有什么区别呢?
迫切内连接其实就是在内连接的inner join 后添加一个fetch关键字。注意:fetch本身不是SQL语句的关键字。只能再HQL中使用,生成SQL语句后,fetch就消失了。那么fetch到底有什么作用呢?
其实HQL内连接查询的和SQL的内连接查询到的结果集是一样的,都是两个表的交集部分的数据。然后在封装数据的时候,普通内连接会将属于客户的数据封装到Customer对象中,会将属于联系人的数据封装到LinkMan对象中,所以每条记录都会是装有两个对象的集合,所以封装以后的数据是List<Object[]>,在Object[]中有连个对象一个是Customer 另一个是LinkMan。所以使用普通内连接的时候可以如下编码:
@Test
/**
* HQL实现多表联查普通内连接
*/
public void hqlMoreTable(){
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//投影查询:按构造方法查询
String Hql = "from Customer c INNER join c.linkMans";
Query query = currentSission.createQuery(Hql);
List<Object[]> list = query.list();
for (Object[] object : list) {
System.out.println(Arrays.toString(object));
}
transaction.commit();
}
那么加了fetch以后,虽然我们查询到的数据是一样的,但是Hibernate发现HQL中有fetch就会将封装到一个对象中,把属于客户的数据封装到Customer对象中,将属于联系人的部分封装到Customer中的联系人的集合中,这样最后封装完成以后是一个List<Customer>对象。所以使用迫切内连接的时候可以如下编写代码:
@Test
/**
* HQL实现多表联查迫切内连接
*/
public void hqlMoreTableJoin(){
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//投影查询:按构造方法查询
String Hql = "from Customer c INNER join fetch c.linkMans";
Query query = currentSission.createQuery(Hql);
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer.toString());
}
transaction.commit();
}
其实内连接和迫切内连接的主要区别就在封装数据,因为他们查询的结果集都是一样的,生成底层的SQL语句也是一样的。
- 内连接:发送就是内连接的语句,封装的时候将属于各自对象的数据封装到各自的对象中,最后得到一个List<Object[]>。
- 迫切内连接:发送的是内连接的语句,需要在编写HQL的时候在join后添加一个fetch关键字,Hibernate会发送HQL中的fetch关键字,从而将每条数据封装到对象中,最后得到一个List<Customer>。
但是迫切内连接封装以后会出现重复的数据,解决该问题需要我们自己在手动编写迫切内连接的时候使用distinct关键字,示例如下:
@Test
/**
* HQL实现多表联查迫切内连接
*/
public void hqlMoreTableDemo(){
Session currentSission = HibernateUtil.getCurrentSission();
Transaction transaction = currentSission.beginTransaction();
//投影查询:按构造方法查询
String Hql = "select distinct c from Customer c INNER join fetch c.linkMans";
Query query = currentSission.createQuery(Hql);
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer.toString());
}
transaction.commit();
}