http://blog.csdn.net/yerenyuan_pku/article/details/70556208

Hibernate中的多表操作

在实际开发中,我们不可能只是简简单单地去操作单表,绝大部分都是要对多表进行操作的。所以本文就来讲述Hibernate中的多表操作,讲之前,先复习一下使用SQL语句是如何进行多表操作的。提示:本文所有案例代码的编写都是建立在前文Hibernate检索方式概述的案例基础之上的!!!

SQL多表操作

SQL多表操作可分为如下几类:

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

    SELECT * FROM t_customer CROSS JOIN t_order;
  • 内连接(INNER JOIN ON) 
    使用内连接,它只能将有关联的数据得到。例如:

    SELECT * FROM t_customer AS c INNER JOIN t_order AS o ON c.id=o.c_customer_id; 
  • 内连接还有一种隐式内连接,它使用”逗号”将表分开,使用WHERE来消除迪卡尔积。例如:

    SELECT * FROM t_customer AS c, t_order o WHERE c.id=o.c_customer_id;
  • 外连接 
    外连接又分为:

    • 左外连接(LEFT OUTER JOIN):它是以左表为基准关联数据,说的大白话一点就是它展示的数据只是在左表中有的,右表中没有的不管。例如:

      SELECT * FROM t_customer c LEFT OUTER JOIN t_order o ON c.id=o.c_customer_id;
    • 右外连接(RIGHT OUTER JOIN):它是以右表为基准关联数据,说的大白话一点就是它展示的数据只是在右表中有的,左表中没有的不管。例如:

      SELECT * FROM t_customer c RIGHT OUTER JOIN t_order o ON c.id=o.c_customer_id;

    提示:OUTER可以省略。

HQL多表操作

HQL多表操作可分为下面几类:

  1. 交叉连接
  2. 内连接 
    • 显示内连接
    • 隐式内连接
    • 迫切内连接
  3. 外连接 
    • 左外连接
    • 迫切左外连接
    • 右外连接

注意:在Hibernate框架中有迫切连接的这一概念,而在SQL中是没有的

内连接

显示内连接

显示内连接使用的是inner join with。如果是在MySQL中使用显示内连接,那么我们就要这样书写SQL语句:

select * from t_customer(表名)inner join t_order(表名) on 条件

但在Hibernate框架中,我们则要书写这样的hql语句:

from Order o inner join o.c

为了便于测试,在cn.itheima.test包下编写一个HQLJoinTest单元测试类,并在该类中编写这样的一个测试方法:

public class HQLJoinTest {

    // 测试显示内连接
@Test
public void test1() { Session session = HibernateUtils.openSession();
session.beginTransaction(); String hql = "from Order o inner join o.c"; Query query = session.createQuery(hql);
List<Object[]> list = query.list(); // 结果是一个List<Object[]>集合,而Object[]中装入的是Customer和Order对象。 for (Object[] objs : list) {
for (Object obj : objs) {
System.out.print(obj + "\t");
}
System.out.println();
} session.getTransaction().commit();
session.close();
} }

运行以上test1方法,Eclipse控制台打印如下: 

可得出结论:query.list()返回的结果是一个List集合,集合存放的是Object[],而Object[]中装入的无非是Customer和Order对象。 
在Hibernate框架中,我们也可书写这样的hql语句:

from Customer c inner join c.orders
  • 1
  • 1

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

public class HQLJoinTest {

    // 测试显示内连接
@Test
public void test1() { Session session = HibernateUtils.openSession();
session.beginTransaction(); String hql = "from Customer c inner join c.orders"; Query query = session.createQuery(hql);
List<Object[]> list = query.list(); // 结果是一个List<Object[]>集合,而Object[]中装入的是Customer和Order对象。 for (Object[] objs : list) {
for (Object obj : objs) {
System.out.print(obj + "\t");
}
System.out.println();
} session.getTransaction().commit();
session.close();
} }

运行以上test1方法,Eclipse控制台打印如下: 

当然了,我们又可书写这样的hql语句:

from Customer c inner join c.orders with c.id=1
  • 1
  • 1

即使用with再添加一个条件。所以为了便于进行测试,将HQLJoinTest单元测试类中的test1方法改为:

public class HQLJoinTest {

    // 测试显示内连接
@Test
public void test1() { Session session = HibernateUtils.openSession();
session.beginTransaction(); String hql = "from Customer c inner join c.orders with c.id=1"; Query query = session.createQuery(hql);
List<Object[]> list = query.list(); // 结果是一个List<Object[]>集合,而Object[]中装入的是Customer和Order对象。 for (Object[] objs : list) {
for (Object obj : objs) {
System.out.print(obj + "\t");
}
System.out.println();
} session.getTransaction().commit();
session.close();
} }

