Hibernate入门第十三讲——Hibernate中的多表查询

在实际开发中,我们不可能只是简简单单地去查询单表,绝大部分都是要对多表进行联合查询的。所以本文就来讲述Hibernate中的多表查询,讲之前,大家可以先复习一下使用SQL语句是如何进行多表查询的。这里我给大家提个醒,本文所有案例代码的编写都是建立在前一讲案例基础之上的!!!

SQL多表查询

SQL的多表查询可分为连接查询和子查询,子查询其实就是SQL嵌套(在这里,它并不是咱的重点,所以我就不展开讲解了),这里我会重点讲解连接查询。

连接查询

连接查询又分为交叉连接(CROSS JOIN)、内连接(INNER JOIN ON)以及外连接。

交叉连接

交叉连接其实是没有实际意义的,它会产生迪卡尔积。例如:

SELECT * FROM cst_customer, cst_linkman;

内连接

使用内连接,它只能将有关联的数据得到,也就是说内连接查询到的是两张表的公共部分。内连接可分为两种,它们分别是隐式内连接和显示内连接。

隐式内连接

内连接有一种隐式内连接,它使用"逗号"将表分开,使用WHERE来消除迪卡尔积。例如:

SELECT * FROM cst_customer c,cst_linkman l WHERE c.cust_id=l.lkm_cust_id;

显示内连接

显示内连接如果写全的话,就像下面这样,但是我们应知道INNER是可以省略的。

SELECT * FROM cst_customer c INNER JOIN cst_linkman l ON c.cust_id=l.lkm_cust_id;

外连接

外连接也可分为两种,它们分别是左外连接(LEFT OUTER JOIN)和右外连接(RIGHT OUTER JOIN)。

左外连接

左外连接是以左表为基准关联数据,说的大白话一点就是它展示的数据只是在左表中有的,右表中没有的不管,也就是说它查询到的是左边表的全部数据以及两张表的公共部分。
左外连接如果写全的话,就像下面这样,但是我们应知道OUTER是可以省略的。

SELECT * FROM cst_customer c LEFT OUTER JOIN cst_linkman l ON c.cust_id=l.lkm_cust_id;

右外连接

右外连接是以右表为基准关联数据,说的大白话一点就是它展示的数据只是在右表中有的,左表中没有的不管,也就是说它查询到的是右边表的全部数据以及两张表的公共部分。
右外连接如果写全的话,就像下面这样,但是我们应知道OUTER是可以省略的。

SELECT * FROM cst_customer c RIGHT OUTER JOIN cst_linkman l ON c.cust_id=l.lkm_cust_id;

HQL多表查询

HQL中的多表查询可分为下面几类。
Hibernate入门第十三讲——Hibernate中的多表查询_第1张图片
温馨提示:在Hibernate框架中有迫切连接的这一概念,而在SQL中是没有的

内连接

显示内连接

显示内连接使用的是inner join with。如果是在MySQL中使用显示内连接,SQL语句是咋写的,你还记得吗?

SELECT * FROM cst_customer c INNER JOIN cst_linkman l ON c.cust_id = l.lkm_cust_id;

但在Hibernate框架中,我们则要这样书写HQL语句,是不是大不同啊!

from Customer c inner join c.linkMans

为了便于测试,我们可以在com.meimeixia.hibernate.demo01包下编写一个HQLJoinTest单元测试类,并在该类中编写这样的一个测试方法:

package com.meimeixia.hibernate.demo01;

import java.util.Arrays;
import java.util.List;

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

import com.meimeixia.hibernate.utils.HibernateUtils;

/**
 * HQL的多表查询
 */
public class HQLJoinTest {
	
	// 测试显示内连接
	@Test
	public void demo01() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tx = session.beginTransaction();
		
		//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
		List<Object[]> list = session.createQuery("from Customer c inner join c.linkMans").list();
		for (Object[] objects : list) {
			System.out.println(Arrays.toString(objects));
		}
	
