一. 一对多映射

1.基本应用

1.1 准备项目

  • 创建项目:hibernate-02-relation

  • 引入jar,同前一个项目

  • 复制实体(客户)、映射、配置、工具类

1.2 创建订单表

表名: t_order

语句

 CREATE TABLE `t_order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`orderno` varchar(20) DEFAULT NULL COMMENT '订单编号',
`product_name` varchar(100) DEFAULT NULL COMMENT '商品名称',
`customer_id` bigint(20) DEFAULT NULL COMMENT '客户id',
PRIMARY KEY (`id`),
KEY `order_customer_fk` (`customer_id`),
CONSTRAINT `order_customer_fk` FOREIGN KEY (`customer_id`) REFERENCES `t_customer` (`c_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

1.3 创建Order实体类

 /**
* 订单(多方)
*/
public class Order {
private Long id;
private String orderno;
private String productName; //关联客户
private Customer customer; //getter seter toString
}

1.4 修改Customer实体类

添加关联订单

 /**
* 客户(一方)
*/
public class Customer{
private Long id;
private String name;
private Character gender;
private Integer age;
private String level; //关联订单
private Set<Order> orders = new HashSet<Order>();
//getter setter toString
}

1.5 Customer配置一对多

   <class name="Customer" table="t_customer">
......
<!-- 一对多配置 -->
<set name="orders">
<!-- 外键字段名称 -->
<key column="customer_id"></key>
<one-to-many class="Order"/>
</set>
</class>

1.6 Order配置多对一

 <hibernate-mapping package="com.qfedu.hibernate.pojo.one2many">

      <class name="Order" table="t_order">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="orderno" column="orderno"></property>
<property name="productName" column="product_name"></property> <!-- 多对一配置
name javaBean中的属性
class 属性的全路径
colunm 对应的列名
-->
-->
<many-to-one name="customer" class="com.itqf.domain.Customer" column="customer_id" />
</class>
</hibernate-mapping>

1.7 将映射文件加入hibernate.cfg.xml

 <mapping resource="/pojo/one2many/Customer.hbm.xml"/>
<mapping resource="/pojo/one2many/Order.hbm.xml"/>

1.8 测试新增关联数据

 public class One2manyTest {
/**
* 需求:1个客户 2张订单
*/
@Test
public void testCreateOrder(){
//准备数据
Customer cust = new Customer();
cust.setName("海伦");
cust.setGender('女');
cust.setAge(18);
cust.setLevel("VIP"); Order o1 = new Order();
o1.setOrderno("201709070001");
o1.setProductName("JavaWeb开发详解"); Order o2 = new Order();
o2.setOrderno("201709070002");
o2.setProductName("Spring开发详解"); Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction(); //建立一对多双向关系
cust.getOrders().add(o1);
cust.getOrders().add(o2); o1.setCustomer(cust);
o2.setCustomer(cust); session.save(cust);
session.save(o1);
session.save(o2); tx.commit();
session.close();
}
}

1.9 测试查询订单

 /**
* 查询操作
*/
@Test
public void testSearch(){
Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction(); //查询一个客户,关联查询订单
Customer cust = session.get(Customer.class, 3L);
System.out.println(cust.getName()+"的订单:");
Set<Order> orders = cust.getOrders();
for (Order order : orders) {
System.out.println(order.getOrderno()+","+order.getProductName());
} tx.commit();
session.close();
}
2.cascade级联操作

2.1. 测试级联保存

当只保存双向关联关系的一方时,会报告错误,此时应该在customer中配置级联保存

级联操作:就是操作一个对象的时候,想同时操作它的关联对象。

修改映射文件

<set name="orders" cascade="save-update">

如下用例,可以先测试查看报错信息;再配置上面的级联保存,然后再次进行测试,成功。

   /**
* 保存操作 - 级联保存
*/
@Test
public void testCascadeSave(){
//准备数据
Customer cust = new Customer();
cust.setName("海伦");
cust.setGender('女');
cust.setAge(18);
cust.setLevel("VIP"); Order o1 = new Order();
o1.setOrderno("201709070001");
o1.setProductName("JavaWeb开发详解"); Order o2 = new Order();
o2.setOrderno("201709070002");
o2.setProductName("Spring开发详解"); Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction(); //建立一对多单向关联
cust.getOrders().add(o1);
cust.getOrders().add(o2);
//o1.setCustomer(cust);
//o2.setCustomer(cust); session.save(cust);//使用级联保存 ( 想保存客户的时候,同时保存订单 )
//session.save(o1);//设置级联保存后不用保存订单
//session.save(o2); tx.commit();
session.close();
}

