一. 一对多映射

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. 学习CSS布局 - position

    position 为了制作更多复杂的布局,我们需要讨论下 position 属性. 它有一大堆的值,名字还都特抽象,别提有多难记了. 让我们先一个个的过一遍,不过你最好还是把这页放到书签里. 先看下运 ...

  2. ASP.NET Core 释放 IDisposable 对象的四种方法

    本文翻译自<Four ways to dispose IDisposables in ASP.NET Core>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! IDispos ...

  3. linux 服务器安全加固和内核参数调优 nf_conntrack

    0.内部设置跳板机,服务器只能通过跳板机登录1.禁止ROOT用户远程登录和登录端口 禁止ROOT用户远程登录 .打开 /etc/ssh/sshd_config PermitRootLogin no . ...

  4. LOJ2541 PKUWC2018 猎人杀 期望、容斥、生成函数、分治

    传送门 首先,每一次有一个猎人死亡之后\(\sum w\)会变化,计算起来很麻烦,所以考虑在某一个猎人死亡之后给其打上标记,仍然计算他的\(w\),只是如果打中了一个打上了标记的人就重新选择.这样对应 ...

  5. Kafka:Configured broker.id 2 doesn't match stored broker.id 0 in meta.properties.

    在安装Kafka集群的时候,碰到这个问题. 我们知道在搭建Kafka集群的时候,我们需要设置broker.id,以作为当前服务器在整个集群的唯一标志. 网上搜查资料是说,log.dirs目录下的met ...

  6. centos7.4下Jira6环境部署及破解操作记录(完整版)

    废话不多说,以下记录了Centos7针对Jira6的安装,汉化,破解的操作过程,作为运维笔记留存. 0) 基础环境 192.168.10.212 Centos7.4 mysql 5.6 jdk 1.8 ...

  7. Python 工程管理及 virtualenv 的迁移

    virtualenv 是管理 python 工程的利器,它可以很好的帮你维护项目中的依赖,使用 virtualenv,还能保持 global 库的干净.不会被不同项目中的第三方库所污染. virtua ...

  8. 作业20171019 alpha-1成绩

    申诉 对成绩有疑问或不同意见的同学,请在群里[@杨贵福]. 申诉时间截止2017年11月21日 12:00. 总结 普遍成绩有明显上升,归功于1. 团队全都超额完成1次站立会议,多数团队完超额2次; ...

  9. JS 实现计算一段文字中的字节数,字母数,数字数,行数,汉字数。

    看到了匹配,第一个想到了用正则表达式,哈哈,果然很方便.不过正则表达式高深莫测!我还没有研究明白啊..目前学了点皮毛.代码如下: <!DOCTYPE html PUBLIC "-//W ...

  10. Individual P1: Preparation

    Individual Project - Word frequency program tally the frequency of words under a directory (2 modes) ...