		tx.commit();
	}
	
}

运行以上demo01方法,Eclipse控制台将打印如下内容:
Hibernate入门第十三讲——Hibernate中的多表查询_第2张图片
于是,我们可得出结论:此时,list()方法返回的结果是一个List集合,集合中存放的是Object[],而Object[]中装入的无非是Customer和LinkMan对象。
当然了,我们又可书写这样的HQL语句,使用with再添加一个条件。

from Customer c inner join c.linkMans with c.cust_id=1

为了便于进行测试,将HQLJoinTest单元测试类中的demo01方法改为:

// 测试显示内连接
@Test
public void demo01() {
	Session session = HibernateUtils.getCurrentSession();
	Transaction tx = session.beginTransaction();
	
	List<Object[]> list = session.createQuery("from Customer c inner join c.linkMans with c.cust_id=1").list();
	for (Object[] objects : list) {
		System.out.println(Arrays.toString(objects));
	}

	tx.commit();
}

运行以上demo01方法,Eclipse控制台将打印如下内容:
Hibernate入门第十三讲——Hibernate中的多表查询_第3张图片

隐式内连接

隐式内连接使用频率并不高,它就是一鸡肋。相信在实际开发中,你压根就用不到这种隐式内连接,你都会显示内连接了,还用得着这玩意。

迫切内连接

HQL迫切内连接很少自己手写,迫切内连接使用的是inner join fetch,其实就是在普通的内连接的inner join后面添加一个fetch关键字。迫切内连接是将得到的结果直接封装到PO类中,而内连接得到的是Object[]数组,数组中封装的是PO类对象。下面我们来验证这一点,在HQLJoinTest单元测试类中编写如下方法:

//测试迫切内连接
@Test
public void demo02() {
	Session session = HibernateUtils.getCurrentSession();
	Transaction tx = session.beginTransaction();
	
	//HQL迫切内连接(很少自己手写):其实就是在普通的内连接的inner join后面添加一个fetch关键字即可
	//from Customer c inner join fetch c.linkMans
	//fetch关键字的作用是通知Hibernate,将另一个对象的数据封装到该对象中。
	List<Customer> list = session.createQuery("from Customer c inner join fetch c.linkMans").list();
	for (Customer customer : list) {
		System.out.println(customer);
	}
	tx.commit();
}

温馨提示:fetch关键字的作用是通知Hibernate,将另一个对象的数据封装到该对象中。在这种情境下,是指将查询出来的LinkMan对象封装到了Customer对象的linkMans集合属性中,你要是不信,可以重写Customer类中的toString()方法,如下图所示。
Hibernate入门第十三讲——Hibernate中的多表查询_第4张图片
运行以上demo02方法,Eclipse控制台将打印如下内容:
Hibernate入门第十三讲——Hibernate中的多表查询_第5张图片
从这里我们也能看出,HQL迫切内连接底层也是执行的inner join,只不过数据结果封装到了对象中了。但问题又来了,我们查询的是两张表的信息,那就会得到合并后的结果,如果是查LinkMan则没问题,但是你要查Customer,Customer就会出现很多重复的数据,这个时候,我们就需要使用关键字——distinct去消除重复了,所以应将demo02方法改为:

//测试迫切内连接
@Test
public void demo02() {
	Session session = HibernateUtils.getCurrentSession();
	Transaction tx = session.beginTransaction();
	
	//HQL迫切内连接(很少自己手写):其实就是在普通的内连接的inner join后面添加一个fetch关键字即可
	//from Customer c inner join fetch c.linkMans
	//fetch关键字的作用是通知Hibernate,将另一个对象的数据封装到该对象中。
	//List list = session.createQuery("from Customer c inner join fetch c.linkMans").list();
	List<Customer> list = session.createQuery("select distinct c from Customer c inner join fetch c.linkMans").list();
	for (Customer customer : list) {
		System.out.println(customer);
	}
	tx.commit();
}

