Hibernate第三天——表间关系与级联操作
第三天,我们来使用Hibernate进行表之间一对多 多对多关系的操作:
这里我们先利用两个例子进行表关系的回顾:
一对多(重点): 例如分类和商品的关系,一个分类多个商品,一个商品属于一个分类
CRM 客户关系管理
客户和联系人:
客户:一般指的是有业务往来的公司(例如百度、腾讯)
联系人:公司里的员工(和联系人联系就联系上公司)
这里的客户和联系人就是一对多的关系(一个公司多个员工,一个员工从属与一个公司)
如何建表:通过外键建立关系
在多的那一方建立一个外键(这个外键指向客户的主键)(因为在一的那一方无法创建出合理的外键)
利用Hibernate建表无需我们手动建表,只需在 Hibernate中配置这个映射关系即可自动生成这个一对多的表
多对多(重点):例如订单和商品的关系,一个订单有多个商品,一个商品可以属于多个订单
用户和角色的关系:
一个用户可以是多个角色,一个角色可以对应多个用户
例如,小王是总经理,也可以是司机;小马可以是司机,也可以是秘书
如何建表:
需要建立第三张表维护两者的关系
至少要有两个外键指向两张表的主键
一对一(较少见):一夫一妻制(当然我们说的是中国的合法关系)
下面我们进行一对多的配置:
一对多的配置:
环境搭建完成后
一、创建实体类(客户和联系人)
二、让实体类之间先互相进行表示,客户实体类有多个联系人,一个联系人属于一个客户
//一个客户多个联系人,要求使用集合表示,但必须使用Set(不可重复)
三、配置映射关系
1.一般一个实体类对应一个映射文件,配置完各自的基本配置
2.配置一对多关系
在客户的映射文件中表示所有的联系人
在联系人表示联系人所属的客户
四、创建核心配置文件
引入映射配置文件(注意是两个)
先看大致的一对多的目录结构:
我们来看看实体类及其配置文件:
客户:
package cn.entity; import java.util.HashSet;
import java.util.Set; public class Customer { //客户ID
private Integer cid;
//客户名称
private String custName;
//客户级别
private String custLevel;
//客户来源
private String custSource;
//客户电话
private String custPhone;
//客户手机
private String custMobile; //一个客户多个联系人,要求使用集合表示,但必须使用Set
private Set<LinkMan> set_LinkMan = new HashSet<LinkMan>(); public Set<LinkMan> getSet_LinkMan() {
return set_LinkMan;
}
public void setSet_LinkMan(Set<LinkMan> set_LinkMan) {
this.set_LinkMan = set_LinkMan;
}
//空构造器
public Customer() { }
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
public String getCustMobile() {
return custMobile;
}
public void setCustMobile(String custMobile) {
this.custMobile = custMobile;
} }
联系人:
package cn.entity; public class LinkMan { //联系人编号
private Integer lkm_id;
//联系人姓名
private String lkm_name;
//联系人性别
private String lkm_gender;
//联系人电话
private String lkm_phone;
//一个联系人对应一个客户
private Customer customer; public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
//空构造器
public LinkMan() { }
public Integer getLkm_id() {
return lkm_id;
}
public void setLkm_id(Integer lkm_id) {
this.lkm_id = lkm_id;
}
public String getLkm_name() {
return lkm_name;
}
public void setLkm_name(String lkm_name) {
this.lkm_name = lkm_name;
}
public String getLkm_gender() {
return lkm_gender;
}
public void setLkm_gender(String lkm_gender) {
this.lkm_gender = lkm_gender;
}
public String getLkm_phone() {
return lkm_phone;
}
public void setLkm_phone(String lkm_phone) {
this.lkm_phone = lkm_phone;
} }
客户映射文件:
<?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>
<!-- 映射的类 name是类的全路径 table是表名 -->
<class name="cn.entity.Customer" table="t_customer">
<id name="cid" column="cid">
<!-- 设置id增长策略 -->
<generator class="native"></generator>
</id>
<!-- 配置其它属性 -->
<property name="custName" column="custName"></property>
<property name="custLevel" column="custLevel"></property>
<property name="custSource" column="custSource"></property>
<property name="custPhone" column="custPhone"></property>
<property name="custMobile" column="custMobile"></property> <!-- 在客户的映射文件中表示所有的联系人 -->
<!-- 使用set集合表示所有联系人,name为客户实体类中表示联系人的set集合名称 -->
<set name="set_LinkMan" cascade="save-update,delete" inverse="true">
<!-- column属性值为外键的名字 -->
<key column="clid"></key>
<!-- 多的那一方的全路径 -->
<one-to-many class="cn.entity.LinkMan"/>
</set>
</class>
</hibernate-mapping>
联系人映射文件:
<?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>
<!-- 映射的类 name是类的全路径 table是表名 -->
<class name="cn.entity.LinkMan" table="t_linkman">
<id name="lkm_id" column="lkm_id">
<!-- 设置id增长策略 -->
<generator class="native"></generator>
</id>
<!-- 配置其它属性 -->
<property name="lkm_name" column="lkm_name"></property>
<property name="lkm_gender" column="lkm_gender"></property>
<property name="lkm_phone" column="lkm_phone"></property>
<!-- 在联系人表示联系人所属的客户 -->
<!-- name为表示客户的那个对象名称(不是对象类型) class为客户实体类的全路径 column为外键名称 -->
<many-to-one name="customer" class="cn.entity.Customer" column="clid"></many-to-one>
</class>
</hibernate-mapping>
在核心配置文件中引入资源(这里只写出核心的两句,其它相同的此处不再赘述):
<!-- 三、引入映射文件 -->
<mapping resource="cn/entity/Customer.hbm.xml"/>
<mapping resource="cn/entity/LinkMan.hbm.xml"/>
一对多级联操作 级联:cascade
级联保存:
添加了一个客户,为这个客户又添加了多个联系人,需要操作两张表
级联删除:
删除了一个客户,客户里的所有联系人也要删除
直接删除时无法直接删除,它有外键的关联(需要先删完联系人,或把联系人的外键设置为Null)
Hibernate进行了封装,可以方便的进行级联操作
级联保存两种写法(一种麻烦,一种简化)
第一种(底层写法)
//一对多的级联保存
@Test
public void testAdd(){
//请将声明放在try之外
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try{
//得到工厂
sessionFactory = HibernateUtils.getSessionFactory();
//得到session
session = sessionFactory.openSession();
//开启事务
tx = session.beginTransaction(); //级联保存操作(较麻烦的方法)
/*创建客户对象*/
Customer cust = new Customer();
cust.setCustName("传智播客");
cust.setCustLevel("VIP8");
cust.setCustSource("百度");
cust.setCustMobile("10086");
cust.setCustPhone("911");
/*创建联系人对象*/
LinkMan lkm = new LinkMan();
lkm.setLkm_name("黑马");
lkm.setLkm_gender("男");
lkm.setLkm_phone("10010"); //建立级联关系
/*联系人放客户set集合里面*/
cust.getSet_LinkMan().add(lkm);
/*客户放联系人里面*/
lkm.setCustomer(cust); //保存到数据库中
session.save(cust);
session.save(lkm); //提交事务
tx.commit(); }catch(Exception e){
e.printStackTrace();
//事务回滚
tx.rollback();
}finally{
//关闭资源
session.close();//使用openSession()需要手动关闭
sessionFactory.close();
}
}
第二种,一般都是根据客户加联系人
在客户映射文件里set标签上进行配置:
<set name="set_LinkMan" cascade="save-update">
设置级联保存,cascade就是级联的意思
在代码中创建两个对象后,把联系人放到客户里面就可以
底层实现就是复杂写法,配置帮助简化
//级联的简化写法,开发常用
@Test
public void testAdd2(){
//请将声明放在try之外
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try{
//得到工厂
sessionFactory = HibernateUtils.getSessionFactory();
//得到session
session = sessionFactory.openSession();
//开启事务
tx = session.beginTransaction(); //级联保存操作(较麻烦的方法)
/*创建客户对象*/
Customer cust = new Customer();
cust.setCustName("百度");
cust.setCustLevel("VIP6");
cust.setCustSource("搜狗");
cust.setCustMobile("10000");
cust.setCustPhone("911");
/*创建联系人对象*/
LinkMan lkm = new LinkMan();
lkm.setLkm_name("白马");
lkm.setLkm_gender("男");
lkm.setLkm_phone("10010"); //联系人放客户即可
cust.getSet_LinkMan().add(lkm);
//再保存客户即可
session.save(cust); //提交事务
tx.commit(); }catch(Exception e){
e.printStackTrace();
//事务回滚
tx.rollback();
}finally{
//关闭资源
session.close();//使用openSession()需要手动关闭
sessionFactory.close();
}
}
推荐的是第二种,第一种作为原理理解
级联删除写法:
(需要先删完联系人,或把联系人的外键设置为Null)
根据客户去删除联系人,在客户的映射文件中进行配置
也是set标签上配置
<set name="set_LinkMan" cascade="save-update,delete">
/**
* 级联删除的操作
*/
@Test
public void testDelete(){
//请将声明放在try之外
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try{
//得到工厂
sessionFactory = HibernateUtils.getSessionFactory();
//得到session
session = sessionFactory.openSession();
//开启事务
tx = session.beginTransaction(); //先查后删
Customer cust = session.get(Customer.class, 2);
session.delete(cust); //提交事务
tx.commit(); }catch(Exception e){
e.printStackTrace();
//事务回滚
tx.rollback();
}finally{
//关闭资源
session.close();//使用openSession()需要手动关闭
sessionFactory.close();
}
}
一对多的修改操作:
先查后改:通过公司增加员工与设置员工的所属公司来操作
但是,Hibernate中有双向维护外键,所以会修改两次外键,这是影响性能的
解决方案是让其中的一方放弃维护外键(让一对多中的一这一方放弃维护,这里就是客户这一方放弃维护)
所有人都认识习主席,而习主席不一定认识所有人
具体操作是通过配置解决 inverse true表示放弃维护,false表示放弃(也就是说inverse表示是否放弃维护)
<set name="set_LinkMan" cascade="save-update,delete" inverse="true">
/**
* 级联修改的操作
*/
@Test
public void testUpdate(){
//请将声明放在try之外
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try{
//得到工厂
sessionFactory = HibernateUtils.getSessionFactory();
//得到session
session = sessionFactory.openSession();
//开启事务
tx = session.beginTransaction();
/*先查后改*/
Customer cust = session.get(Customer.class, 2);
LinkMan lkm = session.get(LinkMan.class, 1);
cust.getSet_LinkMan().add(lkm);
lkm.setCustomer(cust);
//提交事务
tx.commit(); }catch(Exception e){
e.printStackTrace();
//事务回滚
tx.rollback();
}finally{
//关闭资源
session.close();//使用openSession()需要手动关闭
sessionFactory.close();
}
}
接下来是多对多的操作,有了上面的基础,接下来的就简单一些了
多对多的配置:
一般使用的是多对多的级联保存,而比较少用级联删除
配置过程和很像
一、创建实体类:用户和角色
二、两个实体类互相表示
都是使用Set互相表示
三、配置映射关系
包括基本的映射配置和关系映射
四、在核心配置文件中引入映射文件
完成后测试:
运行一下工具类,表出来即正常
多对多级联保存:
和一对多很相似
第一步是配置级联
第二步是代码实现
配置用户的级联保存:<set name="set_LinkMan" cascade="save-update">
把角色放到用户里,保存用户即可完成
步骤都是创建用户、角色对象,再将角色放入用户,保存用户,详细代码和一对多极其相似,略
用户:
package cn.entity02; import java.util.HashSet;
import java.util.Set; public class User { private Integer user_id;//用户ID
private String user_name;//用户名
private String user_password;//密码
//一个用户可以有多个角色
private Set<Role> set_Role = new HashSet<Role>(); public Set<Role> getSet_Role() {
return set_Role;
} public void setSet_Role(Set<Role> set_Role) {
this.set_Role = set_Role;
} public User() { } public Integer getUser_id() {
return user_id;
} public void setUser_id(Integer user_id) {
this.user_id = user_id;
} public String getUser_name() {
return user_name;
} public void setUser_name(String user_name) {
this.user_name = user_name;
} public String getUser_password() {
return user_password;
} public void setUser_password(String user_password) {
this.user_password = user_password;
} }
用户的映射配置文件:
<?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>
<!-- 映射的类 name是类的全路径 table是表名 -->
<class name="cn.entity02.User" table="t_user03">
<id name="user_id" column="user_id">
<!-- 设置id增长策略 -->
<generator class="native"></generator>
</id>
<!-- 配置其它属性 -->
<property name="user_name" column="user_name"></property>
<property name="user_password" column="user_password"></property>
<!-- 多对多的配置
name属性是角色色集合的名称
table是关系表的名字
-->
<set name="set_Role" table="user_role" cascade="save-update">
<!-- 当前表在第三张表中外键的名字 -->
<key column="user_id"></key>
<!-- class为角色的全路径,column为角色在第三张表中的外键 -->
<many-to-many class="cn.entity02.Role" column="role_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
角色:
package cn.entity02; import java.util.HashSet;
import java.util.Set; public class Role { private Integer role_id;//角色ID
private String role_name;//角色名
private String role_memo;//角色描述
//一个角色可以有多个用户
private Set<User> set_User = new HashSet<User>(); public Set<User> getSet_User() {
return set_User;
}
public void setSet_User(Set<User> set_User) {
this.set_User = set_User;
}
public Role() { }
public Integer getRole_id() {
return role_id;
}
public void setRole_id(Integer role_id) {
this.role_id = role_id;
}
public String getRole_name() {
return role_name;
}
public void setRole_name(String role_name) {
this.role_name = role_name;
}
public String getRole_memo() {
return role_memo;
}
public void setRole_memo(String role_memo) {
this.role_memo = role_memo;
} }
角色的映射文件:
<?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>
<!-- 映射的类 name是类的全路径 table是表名 -->
<class name="cn.entity02.Role" table="t_role">
<!-- 当前表在第三张表中外键的名字 -->
<id name="role_id" column="role_id">
<!-- 设置id增长策略 -->
<generator class="native"></generator>
</id>
<!-- 配置其它属性 -->
<property name="role_name" column="role_name"></property>
<property name="role_memo" column="role_memo"></property>
<!-- 配置和User同理 ,都是主要配置外键信息-->
<set name="set_User" table="user_role">
<key column="role_id"></key>
<many-to-many class="cn.entity02.Role" column="user_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
这里配置完一定记得去核心配置文件引入资源!
级联删除也是先配置后删除,但是用的较少,而且代码与一对多极其相似
就注意一点,删除是需要传入对象的,故一定是先查后删
多对多时注意维护第三张表:
一般思路都是通过维护第三张表来维护关系
例如让某个用户有某个角色
根据id查询到用户和角色
把角色放到用户里(getSet().add())
移除则是(getSet.remove())
这里还是得记得:持久态会更新到数据库
Hibernate第三天——表间关系与级联操作的更多相关文章
- 深入理解 Laravel Eloquent(三)——模型间关系(关联)
Eloquent是什么 Eloquent 是一个 ORM,全称为 Object Relational Mapping,翻译为 "对象关系映射"(如果只把它当成 Database A ...
- 自增特性,外键,级联更新与级联删除,表间关系,SELECT用法,GROUP BY
自增特性 自动增长的作用: 问题:为数据表设置主键约束后,每次插入记录时,如果插入的值已经存在,会插入失败. 如何解决:为主键生成自动增长的值. 自动增长的语法: 字段名 数据类型 AUTO_INCR ...
- EF简易教程,从建表到表间关系
唐大兵博客 唐大兵的博客里记录了EF Code First从建表到表之间关系的详细内容. 汪杰的博客(EF里一对一.一对多.多对多关系的配置和级联删除) 汪杰的博客更简洁,但不够充实,读懂了唐大兵博客 ...
- 【SSH三大框架】Hibernate基础第九篇:cascade关联关系的级联操作
这里要说的是Hibernate的关联关系的级联操作,使用cascade属性控制. 依旧用部门和员工举例.多个员工相应一个部门(多对一关联关系) 员工类:Employee.java package cn ...
- Rhythmk 学习 Hibernate 05 - Hibernate 表间关系 [ManyToOne,OneToMany]
1.项目结构: 1.1.场景说明: 一个订单,包含多个产品 1.2.类文件: Order.java package com.rhythmk.model; import java.util.Date; ...
- Rhythmk 学习 Hibernate 06 - Hibernate 表间关系 [One To One]
1.One To One 单相 背景: 古代一个老婆 只能关联一个老公 husband.java package com.rhythmk.model; public class husband { ...
- Node.js ORM框架Sequlize之表间关系
Sequelize模型之间存在关联关系,这些关系代表了数据库中对应表之间的主/外键关系.基于模型关系可以实现关联表之间的连接查询.更新.删除等操作.本文将通过一个示例,介绍模型的定义,创建模型关联关系 ...
- MicroERP开发技术分享:vsFlexGrid、scriptControl实现工资表自定义列与表间关系计算
开发大型的MIS系统,肯定是离不开第三方控件的,同时也要根据项目需要自己写几个. MicroERP共用了以下几个控件: 第三方商业控件: vsFlexGrid:大名鼎鼎的表格控件,不用多说,配合vsP ...
- hibernate 一对多单向注解配置(实现级联操作 )
学生表关联学生的课程表(一对多) 学生表实体类: @Entity @Table(name = "JXD_COM_STUDENT") public class StudentMode ...
随机推荐
- [转载]Cool, Tomcat is able to handle more than 13,000 concurrent connections
Last time I have promised you to take a look at more real life scenario regarding threads. In the la ...
- 机器学习vs深度学习及其知识点
人工智能如火如荼,可以遇见这将会是近10年最大的创新机会.那么到底什么是人工智能? 机器学习和神经网络什么关系? 卷积神经网络中的矩阵内积是怎么计算的?
- 执行相应操作后,将表单及table中数据清空
使用 ccms.util.clearForm('要清空的表单的id'); $("#table中显示数据部分的id").empty();
- MySQL Flashback 工具介绍
MySQL Flashback 工具介绍 DML Flashback 独立工具,通过伪装成slave拉取binlog来进行处理 MyFlash 「大众点点评」 binlog2sql 「大众点评(上海) ...
- 2. 跟踪标记 (Trace Flag) 3604, 3605 输出DBCC命令结果
跟踪标记:3604 功能: 输出DBCC命令返回结果到查询窗口(通常是SSMS窗口),类似print命令的显示效果: 用途: 常用于获取DBCC IND, DBCC PAGE命令的输出结果,因为这2个 ...
- [swift] Async
Async https://github.com/duemunk/Async Syntactic sugar in Swift for asynchronous dispatches in Grand ...
- Linux 文件的详解[分类/扩展名/inode/block]
关于Linux文件的介绍 Linux里文件扩展名和文件类型没有关系,Linux系统中一切皆文件 关于Linux文件分类 纯文本文件(可以cat的) 二进制文件(Linux的可执行文件等,如/b ...
- vmware查看HBA卡、网卡驱动、firmware版本信息
在 ESXi 5.x 中,swfw.sh 命令随 vm-support 支持包收集工具一起提供.swfw.sh 命令可用来识别连接到主机的硬件的固件和驱动程序版本.要运行此命令,请使用该路径: # / ...
- 内置数据结构(list)
列表.元组.字符串.字典和集合是python内置的数据结构,也可以叫内置容器.前3个是线性结构,线性结构可以切片操作.解包和封包操作. dir()方法可以查看对象拥有哪些属性和方法. help()方法 ...
- November 19th 2016 Week 47th Saturday
Nature didn't need an operation to be beautiful. It just was. 自然之美无需刻意而为,其本身即为美. Recently I saw seve ...