运行以上test1方法,Eclipse控制台打印如下: 

隐式内连接

隐式内连接也与我们在SQL中的操作不一样,它是通过.运算符来关联的。值得一提的是,隐式内连接使用频率并不高,大家知道就OK了。如果是在MySQL中使用隐式内连接,那么我们就要这样书写SQL语句:

select * from t_customer,t_order where 条件
  • 1
  • 1

但在Hibernate框架中,我们则要书写这样的hql语句:

from Order o where o.c.id=1

为了便于进行测试,在HQLJoinTest单元测试类中编写如下方法:

public class HQLJoinTest {

    // 测试隐式内连接(使用频率不高。大家知道就OK了)
@Test
public void test2() {
Session session = HibernateUtils.openSession();
session.beginTransaction(); String hql = "from Order o where o.c.id=1";
Query query = session.createQuery(hql);
List list = query.list();
System.out.println(list); session.getTransaction().commit();
session.close();
} }

运行以上test2方法,Eclipse控制台打印如下: 

迫切内连接

迫切内连接使用的是inner join fetch。迫切内连接得到的结果是直接封装到PO类中,而内连接得到的是Object[]数组,数组中封装的是PO类对象。下面我们来验证这一点,在HQLJoinTest单元测试类中编写如下方法:

public class HQLJoinTest {

    // 迫切内连接
@Test
public void test3() {
Session session = HibernateUtils.openSession();
session.beginTransaction(); String hql = "from Order o inner join fetch o.c"; Query query = session.createQuery(hql);
List list = query.list(); // 结果是一个List<>集合,集合中装入的是from后面的对象。
System.out.println(list); session.getTransaction().commit();
session.close();
} }

运行以上test3方法,Eclipse控制台打印如下: 

可得出结论:query.list()返回的结果是一个List集合,集合中装入的是from后面的对象。如还有疑问,可将以上test3方法改为:

public class HQLJoinTest {

    // 迫切内连接
@Test
public void test3() {
Session session = HibernateUtils.openSession();
session.beginTransaction(); String hql = "from Customer c inner join fetch c.orders"; Query query = session.createQuery(hql);
List list = query.list(); // 结果是一个List<>集合,集合中装入的是from后面的对象。
System.out.println(list); session.getTransaction().commit();
session.close();
} }

运行以上test3方法,Eclipse控制台打印如下: 

从这里我们也能看出,迫切内连接底层也是执行的inner join,只不过数据结果封装到了对象中了。但问题又来了,我们查询的是两张表的信息,那就会得到合并后的结果,如果是查Order则没问题,但是你要查Customer,Customer就会出现很多重复的数据,这个时候,我们就需要使用关键字——distinct去消除重复了。故应将test3方法改为:

public class HQLJoinTest {

    // 迫切内连接
@Test
public void test3() {
Session session = HibernateUtils.openSession();
session.beginTransaction(); String hql = "select distinct c from Customer c inner join fetch c.orders"; Query query = session.createQuery(hql);
List<Customer> list = query.list();
for (Customer order : list) {
System.out.println(order);
} session.getTransaction().commit();
session.close();
} }

再次运行以上test3方法,Eclipse控制台打印如下: 
 
结论:使用迫切连接,结果可能出现重复,所以要使用distinct来去除重复。

外连接

左外连接

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

public class HQLJoinTest {

    // 演示外连接
@Test
public void test4() {
Session session = HibernateUtils.openSession();
session.beginTransaction(); List<Object[]> list = session.createQuery("from Customer c left outer join c.orders").list(); // 左外连接 for (Object[] objs : list) {
for (Object obj : objs) {
System.out.print(obj + "\t");
}
System.out.println();
} session.getTransaction().commit();
session.close();
} }

运行以上test4方法,Eclipse控制台打印如下: 

右外连接

右外连接使用的是right outer join。以码明示,将HQLJoinTest单元测试类中的test4方法改为:

public class HQLJoinTest {

    // 演示外连接
@Test
public void test4() {
Session session = HibernateUtils.openSession();
session.beginTransaction(); List<Object[]> list = session.createQuery("from Customer c right outer join c.orders").list(); // 右外连接 for (Object[] objs : list) {
for (Object obj : objs) {
System.out.print(obj + "\t");
}
System.out.println();
} session.getTransaction().commit();
session.close();
} }

运行以上test4方法,Eclipse控制台打印如下: 

迫切左外连接

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

public class HQLJoinTest {