再次运行以上demo02方法,Eclipse控制台会打印如下内容:
Hibernate入门第十三讲——Hibernate中的多表查询_第6张图片
结论:使用迫切内连接,在结果有可能出现重复时,可以使用distinct关键字来去除重复

外连接

左外连接

左外连接使用的是left outer join。以码明示,在HQLJoinTest单元测试类中编写如下方法:

//测试左外连接
@Test
public void demo03() {
	Session session = HibernateUtils.getCurrentSession();
	Transaction tx = session.beginTransaction();
	
	List<Object[]> list = session.createQuery("from Customer c left outer join c.linkMans").list();
	for (Object[] objects : list) {
		System.out.println(Arrays.toString(objects));
	}
	tx.commit();
}

注意:此时,你得重写Customer类中的toString()方法,让其不要包含linkMans属性。接着,运行以上demo03方法,Eclipse控制台将打印如下内容。
Hibernate入门第十三讲——Hibernate中的多表查询_第7张图片

右外连接

右外连接使用的是right outer join。以码明示,在HQLJoinTest单元测试类中编写如下方法:

//测试右外连接
@Test
public void demo04() {
	Session session = HibernateUtils.getCurrentSession();
	Transaction tx = session.beginTransaction();
	
	List<Object[]> list = session.createQuery("from Customer c right outer join c.linkMans").list();
	for (Object[] objects : list) {
		System.out.println(Arrays.toString(objects));
	}
	tx.commit();
}

注意:此时,你得重写Customer类中的toString()方法,让其不要包含linkMans属性。接着,运行以上demo04方法,Eclipse控制台将打印如下内容。
Hibernate入门第十三讲——Hibernate中的多表查询_第8张图片

迫切左外连接

迫切左外连接使用的是left outer join fetch。以码明示,在HQLJoinTest单元测试类中编写如下方法:

//测试迫切左外连接
@Test
public void demo05() {
	Session session = HibernateUtils.getCurrentSession();
	Transaction tx = session.beginTransaction();
	
	// 注意:fetch不可以与单独条件的with一起使用
	String hql = "select distinct c from Customer c left outer join fetch c.linkMans with c.cust_id=1";
	List<Customer> list = session.createQuery(hql).list();
	for (Customer customer : list) {
		System.out.println(customer);
	}
	
	tx.commit();
}

与HQL迫切内连接一样,fetch关键字的作用是通知Hibernate,将另一个对象的数据封装到该对象中。在这种情境下,是指将查询出来的LinkMan对象封装到了Customer对象的linkMans集合属性中,你要是不信,可以重写Customer类中的toString()方法,如下图所示。
Hibernate入门第十三讲——Hibernate中的多表查询_第9张图片
接着你运行以上demo05方法,哎!妈啊!咋会报如下异常呢!
Hibernate入门第十三讲——Hibernate中的多表查询_第10张图片
异常发生的原因:fetch与单独条件的with一起使用了。如果非要让fetch与单独的一个条件使用,那么就必须使用where关键字了,所以我们可以将HQLJoinTest单元测试类中的demo05方法改为:

//测试迫切左外连接
@Test
public void demo05() {
	Session session = HibernateUtils.getCurrentSession();
	Transaction tx = session.beginTransaction();
	
	// 注意:fetch不可以与单独条件的with一起使用
	String hql = "select distinct c from Customer c left outer join fetch c.linkMans where c.cust_id=1";
	List<Customer> list = session.createQuery(hql).list();
	for (Customer customer : list) {
		System.out.println(customer);
	}
	
	tx.commit();
}

再次运行以上demo05方法,发现没有报错,并且Eclipse控制台会打印如下内容。
Hibernate入门第十三讲——Hibernate中的多表查询_第11张图片

你可能感兴趣的:(Hibernate框架,Hibernate框架学习)