Hibernate表关系详细教案回顾任务目标关联映射级联操作一. 一对多映射1.基本应用1.1 准备项目1.2 创建订单表1.3 创建Order实体类1.4 修改Customer实体类1.5 Customer配置一对多1.6 Order配置多对一1.7 将映射文件加入hibernate.cfg.xml1.8 测试新增关联数据1.9 测试查询订单2、cascade级联操作2.1. 测试级联保存2.2 测试级联删除3、inverse关系反转3.1、分析前面的测试3.2、优化3.3、也可以保存订单3.4、结论二. 多对多映射1.基本配置1.1 创建User实体类1.2 创建Role实体类1.3 User映射配置1.4 Role配置1.5 核心配置文件添加映射路径1.6、测试增加4.7、级联保存4.8、级联删除三. 一对一映射的两种设计方案1. 一对一唯一外键关联1.1 创建持久化类1.2 配置映射文件1.3 核心配置1.4 测试2. 一对一主键关联2.1 创建持久化类2.2 配置2.3 修改核心配置文件2.4 测试课前默写作业面试题
1. Hibernate中事务特征及其传播性 2. 更新数据丢失问题 3. Hibernate中的主键策略 4. Hibernate对象的三种状态 5. Hibernate的一级缓存策略
1. 一对多映射 2. 多对多映射 3. 一对一映射
1. 掌握一对多映射 2. 掌握多对多映射 3. 掌握一对一映射
复杂表关系获取
复杂表关系保存
复杂表关系删除
放弃外键维护
技术分析cascade和inverse
hibernate-02-relation表名: t_order
语句
xxxxxxxxxxCREATE 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 x/** * 订单(多方) */public class Order { private Long id; private String orderno; private String productName; //关联客户 private Customer customer; //getter seter toString} 添加关联订单
xxxxxxxxxx/** * 客户(一方) */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} xxxxxxxxxx <class name="Customer" table="t_customer"> ...... <!-- 一对多配置 --> <set name="orders"> <!-- 外键字段名称 --> <key column="customer_id"></key> <one-to-many class="Order"/> </set> </class> xxxxxxxxxx<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> xxxxxxxxxx<mapping resource="com/qfedu/hibernate/pojo/one2many/Customer.hbm.xml"/><mapping resource="com/qfedu/hibernate/pojo/one2many/Order.hbm.xml"/> xxxxxxxxxxpublic class One2manyTest { /** * 需求:1个客户 2张订单 */ 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(); }} xxxxxxxxxx /** * 查询操作 */ 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(); }当只保存双向关联关系的一方时,会报告错误,此时应该在customer中配置级联保存
级联操作:就是操作一个对象的时候,想同时操作它的关联对象。
修改映射文件
xxxxxxxxxx<set name="orders" cascade="save-update">如下用例,可以先测试查看报错信息;再配置上面的级联保存,然后再次进行测试,成功。
xxxxxxxxxx /** * 保存操作 - 级联保存 */ 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(); }当只删除父记录时,在删除客户的时候,Hibernate会把订单表的外键值置空,此时可以配置级联删除
xxxxxxxxxx<set name="orders" cascade="save-update,delete">测试代码
xxxxxxxxxx /** * 级联删除 * 注意: * 1)如果没有级联删除,那么在删除客户的时候,会把订单表的cust_id外键值设置为null * 2)有了级联删除,那么在删除客户的时候,会同时把该客户的所有订单删除 */ 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个insert语句
但是发现日志中多打印了两个update语句
默认情况下inverse的值是false:
xxxxxxxxxx<set name="orders" cascade="all" inverse="false">表示customer 一方需要维护关联关系,因此需要维护外键,有关联记录生成时,会做外键的更新操作。
而这个更新操作是没有必要的,因为order插入的时候已经将外键值插入。
所以customer中的update的语句是多余的
inverse 配置:表示是否把关联关系的维护权反转(放弃)
false:默认值,不反转(不放弃)
true:反转(放弃)
放弃customer方的外键维护
xxxxxxxxxx<set name="orders" cascade="all" inverse="true">重新测试,发现只有三条insert语句!
step1:保存时,保存订单
xxxxxxxxxx //建立一对多单向关联 //cust.getOrders().add(o1); //cust.getOrders().add(o2); o1.setCustomer(cust); o2.setCustomer(cust); //session.save(cust);//使用级联保存 ( 想保存客户的时候,同时向保存订单 ) session.save(o1); session.save(o2);step2:在订单端设置级联保存
xxxxxxxxxx<!-- 多对一配置 --><many-to-one name="customer" class="Customer" column="customer_id" cascade="all"/>通常在一对多的关联配置中,多方无法放弃关系维护权,所以应该放弃 1 方的维护权,意味着在 1 方加上 inverse=true 配置
需求: 用户与角色是多对多的关系
xxxxxxxxxxpublic class User{ private Integer id; private String name; //关联角色 private Set<Role> roles = new HashSet<Role>();} xxxxxxxxxxpublic class Role{ private Integer id; private String name; //关联用户 private Set<User> users = new HashSet<User>();} xxxxxxxxxx <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> xxxxxxxxxx <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> xxxxxxxxxx <mapping resource="com/qfedu/hibernate/pojo/many2many/User.hbm.xml"/> <mapping resource="com/qfedu/hibernate/pojo/many2many/Role.hbm.xml"/>注意:以下测试用例如果直接执行,会报告联合主键插入重复的错误。因此可以在任意一方设置inverse选项=true
xxxxxxxxxx<set name="users" table="t_user_role" inverse="true">测试代码:
xxxxxxxxxxpublic class Many2manyTest { /** * 需求:创建一个用户一个角色 */ 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(); }}注意:在多对多的保存中,如果不设置级联保存,也不设置inverse="true",那么会报告联合主键重复的错误。
可以设置级联保存,在User的多对多关联中设置如下:
xxxxxxxxxx<set name="roles" table="t_user_role" cascade="save-update">测试代码:
xxxxxxxxxxpublic class Many2manyTest { /** * 需求:创建一个用户一个角色 */ 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(); }}当没有设置级联删除的时候,如果删除User表中的记录,那么只删除User表和关联表中的记录
当设置了级联删除的时候,如果删除User表中的记录,那么会将User表、关联表和Role表中的记录全部删除!
xxxxxxxxxx<set name="roles" table="t_user_role" cascade="save-update,delete">测试:
xxxxxxxxxx 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(); }需求:公民表和身份证表是一对一的关系
设计表的两种方案:

