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 ...
随机推荐
- leetCode题解之求二叉树每层的平均值
1.题目描述 Given a non-empty binary tree, return the average value of the nodes on each level in the for ...
- [翻译] SCViewShaker
SCViewShaker https://github.com/rFlex/SCViewShaker About A highly configurable UIView category for s ...
- spider-抓取页面内容
# -*- coding: UTF-8 -*- from HTMLParser import HTMLParser import sys,urllib2,string,re,json reload(s ...
- java.lang.verifyerror:bad type on orerand stack
问题: junit测试的时候报这个错:java.lang.verifyerror:bad type on orerand stack 原因:(多种,自行逐个排查) 1.class not find 引 ...
- Go语言-windows安装配置篇
Go-windows安装配置 前言 学习完了python基础,顺便也要提前学习一下go啦,抱着这样的心情,今晚尝试了安装一下go,很顺利的完成了,没有难度. 需要了解更多的关于Go的基本信息可以去维基 ...
- Nginx-基础配置
正文 本文转载自:http://www.ha97.com/5194.html 文章经过我排版和润色再加工,更加易读.实在是了解nignx配置的基础好文章. 正文 定义Nginx运行的用户和用户组 us ...
- Elasticsearch 填坑记
前言 技术的发展日新月异,传统企业数据库Oracle.SqlServer.DB2,Mysql等在今日不断的被各种大厂自研数据库取代,当然也有类似Elasticsearch等优秀的满足海量数据所使用的开 ...
- Day12 Java异常处理与程序调试
什么是异常? 不正常的,会影响程序的正常执行流程. 例如下面的程序 public static void main(String[] args) { TestDemo1 t = new TestDem ...
- tornado 路由、模板语言、session
一:tornado路由系统: 1.面向资源编程: 场景:当我们给别人提供api的时候,往往提供url.比如:电影票api: http://movie.jd.com/book_ticket:预订电影票. ...
- MySQL(五)SELECT语句执行顺序
上一篇讲述了Oracle的SELECT语法的执行顺序,这篇讲述MySQL的SELECT语法的执行顺序.MySQL的SELECT语法的执行顺序和Oracle的基本相同,只是增加了MySQL独有的LIMI ...