Hibernate框架技术视频课程——笔记(三)

视频课程链接:http://edu.51cto.com/course/10747.html

一、Hibernate关联关系

1. 实体之间的关系

  • 泛化关系

    通过对象之间的继承来实现

  • 关联关系

    通过一个对象持有另一个对象的实例来实现

    类与类之间最常见的关系就是关联关系

2. 关联关系

  • 多对一
  • 一对多
  • 多对多
  • 一对一

二、 多对一关系

1. 数据库表

create table t_dept(
    id int primary key auto_increment,
    name varchar(200)
)engine=Innodb default charset=utf8;

create table t_emp(
    id int primary key auto_increment,
    name varchar(200),
    dept_id int,
    foreign key(dept_id) references t_dept(id)
)engine=Innodb default charset=utf8;

多对一关系:多个员工在同一个部门

​ 多方 ——> 员工

​ 一方 ——> 部门

2. 配置方式

public class Emp implements Serializable {
    private Integer id;
    private String name;
    private Dept dept; // 在多方定义一方的引用 
    //...
}
<!-- 
   配置多对一关系
    name:属性名
    column:列名,外键列
    class:属性的类型
   -->
<many-to-one name="dept" column="dept_id" class="Dept"></many-to-one> 

3. load/get查询

3.1 默认不查询关联数据

调用load/get查询对象时,默认只会查询当前类对象的表,不会查询关联表数据,称为关联数据的延迟加载

只有当第一次访问时才会进行数据的查询

Session session = HibernateUtil.getSession();
TransactionManager.beginTransaction();

Emp emp=(Emp) session.get(Emp.class, 1);
System.out.println(emp.getName());
System.out.println("--------------------------");
System.out.println(emp.getDept().getName());

TransactionManager.commit();

3.2 关于no Session问题

​ 当执行下面代码时会出现no Session问题

Emp emp=null;
try {
  Session session = HibernateUtil.getSession();
  TransactionManager.beginTransaction();

  emp=(Emp) session.get(Emp.class, 1);
  System.out.println(emp.getName());
  System.out.println("--------------------------");

  TransactionManager.commit();
} catch (Exception e) {
  TransactionManager.rollback();
  e.printStackTrace();
} finally {
  HibernateUtil.close();
}
System.out.println(emp.getDept().getName());

​ 如何解决:

  • 使用lazy="false"
  • 使用fetch="join"

3.3 lazy配置

<!--                
lazy:数据加载策略,可取值如下
     false:立即加载关联数据
       使用的是:两次查询
     proxy:懒加载,以代理对象的方式进行延迟加载,默认值   
     no-proxy:懒加载,  该方式在编译时需要进行 字节码增强,否则和proxy没区别,很少使用
   -->
<many-to-one name="dept" column="dept_id" class="Dept" lazy="no-proxy"></many-to-one> 

3.4 fetch配置

<!--
fetch:抓取数据,可取值如下
     join:立即加载,使用联接查询
       使用的是:一次查询!!
       注:此时会lazy配置无效,总是会立即加载关联数据,使用联接查询
     select:    懒加载,会再执行一次selecg查询,默认值
   -->
<many-to-one name="dept" column="dept_id" class="Dept" lazy="false" fetch="join"></many-to-one> 

4. HQL单表查询

Emp emp= null;
try {
  Session session = HibernateUtil.getSession();
  TransactionManager.beginTransaction();

  String hql="from Emp e where e.id=:id";
  emp=(Emp) session.createQuery(hql).setInteger("id", 1).uniqueResult();
  System.out.println(emp.getName());
  System.out.println("----------------------");

  TransactionManager.commit();
} catch (Exception e) {
  TransactionManager.rollback();
  e.printStackTrace();
} finally {
  HibernateUtil.close();
}
System.out.println(emp.getDept().getName());

结论:对于HQL查询,lazy配置项有效,fetch配置项无效

5. HQL联接查询

5.1 left join

​ left join 只做联接操作,不查关联数据

​ 根据lazy配置项,择机进行再次查询

5.2 left join fetch

​ left join fetch既做联接操作,也查关联数据

​ lazy配置项无效,总是使用联接查询,一次性将数据都查询出来

6. 增删改操作

6.1 基本操作

​ 添加操作:

  • 多方对象中的一方为null持久态游离态

    ​ 正常保存

  • 多方对象中的一方为临时态

    ​ 报错:

    org.hibernate.TransientObjectException: object references an unsaved transient instanc

    解决方法:

    • 转换为持久态
    • 配置cascade

6.2 cascade配置

<!--    
    cascade:级联操作
     none:不进行级联操作,默认值
     all:对所有操作进行级联操作(insert/delete/update)  
     save-update:执行保存和更新时进行级联操作
     delete:执行删除时进行级联操作,主要用于一对多操作
   -->
<many-to-one name="dept" column="dept_id" class="Dept" lazy="false" fetch="select" cascade="delete"></many-to-one> 

三、一对多关系

1. 数据库表

create table t_class(
    id int primary key auto_increment,
    name varchar(200)
)engine=Innodb default charset=utf8;