2.2 测试级联删除

当只删除父记录时,在删除客户的时候,Hibernate会把订单表的外键值置空,此时可以配置级联删除

<set name="orders" cascade="save-update,delete">

测试代码

    /**
* 级联删除
* 注意:
* 1)如果没有级联删除,那么在删除客户的时候,会把订单表的cust_id外键值设置为null
* 2)有了级联删除,那么在删除客户的时候,会同时把该客户的所有订单删除
*/
@Test
public void testCascadeDelete(){
//准备数据 Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction(); Customer cust = session.get(Customer.class, 4L);
session.delete(cust); tx.commit();
session.close();
}

3、inverse关系反转

3.1、分析前面的测试

  1. 运行级联保存的测试用例

  2. 查看日志中的sql语句

插入一个用户、两个订单,应该执行3个insert语句

但是发现日志中多打印了两个update语句

默认情况下inverse的值是false:

<set name="orders" cascade="all" inverse="false">

表示customer 一方需要维护关联关系,因此需要维护外键,有关联记录生成时,会做外键的更新操作。

而这个更新操作是没有必要的,因为order插入的时候已经将外键值插入。

所以customer中的update的语句是多余的

3.2、优化

inverse 配置:表示是否把关联关系的维护权反转(放弃)

false:默认值,不反转(不放弃)

true:反转(放弃)

放弃customer方的外键维护

<set name="orders" cascade="all" inverse="true">

重新测试,发现只有三条insert语句

3.3、也可以保存订单

step1:保存时,保存订单

  //建立一对多单向关联
//cust.getOrders().add(o1);
//cust.getOrders().add(o2);
o1.setCustomer(cust);
o2.setCustomer(cust);
//session.save(cust);//使用级联保存 ( 想保存客户的时候,同时向保存订单 )
session.save(o1);
session.save(o2);

step2:在订单端设置级联保存

<!-- 多对一配置 -->
<many-to-one name="customer" class="Customer" column="customer_id" cascade="all"/>

3.4、结论

通常在一对多的关联配置中,多方无法放弃关系维护权,所以应该放弃 1 方的维护权,意味着在 1 方加上 inverse=true 配置

二. 多对多映射

需求: 用户与角色是多对多的关系

1.基本配置

1.1 创建User实体类

 public class User{

     private Integer id;
private String name; //关联角色
private Set<Role> roles = new HashSet<Role>();
}

1.2 创建Role实体类

 public class Role{
private Integer id;
private String name; //关联用户
private Set<User> users = new HashSet<User>();
}

1.3 User映射配置

 <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.qfedu.hibernate.pojo.many2many"> <class name="User" table="t_user">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property> <!-- 多对多映射 -->
<!--
table:中间表名
-->
<set name="roles" table="t_user_role" >
<!-- 当前方在中间表的外键 -->
<key column="user_id"/>
<!-- column:对方在中间表的外键 -->
<many-to-many class="Role" column="role_id"/>
</set>
</class>
</hibernate-mapping>

1.4 Role配置

 <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.qfedu.hibernate.pojo.many2many"> <class name="Role" table="t_role">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property> <!-- 多对多映射 -->
<!--
table:中间表名
-->
<set name="users" table="t_user_role" >
<!-- 当前方在中间表的外键 -->
<key column="role_id"/>
<!-- column:对方在中间表的外键 -->
<many-to-many class="User" column="user_id"/>
</set>
</class> </hibernate-mapping>

1.5 核心配置文件添加映射路径

   <mapping resource="/pojo/many2many/User.hbm.xml"/>
<mapping resource="/pojo/many2many/Role.hbm.xml"/>

1.6、测试增加

注意:以下测试用例如果直接执行,会报告联合主键插入重复的错误。因此可以在任意一方设置inverse选项=true

<set name="users" table="t_user_role" inverse="true">

测试代码:

 public class Many2manyTest {
/**
* 需求:创建一个用户一个角色
*/
@Test
public void testCreateUser() { User u1 = new User();
u1.setName("Helen1"); Role r1 = new Role();
r1.setName("超级管理员1"); u1.getRoles().add(r1);
r1.getUsers().add(u1); Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction();
//双向都保存
session.save(u1);
session.save(r1); tx.commit();
session.close();
}
}

4.7、级联保存

注意:在多对多的保存中,如果不设置级联保存,也不设置inverse="true",那么会报告联合主键重复的错误。

可以设置级联保存,在User的多对多关联中设置如下:

<set name="roles" table="t_user_role" cascade="save-update">