    // 演示迫切左外连接
@Test
public void test5() {
Session session = HibernateUtils.openSession();
session.beginTransaction(); // 注意:fetch不可以与单独条件的with一起使用
List<Customer> list = session.createQuery("select distinct c from Customer c left outer join fetch c.orders with c.id=1").list(); for (Customer customer : list) {
System.out.println(customer);
} session.getTransaction().commit();
session.close();
} }

运行以上test5方法,会发现报如下异常: 

异常发生原因:fetch不可以与单独条件的with一起使用。如果非要让fetch与单独的一个条件使用,则必须使用where关键字。故将HQLJoinTest单元测试类中的test5方法改为:

public class HQLJoinTest {

    // 演示迫切左外连接
@Test
public void test5() {
Session session = HibernateUtils.openSession();
session.beginTransaction(); // 注意:fetch不可以与单独条件的with一起使用
List<Customer> list = session.createQuery("select distinct c from Customer c left outer join fetch c.orders where c.id=1").list(); for (Customer customer : list) {
System.out.println(customer);
} session.getTransaction().commit();
session.close();
} }

再次运行以上test5方法,Eclipse控制台打印如下: 

Hibernate中的Session管理

Hibernate提供了三种管理Session的方式:

  1. Session对象的生命周期与本地线程绑定(ThreadLocal)
  2. Session对象的生命周期与JTA事务绑定(也即分布式事务管理,应用在分布式数据库里面,在此并不打算讲解)
  3. Hibernate委托程序来管理Session的生命周期

我们之前一直所使用的是第三种方式,即通过程序获取一个Session对象,然后使用它,最后关闭它(即session.close();)。 
在实际开发中我们一般使用的是前两种,但由于第二种方式是基于分布式数据库而言的,所以在此主要介绍关于本地线程绑定Session这种方式,这种方式的使用步骤为:

  1. 需要在hibernate.cfg.xml核心配置文件添加如下配置:

    <property name="hibernate.current_session_context_class">thread</property>
  2. 在获取Session时不要再使用openSession()方法而是使用getCurrentSession()方法。故应在HibernateUtils工具类中添加如下方法:

    public static Session getCurrentSession() {
    return sessionFactory.getCurrentSession();
    }

    如此一来,HibernateUtils工具类的代码就变为:

    public class HibernateUtils {
    
        private static Configuration config;
    private static SessionFactory sessionFactory; static {
    config = new Configuration().configure();
    sessionFactory = config.buildSessionFactory();
    } public static Session openSession() {
    return sessionFactory.openSession();
    } public static Session getCurrentSession() {
    return sessionFactory.getCurrentSession();
    }
    }

本地线程绑定Session这种方式的大概内部原理可用下图来表示: 

接下来,我们就要在cn.itheima.test包下编写一个SessionManageTest单元测试类测试Session绑定到线程中,即在SessionManageTest单元测试类中编写如下方法:

public class SessionManageTest {

    // 测试Session绑定到线程中
@Test
public void test1() {
// 这时每一次获取都是一个新的Session
Session s1 = HibernateUtils.openSession();
Session s2 = HibernateUtils.openSession(); System.out.println(s1 == s2); // false Session s3 = HibernateUtils.getCurrentSession();
Session s4 = HibernateUtils.getCurrentSession(); System.out.println(s3 == s4); // true
} }

运行以上方法,Eclipse控制台打印:

false 
true

关于getCurrentSession方法使用时的注意事项

例如,我们要简单查询id为1的客户,我们相当然地会在SessionManageTest单元测试类中编写如下方法:

public class SessionManageTest {

    @Test
public void test2() {
Session session = HibernateUtils.getCurrentSession();
session.beginTransaction(); Customer customer = session.get(Customer.class, 1);
System.out.println(customer); session.getTransaction().commit();
session.close();
} }

上述代码执行后,会产生如下问题: 

产生该问题的原因:使用getCurrentSession方法获取的与线程绑定的session对象,在事务关闭(提交)时,session对象也会close掉,简单说,就不需要我们再手动close掉session对象了。故应将test2方法改为:

public class SessionManageTest {

    @Test
public void test2() {
Session session = HibernateUtils.getCurrentSession();
session.beginTransaction(); Customer customer = session.get(Customer.class, 1);
System.out.println(customer); session.getTransaction().commit();
} }