create table t_student(
    id int primary key auto_increment,
    name varchar(200),
    class_id int,
    foreign key(class_id) references t_class(id)
)engine=Innodb default charset=utf8;

一对多关联:一个班级包含多名学生

​ 一方 —— > 班级

​ 多方 —— > 学生

2. 配置方式

<!-- 
   配置一对多关系
    name:属性名
    table:属性关联的表

    cascade:级联操作
     none
     all
     save-update
     delete:级联删除,删除的过程:
      1.将所有引用一方数据的外键全部设置为null
       将clazz对象对应的所有student的class_id都设置为null
      2.删除对应set集合中的数据
       将clazz中的set集合中的student删除
      3.删除自己对应的数据
       将clazz对象删除           
   -->
<set name="students" table="t_student" lazy="false" fetch="join" cascade="delete">
  <!-- 关联的列,外键列,即关联表t_student中的哪个字段关联到t_class表 -->
  <key column="class_id"></key>
  <!-- 关联的类,属性的类型 -->
  <one-to-many class="Student"/>
</set> 

3. inverse配置

​ inverse 反转,一般指控制权的反转

<!--
inverse:反转
     false:一方维护关联关系,默认值
     true:由对方维护关联关系,即由多方维护,一方放弃对set集合的维护                
   -->
<set name="students" table="t_student" lazy="true" fetch="select" inverse="true">

4. 双向关联关系

4.1 双向一对多

​ 前面我们配置的都是单向的关系:

  • 单向多对一
  • 单向一对多

    ​如果同时配置了单向多对一和单向一对多,则就是双向一对多双向多对一

    ​inverse属性只能在<set>中配置,一般都会配置,因为关联关系由多方维护更合适

4.2 关于栈溢出的问题

​ 重写实体类的toString()的方法

Exception in thread "main" java.lang.StackOverflowError

原因:因为重写了关联实体的toString()方法,并且包含关联属性,当调用toString()方法时会导航查询关联对象,关联对象双会导航查询对方,最终导致栈溢出。

解决:不要重写toString()方法,或者 重写toString()时不要包含关联属性

四、多对多关系

1. 数据库表

关系型数据库中如何实现多对多关系?

  • 使用中间表,将多对多转换为两个一对多
  • 用中间表来维护多对多关系和数据
create table t_stu(
    id int primary key auto_increment,
    name varchar(200)
)engine=Innodb default charset=utf8;

create table t_course(
    id int primary key auto_increment,
    name varchar(200)
)engine=Innodb default charset=utf8;

create table t_stu_course(
    id int primary key auto_increment,
    stu_id int,
    course_id int,
    foreign key(stu_id) references t_stu(id),
    foreign key(course_id) references t_course(id)
)engine=Innodb default charset=utf8;

多对多关系:一个学生可以选择多门课程,一个课程也可以被多个学生选择

  • 多方 ——> 学生
  • 多方 ——> 课程

2. 配置方式

<!-- 配置多对多关系 -->
<set name="courses" table="t_stu_course">
  <!-- 关联到当前类Stu的外键列 -->
  <key column="stu_id"></key>
  <!-- class指定属性的类型,column指定关系的列,外键列 -->
  <many-to-many class="Course" column="course_id"></many-to-many>
</set>

3. 基本操作

五、一对一关系

1. 两种方式

  • 基于外键的一对一映射
  • 基于主键的一对一映射

2. 基于外键的一对一

2.1 数据库表

create table t_card(    
    id int primary key auto_increment,
    name varchar(200)
)engine=Innodb default charset=utf8;

create table t_person(  
    id int primary key auto_increment,
    name varchar(200),
    card_id int unique, -- 通过指定unique将多对一关系变成一对一关系
    foreign key(card_id) references t_card(id)
)engine=Innodb default charset=utf8;

2.2 配置方式

<!-- 
   使用many-to-one配置一对一关联关系,通过unique变为一对关联关系
    name:属性名
    column:外键列
    class:属性类型
    unique:将多对一变成一对一
   -->
<many-to-one name="card" column="card_id" class="Card" unique="true"></many-to-one>
<!-- 
   使用one-to-one配置一对一关联关系
    property-ref:引用的关联类的属性,使用外键关联
   -->
<one-to-one name="person" class="Person" property-ref="card"></one-to-one>

2.3 基本操作

3. 基于主键的一对一

​ 通过主键来指定一对一的关系

六、组件映射

1. 简介

​ 将一个对象的多个属性封装成单独的另一个对象,这个封装的对象称为组件Component,组件不需要单独的映射文件

​ 对象关系:一个对象是另一个对象的一部分

2. 数据库表

create table t_consumer(    
    id int primary key auto_increment,
    age int,
    first_name varchar(200),
    last_name varchar(200)
)engine=Innodb default charset=utf8;

3. 配置方式

<!-- 
   配置组件映射关系
    name:属性名
    class:属性类型
   -->
<component name="name" class="Name">
  <property name="firstName" column="first_name"></property>
  <property name="lastName" column="last_name"></property>
</component>

猜你喜欢

转载自blog.51cto.com/12402007/2159436