测试代码:

 public class Many2manyTest {
/**
* 需求:创建一个用户一个角色
*/
@Test
public void testCreateUser() { User u1 = new User();
u1.setName("Helen1"); Role r1 = new Role();
r1.setName("超级管理员1"); u1.getRoles().add(r1);
//r1.getUsers().add(u1); Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction(); session.save(u1);
//session.save(r1); tx.commit();
session.close();
}
}

4.8、级联删除

当没有设置级联删除的时候,如果删除User表中的记录,那么只删除User表和关联表中的记录

当设置了级联删除的时候,如果删除User表中的记录,那么会将User表、关联表和Role表中的记录全部删除!

<set name="roles" table="t_user_role" cascade="save-update,delete">

测试:

     @Test
public void testCascadeDelete() { Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction(); User u = session.get(User.class, 6);
session.delete(u); tx.commit();
session.close();
}

三. 一对一映射的两种设计方案

需求:公民表和身份证表是一对一的关系

设计表的两种方案:

1. 一对一唯一外键关联

1.1 创建持久化类

Person

 public class Person {
private Integer id;
private String name; //关联身份证
private Card card;
}

Card

 public class Card {
private Integer id;
private String cardno; //关联公民
private Person person;
}

1.2 配置映射文件

Person.hbm.xml

<hibernate-mapping package="pojo.one2one_fk">

     <class name="Person" table="t_person">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property> <!-- 一对一映射 -->
<one-to-one name="card" class="Card" />
</class> </hibernate-mapping>

Card.hbm.xml

 <hibernate-mapping package="pojo.one2one_fk">

      <class name="Card" table="t_card">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="cardno" column="cardno"></property> <!-- 唯一外键(一对一) -->
<many-to-one name="person" class="Person" column="person_id" unique="true" />
</class> </hibernate-mapping>

1.3 核心配置

    <mapping resource="/pojo/one2one_fk/Person.hbm.xml"/>
<mapping resource="/pojo/one2one_fk/Card.hbm.xml"/>

1.4 测试

 public class One2OneTest {
@Test
public void testCreatePerson() { Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction(); Person p = new Person();
p.setName("Helen"); Card c = new Card();
c.setCardno("1234"); p.setCard(c);
c.setPerson(p); session.save(p);
session.save(c); tx.commit();
session.close();
}
}

2. 一对一主键关联

2.1 创建持久化类

Person

 public class Person {
private Integer id;
private String name; //关联身份证
private Card card;
}

Card

 public class Card {
private Integer id;
private String cardno; //关联公民
private Person person;
}

2.2 配置

Person.hbm.xml

 <hibernate-mapping package="pojo.one2one_pk">

      <class name="Person" table="t_person_pk">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property> <!-- 主键(一对一映射) -->
<one-to-one name="card" class="Card" />
</class> </hibernate-mapping>

Card.hbm.xml

 <hibernate-mapping package="pojo.one2one_pk">

      <class name="Card" table="t_card_pk">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="cardno" column="cardno"></property> <!-- 关联主键(一对一) -->
<!--constrained="true" 表示检查约束,查询时使用select查询,而不是join查询方式-->
<one-to-one name="person" class="Person" constrained="true" /> </class> </hibernate-mapping>

2.3 修改核心配置文件

   <mapping resource="/pojo/one2one_pk/Person.hbm.xml"/>
<mapping resource="/pojo/one2one_pk/Card.hbm.xml"/>

2.4 测试

 public class One2OneTestPK {
@Test
public void testCreatePerson() { Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction(); Person p = new Person();
p.setName("Helen"); Card c = new Card();
c.setCardno("1234"); p.setCard(c);
c.setPerson(p); session.save(p);
session.save(c); tx.commit();
session.close();
}
}