(转)Hibernate中的多表操作的更多相关文章

  1. (十五)Hibernate中的多表操作(5):双向多对多

    Hibernate的双向关联. 对象之间可以相互读取.        双向只针对读取的操作.对于增.删除.改的操作没有任何影响. 案例 : 实现双向多对多 MenuBean.java package ...

  2. (十一)Hibernate中的多表操作(1):单向一对多

    一.单向一对多() 案例一(用XML文件配置): 一个班级有多个学生,班级可以查看所有学生的信息. ClassBean.java package bean; import java.util.Hash ...

  3. (十二)Hibernate中的多表操作(2):单向多对一

    由“多”方可知“一”方的信息,比如多个员工使用同一栋公寓,员工可以知道公寓的信息,而公寓无法知道员工的信息. 案例一:使用xml配置 pojo类 Group.java package bean; // ...

  4. (十四)Hibernate中的多表操作(4):单向一对一

    案例一: 注解方式实现一对一 UserBean.java package bean; import java.io.Serializable; import javax.persistence.Col ...

  5. (十三)Hibernate中的多表操作(3):单向多对多

    多对多的处理方式是,有一张中间表,中间表保存两个多方之间的关系.首先来看实际应用场景:在之前开发的系统中,应用了基于角色的控制访问,也就是RBAC模型,一个用户可能存在多种角色,一种角色也可能有多个用 ...

  6. Hibernate中的多表查询及抓取策略

    1.Hibernate中的多表查询 1.1SQL中的多表查询 [交叉连接] select * from A,B; [内连接] 显示内连接:inner join(inner 可以省略) Select * ...

  7. Hibernate超简单多表操作

    所谓一对多映射 在数据库中我们通常会通过添加外键的方式将表关联起来,表现一对多的关系. 而在Hibernate中,我们则要通过在一方持有多方的集合来实现,即在"一"的一端中使用元素 ...

  8. Hibernate框架笔记03表操作多对多配置

    目录 1. 数据库表与表之间的关系 1.1 一对多关系 1.2 多对多关系 1.3 一对一关系[了解] 2. Hibernate的一对多关联映射 2.1 创建一个项目,引入相关jar包 2.2. 创建 ...

  9. 《Java从入门到放弃》入门篇:hibernate中的多表对应关系

    hibernate中的对应关系其实就是数据库中表的对应关系, 就跟某些电影中的某些场景是一样一样滴. 比如可以是一男一女,还可以是一男多女, 更可以是多男一女,最后最后最后还可以是多男多女!!! 有些 ...

随机推荐

  1. Visual Studio 中的 .NET Framework 类库

    Visual Studio 中的 .NET Framework 类库 .NET Framework 类库由命名空间组成.每个命名空间都包含可在程序中使用的类型:类.结构.枚举.委托和接口. 当您在 V ...

  2. java中POJO类和DTO类都要实现序列化

    java中POJO类和DTO类都要实现序列化 java中POJO类和DTO类都要实现序列化 java中POJO类和DTO类都要实现序列化 序列化:序列化是将对象转换为容易传输的格式的过程.例如,可以序 ...

  3. sizeThatFits and sizeToFit

    http://liuxing8807.blog.163.com/blog/static/9703530520134381526554/ sizeThatFits and sizeToFit是UIVie ...

  4. HDOJ 5402 Travelling Salesman Problem 模拟

    行数或列数为奇数就能够所有走完. 行数和列数都是偶数,能够选择空出一个(x+y)为奇数的点. 假设要空出一个(x+y)为偶数的点,则必须空出其它(x+y)为奇数的点 Travelling Salesm ...

  5. git bash here真牛!

    git bash here真牛! 在Windows上面安装了git,在文件夹里面空白处右键点击,选择git bash here: 随手敲了几个命令:ls,ls -a,which ls,who, fin ...

  6. 1.4-动态路由协议OSPF①

    r2#sh ip ospf border-routers 查看ABR 修改OSPF接口优先级 r1(config)#int e 0 r1(config-if)#ip ospf priority 100 ...

  7. 极客标签互动课程系列 - Javascript生成SVG动画素描特效

    课程描写叙述:在这个课程中,我们将介绍SVG.而且介绍怎样使用javascript来控制SVG生成素描动画效果 课程地址:http://www.gbtags.com/gb/gbliblist/21.h ...

  8. C#实现马尔科夫模型例子

    已知条件:三个缸N状态,每个缸中不同颜色球的个数M状态值,时间轴T,观察值序列O 参数:状态值序列,转移概率序列 求:概率 后台代码如下 , M = ;//N状态,M状态值 (0橙色,1绿色,2蓝色, ...

  9. Android中不同方向嵌套滑动的解决方式(ListView为样例)

    前言: 就像手机QQ的聊天消息列表.一个纵向滑动的ListView列举全部消息,但每一条消息能够横向滑动. 而默认情况下,仅仅能有一个地方消化处理触摸事件,要么ListView吃掉这个事件.要么子It ...

  10. 【Spark】Stage生成和Stage源代码浅析

    引入 上一篇文章<DAGScheduler源代码浅析>中,介绍了handleJobSubmitted函数,它作为生成finalStage的重要函数存在.这一篇文章中,我将就DAGSched ...