Person
xxxxxxxxxxpackage com.qfedu.hibernate.pojo.one2one_fk;public class Person { private Integer id; private String name; //关联身份证 private Card card;}Card
xxxxxxxxxxpackage com.qfedu.hibernate.pojo.one2one_fk;public class Card { private Integer id; private String cardno; //关联公民 private Person person;}Person.hbm.xml
xxxxxxxxxx<hibernate-mapping package="com.qfedu.hibernate.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
xxxxxxxxxx<hibernate-mapping package="com.qfedu.hibernate.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> xxxxxxxxxx <mapping resource="com/qfedu/hibernate/pojo/one2one_fk/Person.hbm.xml"/> <mapping resource="com/qfedu/hibernate/pojo/one2one_fk/Card.hbm.xml"/> xxxxxxxxxxpublic class One2OneTest { 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(); }}Person
xxxxxxxxxxpublic class Person { private Integer id; private String name; //关联身份证 private Card card;}Card
xxxxxxxxxxpackage com.qfedu.hibernate.pojo.one2one_pk;public class Card { private Integer id; private String cardno; //关联公民 private Person person;}Person.hbm.xml
xxxxxxxxxx<hibernate-mapping package="com.qfedu.hibernate.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
xxxxxxxxxx<hibernate-mapping package="com.qfedu.hibernate.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> <!-- 关联主键(一对一) --> <one-to-one name="person" class="Person" constrained="true" /> </class> </hibernate-mapping> xxxxxxxxxx <mapping resource="com/qfedu/hibernate/pojo/one2one_pk/Person.hbm.xml"/> <mapping resource="com/qfedu/hibernate/pojo/one2one_pk/Card.hbm.xml"/> xxxxxxxxxxpublic class One2OneTestPK { 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(); }}1. Hibernate中事务传播性的几种方案 2. Hibernate中的主键的几种实现策略 3. Hibernate对象的三种状态相互转换的方法
xxxxxxxxxx一、使用如下表关系完成以下功能1. 配置表之间的关系2. 使用单元测试完成各表的CRUD操作(测试级联操作)表名 employee 中文表名称 人员信息表序号 字段名称 字段说明 类型 属性 备注1 id 编号 int 自增 主键2 name 用户名 varchar(20) 非空 3 birthday 生日 varchar(20) 非空 4 gender 性别 varchar(4) 5 career 职业 varchar(20) 6 address 地址 varchar(50) 7 mobile 手机 varchar(20)8 posid 职位 int 外键表名 position 中文表名称 职业信息表序号 字段名称 字段说明 类型 属性 备注1 id 编号 int 自增 主键2 name 职业名称 varchar(20) 非空 二、根据以下关系自己设计表,并完成各表的CRUD操作用户对角色(多对一)角色对权限(多对多)提示:一共建立4张表1. Hibernate中关联映射的配置方法 2. Hibernate多对多映射方案(组合主键、非组合主键) 3. Hibernate中Cascade关键字和inverse关键字的用法