Hibernate表关系03的更多相关文章

  1. Hibernate表关系映射之一对一映射

    一.数据表的映射关系 在数据库领域中,数据表和数据表之间关系一般可以分为如下几种: 一对一:比如公民和身份证的关系,一个人只有一张身份证,同时每张身份证也仅仅对应一个人! 一对多:比如客户和订单之间的 ...

  2. Hibernate表关系映射之多对多映射

    一.多对多的实现原理 在数据库中实现多对多的关系,必须使用连接表.也就是用一个独立的表来存入两个表的主键字段,通过遍历这张表来获取两表的关联关系. 而在我们的对象中,多对多是通过两者对象类中互相建立对 ...

  3. Hibernate表关系映射之一对多映射

    一.基本概述 在表中的一对多,是使用外键关联,通过一张表的一个键另一个表的外键来建立一多关系;而在类中表示为一个类中有一个集合属性包含对方类的很多对象,而在另一个类中,只包含前述类的一个对象,从而实现 ...

  4. hibernate表关系

    1.一对一 用户表可以查分成两个表,一个userInfo.一个userLogin表 实现方式: (1)使用外键:外键+唯一性约束+非空约束 (2)公用主键:公用主键,从表的主键同时也是外键,来源于主表 ...

  5. Hibernate框架进阶(中篇)之多表关系

    导读 Hibernate进阶主要分为上中下三篇,本文是中篇,主要讲解Hibernate框架中多表关系的实现.我们知道多表关系有一对一.一对多(多对一)和多对多三种关系.而1对1关系一般合并为一个表处理 ...

  6. Hibernate的多表关系

    多表关系 一对多/多对一 O 对象 一的一方使用集合. 多的一方直接引用一的一方. R 关系型数据库 多的一方使用外键引用一的一方主键. M 映射文件: 一: <set name="& ...

  7. hibernate(3) —— 关系映射

    hibernate中关系映射指的是实体类与实体类间的关系.和数据库中表与表之间的关系类似,有一对一,多对一,一对多,多对多四种映射关系. 一:一对一映射 两个对象之间是一对一的关系,如人和身份证之间是 ...

  8. SSH网上商城---需求分析+表关系分析

    SSH---小编初次接触的时候傻傻的以为这个跟SHE有什么关系呢?又是哪路明星歌手,后来才知道小编又土鳖了,原来SSH是这个样子滴,百度百科对她这样阐述,SSH即 Spring + Struts +H ...

  9. Hibernate注解关系映射

    Hibernate Annotation关系映射的几种类型映射用法及使用方法(说明:以前实例的实体是user和role,主键分别是userid和roleid)   1)一对一外键关联映射(单向) @O ...

随机推荐

  1. UOJ219 NOI2016 优秀的拆分 二分、字符串哈希

    传送门 题目可以转化为求\(AA\)的数量,设\(cnt1_x\)表示左端点为\(x\)的\(AA\)的数量,\(cnt2_x\)表示右端点为\(x\)的\(AA\)的数量,那么答案就是\(\sum ...

  2. flask多app和栈的应用

    一.简介     flask的蓝图可以实现url的分发,当有多个app时也可以利用app进行url分发,这里介绍下使用方式和内部原理以及栈的应用. 二.多app使用 使用示例 from werkzeu ...

  3. Sharding模式

    将数据存储为一组水平的数据分区.这种模式可以在存储和访问大量的数据的时候提高可扩展性. 场景和问题 由单个服务器托管的数据存储可能受到下列限制: 存储空间限制.基于大规模云应用所使用的数据仓库,可能会 ...

  4. JVM规范系列:总结

    我们花了几天的时间来阅读<Java虚拟机规范>,了解要实现一个虚拟机应该包括什么内容.通过这么一次阅读,我们大致了解了虚拟机规范的内容. 第1章.对Java虚拟机进行了一些简单的介绍. 第 ...

  5. SpringBoot日记——分布式篇

    思考:什么是分布式?什么是微服务? 一些概念:RPC-远程过程调用,某台机器想要调用另一台机器所需要的一种服务,及分布式的服务框架,比如dubbo或者SpringCloud. 铺天盖地的分布式互联网系 ...

  6. 分布式监控系统Zabbix-3.0.3--短信报警设置

    前面已分别介绍了zabbix的邮件.微信报警设置,这些都是手机在有网络时才能收到报警信息,那如果手机没有网的情况下怎么办,这就需要考虑使用短信接口报警了.当服务出现故障达到预警级别是通过发送短信的形式 ...

  7. svn代码发版的脚本分享

    背景:开发将其代码放到svn里面,如何将修改后存放到svn里的代码发布到线上?简单做法:写个shell脚本,用于代码发版.比如开发的代码存放svn的路径是:svn://112.168.19.120/h ...

  8. kvm虚拟化管理平台WebVirtMgr部署-完整记录(3)

    继下面三篇文章完成了kvm虚拟化管理平台webvirtmgr环境的部署安装:kvm虚拟化管理平台WebVirtMgr部署-虚拟化环境安装-完整记录(0)kvm虚拟化管理平台WebVirtMgr部署-完 ...

  9. beta阶段测试基本概况对应机型硬件信息

    机型测试概况 测试结果 测试终端数 品牌分布分析 系统分布分析 分辨率分布 未执行 1 联想 4.0.3 480*800 安装失败 1 联想 4.2.1 480*854 通过 119 华为, 三星, ...

  10. 【CV】ICCV2015_Describing Videos by Exploiting Temporal Structure

    Describing Videos by Exploiting Temporal Structure Note here: it's a learning note on the topic of v ...