Hibernate的映射关系与级联(一对一、一对多、多对多)
来源:互联网 发布:c语言成绩查询系统湖北 编辑:程序博客网 时间:2024/05/22 00:21
Hibernate的映射关系与级联(一对一、一对多、多对多)
一、概述
长期专业踩坑……怪物猎人要登陆switch了
1、外键注意:
- 被引用的列必须是其所在表的主键或者唯一列(此处的department表的dept_name)
- 引用列 和 被引用列应该数据类型一致,并且最好长度一致
- 如果存在数据,那么引用列中 不能 存在 被引用列中没有的数据
2、配置注意:
- 一的那边配置了,多的那边不配置,叫单向一对多
- 一的那边配置了,多的那边也配置,叫双向一对多
- 一的那边不配置,多的那边配置了,报错
3、字段重复注意:
- 因为是一给多加字段,所以在多的一边加的字段不要重复,会报错
4、级联删除注意:
- 在数据库中直接删除级联的表,要先删除子表,再删除主表,一开始就删除主表是不行的,因为他被子表给引用着
5、级联插入注意
- 级联之后,从表插不进去,为什么?
- 增加了父亲的话,可以在这个父亲的名下增加相应的孩子
- 但是你随便增加孩子却不行,孩子必须有父亲,没有父亲的孩子哪里来的?石头蹦?孙悟空?
- 这就是级联,都是连在一起的,有父亲才有孩子
6、测试的时候不要被之前的表数据给阻碍,所以每次测试前请删除相关的表
如果删除表的时候数据库工具一直在转圈,像死机了一样关闭不行,又不动,操作不了,其实是因为你的Eclipse还在开着刚才运行的代码,每一个都是独立的线程,数据库工具会因为等待其关闭而处于阻塞状态,在Eclipse的Console界面可以看到右上角有红点,表示在运行中,把所有的红点都关掉就可以了
//删除DROP TABLE IF EXISTS `toy_son`;DROP TABLE IF EXISTS `son`;DROP TABLE IF EXISTS `toy`;DROP TABLE IF EXISTS `father`;DROP TABLE IF EXISTS `mother`;DROP TABLE IF EXISTS `father`;//查询select * from father;select * from mother;select * from son;select * from toy;select * from toy_son;
7、其实cascade不是必须的,你使用了cascade就可以只保存主表,从表会跟着保存,你不设置cascade,就需要自己手动保存从表
8、我在下面要用的表的表关系
- 一个父亲有多个孩子
- 父亲(Father)
- id(f_id,主键)
- 姓名(f_name)
- 儿子(Son)
- id(s_id,主键)
- 姓名(s_name)
- 父亲(Father)
- 一个父亲有一个妻子
- 父亲(Father)
- id(f_id,主键)
- 姓名(f_name)
- 母亲(Mother)
- id(m_id,主键)
- 姓名(m_name)
- 父亲(Father)
- 多个孩子有多个玩具
- 玩具(Toy)
- id(t_id,主键)
- 姓名(t_name)
- 儿子(Son)
- id(s_id,主键)
- 姓名(s_name)
- 玩具(Toy)
9、先来个总结
- 1、主键一对多
- 以一的那方主键作为来个表之间的桥梁,所以是将一那方的字段插到多那方
- 2、非主键一对多
- 以一的那方的某个字段作为来个表之间的桥梁,所以是将一那方的字段插到多那方
- 3、主键多对多
- 两个表都给出主键,插到一个第三方表,这样产生关系,在第三方表就可以通过我的主键查到与我相关的你的主键,再通过你的主键去查你表里的数据
- 4、非主键多对多
- 两个表都给出某一个字段,插到一个第三方表,这样产生关系,在第三方表就可以通过我的字段查到与我相关的你的字段,再通过你的字段去查你表里的数据
- 5、主键一对一
- 不需要在对方的表里插字段,因为是一一对应的,所以我的主键的值就是你主键的值
- 6、非主键一对一
- 你的某个字段等于我现有的某个字段,一一对应
- 7、双向
- 你的表里有与我相关的字段可以查到我,我的表里也有你的字段可以查到你对应的数据
- 8、单向
- 你的表里没有与我相关的字段,在你的表里查不到我,但是我的表里有你的字段,可以查到你对应的数据
二、示例
- 1、主键一对多示例
- 2、非主键一对多示例
- 3、主键多对多示例
- 4、非主键多对多示例
- 5、主键一对一示例
- 6、非主键一对一示例
- 7、双向示例
- 8、单向示例
下面的xml示例都会省略头部声明,完整的xx.hbm.xml参考如下:
<?xml version="1.0"?><!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.iamzhuwh.more2more"> <class name="Father" table="FATHER"> <id name="f_id" column="F_ID"> <generator class="native"/> </id> <property name="f_name" type="string" column="F_NAME"/> </class></hibernate-mapping>
1、主键一对多示例
儿子与父亲的联系在于父亲的f_id,因为f_id是唯一可以识别父亲的标识,所以将父亲的主键f_id作为外键给儿子,以后凭f_id就可以找到父亲对应的儿子啦
/** 父亲 */public class Father { /** 父亲的id,主键 */ private int f_id; /** 父亲的名字 */ private String f_name; /** 可能有多个儿子,所以是集合,对应配置文件的<set one-to-many>*/ private Set son = new HashSet();}/** 儿子 */public class Son { /** 儿子的id,主键 */ private int s_id; /** 儿子的名字 */ private String s_name; /** 只有一个父亲,对应配置文件的<many-to-one>*/ private Father father;}/** 父亲配置文件,Father.hbm.xml */<hibernate-mapping package="com.iamzhuwh.one2more"> <!-- 对应的实体类:com.iamzhuwh.one2more.Father 定义class标签, <class name="Father" table="FATHER"> name是实体类名字, table是你要创建的表名 包名:package="com.iamzhuwh.one2more" --> <class name="Father" table="FATHER"> <!-- id是主键,自增 --> <id name="f_id" column="F_ID"> <!-- 主键配置 --> <generator class="native"/> </id> <!-- 其他字段 --> <property name="f_name" type="string" column="F_NAME"/> <set name="son" inverse="false" cascade="all" > <!-- 一的一方的外键column="f_id" 儿子与父亲的联系在于父亲的f_id,因为主键f_id是唯一可以识别父亲的标识 --> <key column="f_id"></key> <one-to-many class="com.iamzhuwh.one2more.Son"/> </set> </class></hibernate-mapping><!-- 儿子配置文件,Son.hbm.xml --><hibernate-mapping package="com.iamzhuwh.one2more"> <!-- 对应的实体类,name是实体类名字,table是你要创建的表名 --> <class name="Son" table="SON"> <!-- id是主键,自增 --> <id name="s_id" column="S_ID"> <!-- 主键配置 --> <generator class="native"/> </id> <!-- 其他字段 --> <property name="s_name" type="string" column="S_NAME"/> <property name="s_f_id" type="int" column="S_F_ID"/> <!-- 一的一方的外键column="f_id" 儿子与父亲的联系在于父亲的f_id,因为主键f_id是唯一可以识别父亲的标识 --> <many-to-one name="father" column="f_id" class="com.iamzhuwh.one2more.Father"> </many-to-one> </class></hibernate-mapping>/** 测试代码 */public class Test { @SuppressWarnings("unchecked") public static void main(String[] args) { /** 父亲 */ Father father = new Father(); father.setF_name("f1"); /** 儿子 */ Son son = new Son(); son.setS_name("s1"); /** 级联 增加了父亲的话,可以在这个父亲的名下增加相应的孩子 但是你随便增加孩子却不行,孩子必须有父亲,没有父亲的孩子哪里来的?石头蹦?孙悟空? 这就是级联,都是连在一起的 */ father.getSon().add(son); /** 获取连接 */ SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); /** 因为是连在一起的,所以保存了父亲,父亲名下的孩子也会被保存 */ System.out.println("Father:"+session.save(father)); /** 提交事务 */ session.getTransaction().commit(); }
2、非主键一对多示例
以上面的代码为基础,上面提到了父亲的主键f_id作为外键给儿子,以后凭f_id就可以找到父亲对应的儿子,那么如果父亲给的外键不是主键呢?比如f_name,父亲的名字
- 使用非主键作为外键,这个外键必须是唯一不可重复的,所以要设置unique=”true”
- 在一的那边使用属性property-ref,即是
/** 父亲配置 */<hibernate-mapping package="com.iamzhuwh.one2more"> <!-- 对应的实体类,name是实体类名字,table是你要创建的表名 --> <class name="Father" table="FATHER"> <!-- id是主键,自增 --> <id name="f_id" column="F_ID"> <!-- 主键配置 --> <generator class="native"/> </id> <!-- 这里变一下 unique="true" --> <property name="f_name" type="string" column="F_NAME" unique="true"/> <set name="son" inverse="false" cascade="all" > <!-- **** 重要 **** 这里变一下 property-ref="f_name" 用父亲表的父亲名字f_name作为外键,赋予给儿子 key column="s_f_name",这句话只是给加在儿子表的那个字段起个名字叫s_f_name而已 使用property-ref="f_name"才是真正将f_name的值赋予给s_f_name --> <key column="s_f_name" property-ref="f_name"></key> <one-to-many class="com.iamzhuwh.one2more.Son"/> </set> </class></hibernate-mapping><!-- 儿子配置 --><hibernate-mapping package="com.iamzhuwh.one2more"> <!-- 对应的实体类,name是实体类名字,table是你要创建的表名 --> <class name="Son" table="SON"> <!-- id是主键,自增 --> <id name="s_id" column="S_ID"> <!-- 主键配置 --> <generator class="native"/> </id> <!-- 其他字段 --> <property name="s_name" type="string" column="S_NAME"/> <property name="s_f_id" type="int" column="S_F_ID"/> <!-- 这里变一下,column="s_f_name" --> <many-to-one name="father" column="s_f_name" class="com.iamzhuwh.one2more.Father"> </many-to-one> </class></hibernate-mapping>
3、主键多对多示例
- 多对多的时候,要记住,不是简单给个字段对方而已,要建一个中间表,将双方的外键放上去
/** 儿子表的实体 */public class Son { /** 儿子的id,主键 */ private int s_id; /** 儿子的名字 */ private String s_name; /** 儿子的玩具,一个儿子可以拥有多个玩具 */ private Set toys = new HashSet();}/** 玩具表的实体 */public class Toy { /** 玩具的id,主键 */ private int t_id; /** 玩具的名字 */ private String t_name; /** 每个玩具可以被多个人拥有*/ private Set sons = new HashSet();}<!-- 儿子表的配置 --><hibernate-mapping package="com.iamzhuwh.more2more"> <!-- SON表 --> <class name="Son" table="SON"> <!-- 主键,自增 --> <id name="s_id" column="S_ID"> <generator class="native"/> </id> <!-- 其他字段 --> <property name="s_name" type="string" column="S_NAME"/> <!-- 儿子可以有多个玩具,所以将儿子的s_id作为外键,<key column="S_ID"></key> 玩具可以有多个主人,所以将玩具的t_id作为外键,<many-to-many column="T_ID" 如果让玩具表来处理级联的增删改,需要在玩具的Set标签中配置inverse与cascade **** 重点 **** 这个table填的是TOY_SON!!!<set name="toys" table="TOY_SON" 根据上面一对多的经验,我们是要外键给对方,所以这里填的应该是对方的表名才对吧? 但是这里为啥填的是TOY_SON?为啥不是TOY或者SON? 因为要新建一张第三方叫TOY_SON的表,来存放儿子与玩具的外键 <set name="toys",这里填的是你在Son.java中配置的Set集合的名字: private Set toys = new HashSet();/** 儿子的玩具,一个儿子可以拥有多个玩具*/ --> <set name="toys" table="TOY_SON" inverse="true" cascade="all"> <!-- column="S_ID",代表是Son给出的外键是S_ID --> <key column="S_ID"></key> <many-to-many column="T_ID" class="com.iamzhuwh.more2more.Toy"></many-to-many> <!-- column="T_ID",代表是com.iamzhuwh.more2more.Toy给出的外键是T_ID --> </set> </class></hibernate-mapping><!-- 玩具表的配置 --><hibernate-mapping package="com.iamzhuwh.more2more"> <!-- TOY表 --> <class name="Toy" table="TOY"> <!-- id是主键,自增 --> <id name="t_id" column="T_ID"> <!-- 主键配置 --> <generator class="native"/> </id> <!-- 其他字段 --> <property name="t_name" type="string" column="T_NAME"/> <!-- 玩具可以有多个主人,所以将玩具的t_id作为外键,<key column="T_ID"></key> 儿子可以有多个玩具,所以将儿子的s_id作为外键,<many-to-many column="S_ID" **** 注意 **** 如果让玩具表来处理级联的增删改,需要在玩具的Set标签中配置inverse与cascade 但是上面已经让儿子来处理了,所以在玩具这里不用配置inverse与cascade **** 重点 **** 这个table填的是TOY_SON!!!<set name="sons" table="TOY_SON"> 根据上面一对多的经验,我们是要外键给对方,所以这里填的应该是对方的表名才对吧? 但是这里为啥填的是TOY_SON?为啥不是TOY或者SON? 因为要新建一张第三方叫TOY_SON的表,来存放儿子与玩具的外键 <set name="sons",这里填的是你在Toy.java中配置的Set集合的名字: private Set sons = new HashSet();/** 每个玩具可以被多个人拥有*/ --> <set name="sons" table="TOY_SON"> <!-- column="T_ID",代表Toy给出的外键是T_ID --> <key column="T_ID"></key> <many-to-many column="S_ID" class="com.iamzhuwh.more2more.Son"></many-to-many> <!-- column="S_ID",代表是Son给出的外键是S_ID --> </set> </class></hibernate-mapping>/** 测试代码 */public class Test { @SuppressWarnings("unchecked") public static void main(String[] args) { /** 玩具 */ Toy toy = new Toy(); toy.setT_name("t1"); /** 儿子 */ Son son = new Son(); son.setS_name("s1"); /** 级联 因为在Son配置表中配置了<set name="toys" table="TOY_SON" inverse="true" cascade="all"> 所以级联让Toy来处理,增加了Toy的话,可以把玩具赋给相应的孩子 */ toy.getSons().add(son); son.getToys().add(toy); /** 获取连接 */ SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); /** 因为是连在一起的,所以保存了儿子,儿子名下的玩具也会被保存 */ System.out.println("Son:"+session.save(son)); /** 提交事务 */ session.getTransaction().commit(); }}
4、非主键多对多示例
上面是主键多对多示例,所以使用主键id作为外键,这次是非主键作为外键,所以我们使用名字name作为外键
<hibernate-mapping package="com.iamzhuwh.more2more"> <class name="Son" table="SON"> <id name="s_id" column="S_ID"> <generator class="native"/> </id> <!-- 这次要用这个s_name作为外键,所以要加一个unique="true" 让其变为唯一可以代表这个儿子的标识 --> <property name="s_name" type="string" column="S_NAME" unique="true"/> <!-- <key column="S_NAME" property-ref="s_name"></key> 解析: column="S_NAME": 在TOY_SON表中创建一个字段名字叫S_NAME property-ref="s_name": 这个字段的类型或者值请参考Son配置表的property name="s_name" ———————————————————————————————————————————————————— <many-to-many column="T_NAME" property-ref="t_name" 解析: column="T_NAME": 在TOY_SON表中创建一个字段名字叫T_NAME property-ref="t_name": 这个字段的类型或者值请参考Toy配置表的property name="t_name" --> <set name="toys" table="TOY_SON" inverse="true" cascade="all" > <key column="S_NAME" property-ref="s_name"></key> <many-to-many column="T_NAME" property-ref="t_name" class="com.iamzhuwh.more2more.Toy"></many-to-many> </set> </class></hibernate-mapping><hibernate-mapping package="com.iamzhuwh.more2more"> <class name="Toy" table="TOY"> <id name="t_id" column="T_ID"> <generator class="native"/> </id> <!-- 这次要用这个t_name作为外键,所以要加一个unique="true" 让其变为唯一可以代表这个玩具的标识 --> <property name="t_name" type="string" column="T_NAME" unique="true"/> <!-- <key column="T_NAME" property-ref="t_name"></key> 解析: column="T_NAME": 在TOY_SON表中创建一个字段名字叫T_NAME property-ref="t_name": 这个字段的类型或者值请参考Toy配置表的property name="t_name" ———————————————————————————————————————————————————— <many-to-many column="S_NAME" property-ref="s_name" 解析: column="S_NAME": 在TOY_SON表中创建一个字段名字叫S_NAME property-ref="s_name": 这个字段的类型或者值请参考Son配置表的property name="s_name" --> <set name="sons" table="TOY_SON"> <key column="T_NAME" property-ref="t_name"></key> <many-to-many column="S_NAME" property-ref="s_name" class="com.iamzhuwh.more2more.Son"></many-to-many> </set> </class></hibernate-mapping>
5、主键一对一示例
这个一对一,你查看数据库表,是没有多出字段的,为什么?因为是一一对应的,比如我用我的id就可以找到你的id,因为是一一对应,所以不需要在你的表里插一个我的id
对比一堆多,一的那边会在多的那边插入一个字段,比如id,来表示两者之间存在关系,通过这个id我就可以查找到跟我关联的你的表的数据
使用one to one 的方式展示主键一对一示例
/** 孩子他爸 */public class Father { /** 丈夫的id,主键 */ private int f_id; /** 丈夫的名字 */ private String f_name; /** 只有一个妻子*/ private Mother mother;}/** 孩子他妈 */public class Mother { /** 妻子的id,主键 */ private int m_id; /** 妻子的名字 */ private String m_name; /** 只有一个丈夫 */ private Father father;}<!-- 孩子他爸的配置 --><hibernate-mapping package="com.iamzhuwh.one2one"> <!-- 对应的实体类,name是实体类名字,table是你要创建的表名 --> <class name="Father" table="FATHER"> <!-- id是主键,自增 --> <id name="f_id" column="F_ID"> <!-- 主键配置 --> <generator class="native"/> </id> <!-- 其他字段 --> <property name="f_name" type="string" column="F_NAME"/> <!-- 父母是一对一的,一个丈夫对应一个妻子,所以在丈夫中引入妻子 <one-to-one name="mother" 这里就是配置了妻子的信息 name="mother"代表在Father.java中创建了一个叫mother的变量 参考Father.java: /** 只有一个妻子*/ private Mother mother; property-ref="m_id"代表引用Mother中的m_id作为外键 参考Mother.hbm.xml: <id name="m_id" column="M_ID"> --> <one-to-one name="mother" property-ref="m_id" cascade="all" class="com.iamzhuwh.one2one.Mother"></one-to-one> </class></hibernate-mapping><!-- 孩子他妈的配置 --><hibernate-mapping package="com.iamzhuwh.one2one"> <!-- 对应的实体类,name是实体类名字,table是你要创建的表名 --> <class name="Mother" table="MOTHER"> <!-- id是主键,自增 --> <id name="m_id" column="M_ID"> <!-- 主键配置 --> <generator class="native"/> </id> <!-- 其他字段 --> <property name="m_name" type="string" column="M_NAME"/> <!-- 父母是一对一的,一个丈夫对应一个妻子,所以在妻子中引入丈夫 <one-to-one name="father" 这里就是配置了丈夫的信息 name="father"代表在Mother.java中创建了一个叫father的变量 参考Mother.java: /** 只有一个丈夫 */ private Father father; property-ref="f_id"代表引用Father中的f_id作为外键 参考Father.hbm.xml: <id name="f_id" column="F_ID"> --> <one-to-one name="father" property-ref="f_id" class="com.iamzhuwh.one2one.Father"></one-to-one> </class></hibernate-mapping>/** 测试代码 */public class Test { @SuppressWarnings("unchecked") public static void main(String[] args) { /** 孩子爸 */ Father father = new Father(); father.setF_name("f1"); /** 孩子妈 */ Mother mother = new Mother(); mother.setM_name("m1"); /** 级联 增加丈夫,也可以增加妻子 */ father.setMother(mother); mother.setFather(father); /** 获取连接 */ SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); /** 因为在孩子他爸的配置表里面配置了cascade="all", 所以这里只需要保存孩子爸,孩子妈就会因为级联而被保存 如果没有cascade,则孩子妈需要手动保存 */ System.out.println("Father:"+session.save(father)); /** 提交事务 */ session.getTransaction().commit(); }}
使用many to one 的方式展示主键一对一示例
还是在上面的基础上改,改父亲配置表里面的孩子妈配置,将one to one改为more to one,给more to one的字段配置唯一约束unique,它的效果就相当于变成one to one了
<!-- 修改孩子妈配置表 --><many-to-one name="father" column="F_ID" class="com.iamzhuwh.one2one.Father" unique="true"></many-to-one><!-- 孩子爸配置表还是不变 --> <one-to-one name="mother" cascade="all" class="com.iamzhuwh.one2one.Mother"></one-to-one>
6、非主键一对一示例
按上面的经验,在使用one to one的基础上,修改为非主键一对一,以名字作为外键,设置名字唯一的约束unique,其他不变
使用one to one 的方式展示非主键一对一示例
<!-- 孩子爸配置表 --><property name="f_name" type="string" column="F_NAME" unique="true"/><!--引用孩子妈的名字作为外键, property-ref="m_name" --><one-to-one name="mother" property-ref="m_name" cascade="all" class="com.iamzhuwh.one2one.Mother"></one-to-one><!-- 孩子妈配置表 --> <property name="m_name" type="string" column="M_NAME" unique="true"/><!--引用孩子爸的名字作为外键, property-ref="f_name" --><one-to-one name="father" property-ref="f_name" class="com.iamzhuwh.one2one.Father"></one-to-one>
使用many to one 的方式展示非主键一对一示例
<!-- 在上面的进程上修改孩子妈配置表 <one-to-one变<many-to-one --> <many-to-one name="father" property-ref="f_name" class="com.iamzhuwh.one2one.Father" unique="true"></many-to-one>
7、双向示例
这个是一对多的双向示例,双向就是说通过我可以查到你,通过你可以查到我
/** 父亲 */public class Father { /** 父亲的id,主键 */ private int f_id; /** 父亲的名字 */ private String f_name; /** 可能有多个儿子,所以是集合,对应配置文件的<set one-to-many>*/ private Set son = new HashSet();}/** 儿子 */public class Son { /** 儿子的id,主键 */ private int s_id; /** 儿子的名字 */ private String s_name; /** 只有一个父亲,对应配置文件的<many-to-one>*/ private Father father;}/** 父亲配置文件,Father.hbm.xml */<hibernate-mapping package="com.iamzhuwh.one2more"> <!-- 对应的实体类:com.iamzhuwh.one2more.Father 定义class标签, <class name="Father" table="FATHER"> name是实体类名字, table是你要创建的表名 包名:package="com.iamzhuwh.one2more" --> <class name="Father" table="FATHER"> <!-- id是主键,自增 --> <id name="f_id" column="F_ID"> <!-- 主键配置 --> <generator class="native"/> </id> <!-- 其他字段 --> <property name="f_name" type="string" column="F_NAME"/> <set name="son" inverse="false" cascade="all" > <!-- 一的一方的外键column="f_id" 儿子与父亲的联系在于父亲的f_id,因为主键f_id是唯一可以识别父亲的标识 --> <key column="f_id"></key> <one-to-many class="com.iamzhuwh.one2more.Son"/> </set> </class></hibernate-mapping><!-- 儿子配置文件,Son.hbm.xml --><hibernate-mapping package="com.iamzhuwh.one2more"> <!-- 对应的实体类,name是实体类名字,table是你要创建的表名 --> <class name="Son" table="SON"> <!-- id是主键,自增 --> <id name="s_id" column="S_ID"> <!-- 主键配置 --> <generator class="native"/> </id> <!-- 其他字段 --> <property name="s_name" type="string" column="S_NAME"/> <property name="s_f_id" type="int" column="S_F_ID"/> <!-- 一的一方的外键column="f_id" 儿子与父亲的联系在于父亲的f_id,因为主键f_id是唯一可以识别父亲的标识 --> <many-to-one name="father" column="f_id" class="com.iamzhuwh.one2more.Father"> </many-to-one> </class></hibernate-mapping>/** 测试代码 */public class Test { @SuppressWarnings("unchecked") public static void main(String[] args) { /** 父亲 */ Father father = new Father(); father.setF_name("f1"); /** 儿子 */ Son son = new Son(); son.setS_name("s1"); /** 级联 增加了父亲的话,可以在这个父亲的名下增加相应的孩子 但是你随便增加孩子却不行,孩子必须有父亲,没有父亲的孩子哪里来的?石头蹦?孙悟空? 这就是级联,都是连在一起的 */ father.getSon().add(son); /** 获取连接 */ SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); /** 因为是连在一起的,所以保存了父亲,父亲名下的孩子也会被保存 */ System.out.println("Father:"+session.save(father)); /** 提交事务 */ session.getTransaction().commit(); }
8、单向示例
这个是一对多的单向示例,单向就是说通过我可以查到你,你却查不到我
/** 父亲 */public class Father { /** 父亲的id,主键 */ private int f_id; /** 父亲的名字 */ private String f_name; /** 可能有多个儿子,所以是集合,对应配置文件的<set one-to-many>*/ private Set son = new HashSet();}/** 儿子 */public class Son { /** 儿子的id,主键 */ private int s_id; /** 儿子的名字 */ private String s_name; /** 只有一个父亲,对应配置文件的<many-to-one>*/ private Father father;}/** 父亲配置文件,Father.hbm.xml */<hibernate-mapping package="com.iamzhuwh.one2more"> <!-- 对应的实体类:com.iamzhuwh.one2more.Father 定义class标签, <class name="Father" table="FATHER"> name是实体类名字, table是你要创建的表名 包名:package="com.iamzhuwh.one2more" --> <class name="Father" table="FATHER"> <!-- id是主键,自增 --> <id name="f_id" column="F_ID"> <!-- 主键配置 --> <generator class="native"/> </id> <!-- 其他字段 --> <property name="f_name" type="string" column="F_NAME"/> <set name="son" inverse="false" cascade="all" > <!-- 一的一方的外键column="f_id" 儿子与父亲的联系在于父亲的f_id,因为主键f_id是唯一可以识别父亲的标识 --> <key column="f_id"></key> <one-to-many class="com.iamzhuwh.one2more.Son"/> </set> </class></hibernate-mapping><!-- 儿子配置文件,Son.hbm.xml --><hibernate-mapping package="com.iamzhuwh.one2more"> <!-- 对应的实体类,name是实体类名字,table是你要创建的表名 --> <class name="Son" table="SON"> <!-- id是主键,自增 --> <id name="s_id" column="S_ID"> <!-- 主键配置 --> <generator class="native"/> </id> <!-- 其他字段 --> <property name="s_name" type="string" column="S_NAME"/> <!-- 一的一方的外键column="f_id" 儿子与父亲的联系在于父亲的f_id,因为主键f_id是唯一可以识别父亲的标识 因为现在是单向,所以直接把这里给去掉,这样在父亲表就找不到儿子的踪影了,但是在儿子表可以找到父亲的id <many-to-one name="father" column="f_id" class="com.iamzhuwh.one2more.Father"> </many-to-one> --> </class></hibernate-mapping>/** 测试代码 */public class Test { @SuppressWarnings("unchecked") public static void main(String[] args) { /** 父亲 */ Father father = new Father(); father.setF_name("f1"); /** 儿子 */ Son son = new Son(); son.setS_name("s1"); /** 级联 增加了父亲的话,可以在这个父亲的名下增加相应的孩子 但是你随便增加孩子却不行,孩子必须有父亲,没有父亲的孩子哪里来的?石头蹦?孙悟空? 这就是级联,都是连在一起的 */ father.getSon().add(son); /** 获取连接 */ SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); /** 因为是连在一起的,所以保存了父亲,父亲名下的孩子也会被保存 */ System.out.println("Father:"+session.save(father)); /** 提交事务 */ session.getTransaction().commit(); }
三、踩坑
问题1
- Foreign key (FK_9jbtfsoc50vbxnpaladvwbd50:SON [s_id])) must have same number of columns as the referenced primary key (SON [t_id,s_id])
- 添加property-ref即可,如:
<key column="S_ID" property-ref="s_id"></key>
问题2
Field ‘S_ID’ doesn’t have a default value 或者 Field ‘T_ID’ doesn’t have a default value
我的原因是跟inverse相关,也有人说是在数据库给字段加默认值,或者是主键不要native,但是我的主键本来就是非空且自增,不可能是默认值或者native的问题
问题3
- object references an unsaved transient instance - save the transient instance before flushing: com.iamzhuwh.more2more.Son
- son做级联,却保存toy,所以报错,应该是save(son),然后根据级联,son会将里面的toy拿出来保存
问题4
注意两个事情:(以下面代码为例)
- 首先这里定义了一个字段:
<property name="t_name" type="string" column="T_NAME" unique="true"/>
- name=”t_name”是用来在这个配置表中代表这个字段的
- column=”T_NAME”是用来代表在数据库中要展示的字段名
- 所以这里要这样引用:
<key column="T_NAME" property-ref="t_name"></key>
- 在数据库定义一个字段叫T_NAME
- 这个字段的值引用在本配置文件中配置的t_name的信息
<class name="Toy" table="TOY"> <id name="t_id" column="T_ID"> <generator class="native"/> </id> <property name="t_name" type="string" column="T_NAME" unique="true"/> <set name="sons" table="TOY_SON"> <key column="T_NAME" property-ref="t_name"></key> <many-to-many column="S_NAME" class="com.iamzhuwh.more2more.Son"></many-to-many> </set>
四、课外知识
1、为什么会有怎么多种关系?
一对多、多对一、单向、双向是针对不同的情况和业务需求已定的,其实本质是一样的,只是操作的方式不同
- 1、什么是单向与双向?
- 例如:领导叫我查用户与包裹的信息
- 单向一对多:
- 用户A对于多个包裹,领导叫我在用户表查找用户A的地址,我就查呗
- 其实我查询包裹上的信息也可以查到用户A的地址,但是没必要,因为领导说了,到用户表去查
- 又或者我没有查询包裹信息的权限,所以只有从用户表查
- 单向多对一:
- 多个包裹对于用户A,领导叫我在包裹信息表里面查用户A的地址,我就查呗
- 其实我查询用户表上的信息也可以查到用户A的地址,但是没必要,因为领导说了,到包裹信息表去查
- 又或者我没有查询用户表的权限,所以只有从包裹信息表查
- 双向一对多/双向多对一:
- 领导又来了,叫我查用户A的地址,不管我怎么查,我从用户表可以查到,从包裹信息表也可以查到
- 懂了么?
- 这就是针对不同的业务需求决定使用哪种映射关系,但是本质都是可以查到目标信息
- 单向一对多:
- 例如:领导叫我查用户与包裹的信息
- 2、什么是一对多与多对一?
- 例如:用户与包裹
- 用户A买了一堆的东西,那就是一个用户对应多个包裹,也就是一对多
- 一堆的包裹的收件人都是用户A,那就是多个包裹对应一个用户,也就是多对一
- 例如:用户与包裹
3、主键与外键
- a、什么是主键
- 解释:
- 一张表中的一条数据,可能会包含多个属性,比如姓名、性别
- 已知一张表有多条数据,用什么来区分每条数据?就是用主键了
- 不为空的,不重复的,能代表一条数据的,叫主键
- 什么东西可以作为主键?
- 一般是XX_id,从1开始自动递增,保证不重复
- 也可以是一组属性,比如姓名、年龄、性别加起来作为一个主键
- 例子:
- 例如一个人,身份证号码可以作为他的唯一标示,也就是主键
- 例如一台电视,品牌、型号、序列号加起来可以作为它的唯一标示,也就是主键
- 解释:
- b、什么是外键
- 解释:
- 在自己的表中,有一个字段,可以确定另一张表的数据的唯一性,叫外键
- 这个字段,对另一张表来说,叫主键,对自己的这张表来说,叫外键
- a表与b表,a表中的某一个字段可以区分b表的每一条数据,这个字段就是a表的外键,是b表的主键
- 例子:
- 比如一件快递包裹
- 在天猫商家的系统里,有这个包裹的购买人id、订单号、电话、快递单号等信息,主键是订单号,通过订单号区分每一个订单
- 在快递公司的系统里,有这个包裹的快递单号、收件人、电话、地址等信息,主键是快递单号,通过快递单号区分每一个包裹
- 对快递公司来说,这个快递单号是主键
- 对天猫商家来说,这个快递单号是外键
- 我通过订单号可以查到所有订单信息,然后从中找到快递单号,然后在快递表里查快递单号,就可以查到包裹的物流信息
- 解释:
- c、区别
- 主键
- 唯一标识一条记录,不能有重复的,不允许为空
- 用来保证数据完整性
- 一张表主键只能有一个
- 外键
- 表的外键是给另一张表使用的,这个外键在另一张表里可以重复,可以是空值。但是在自己表里必须是唯一的,不然你怎么么代表自己表里的一条数据
- 用来和其他表建立联系用的
- 一个表可以有多个外键
- 主键
- a、什么是主键
4、一对一
a、单向一对一主键关联
- 简单来说,我的主键,就是我的外键,也就是你的主键,一样的值
- 单向的意思就是,只可以我用我的外键查你,你不查我
b、双向一对一主键关联
- 简单来说,我的主键,就是我的外键,也就是你的主键,一样的值
- 双向的意思就是,我可以查你,你可以查我
c、单向一对一外键关联
- 简单来说,我的主键很宝贵,不给你,那么我的其他字段呢,可以作为我的外键,给你当主键用
- 单向的意思就是,只可以我用我的外键查你,你不查我
d、双向一对一外键关联
- 简单来说,我的主键很宝贵,不给你,那么我的其他字段呢,可以作为我的外键,给你当主键用
- 双向的意思就是,我可以查你,你可以查我
- e、一对一外键关联的注意点
- 因为是一对一,所以是唯一的,所以我的外键也要是唯一的,所以要设置属性unique=true
- 那为什么一对一主键关联不需要设置这个属性呢,因为两边都是主键啊,主键本来就是唯一且非空的
2、什么是级联
比如主表改了数据,子表相关的数据也会跟着变化,这就是级联
- 例如用户表里面,用户的地址改了,那么包裹的地址也要跟着变化
- 用实体类来说,Father就是那个主表,里面有一个set集合的变量,son就是set集合中的一个元素,所以是father.getSon().add(son);
- cascade参数
- cascade=”all
- 所有情况下均进行关联操作,包括save、update、delete
- cascade=”none”
- 所有情况下均不进行关联操作,这是默认值
- cascade=”save-update”
- 仅save、update、saveOrUpdate时进行关联操作
- cascade=”delete”
- 仅delete时进行关联操作
- cascade=”all-delete-orphan”
- 用户对应多个包裹,其中一个包裹,用户不要了,这个包裹就被干掉了
- 当一个节点在对象图中成为孤儿节点时,删除该节点(摘自百度)
- inverse参数
- 这个参数决定谁来维护这个关联关系,它只在主表的那个配置文件里面配置
- 即Father的配置文件,
- 什么是关联关系,即是主表更新了,主表要去更新一下子表吗?还是让子表自己更新?
- inverse=”false”,代表指定Father维护这个关联关系,即主表更新的同时,子表也更新了,然后主表还要更新一下子表
- 比如有Father表,有f_id,Son表,也有f_id,因为两个是关联的嘛,然后更新Father表,Son表也会一起更新,但是这个时候Son表的f_id是空的,因为Father表还没更新完
- 待Father表更新完了,然后Son表也更新完了,Father表会将新增的那个f_id扔给Son表去update一下刚才空的f_id
- inverse=”true”,代表指定Son维护这个关联关系,即主表更新后,子表再更新更新
- 比如有Father表,有f_id,Son表,也有f_id,因为两个是关联的嘛,然后更新Father表,Son表是不会有动静的,待Father表更新完了,把f_id扔给Son表,然后Son表再默默的更新自己的信息
- inverse=”false”,代表指定Father维护这个关联关系,即主表更新的同时,子表也更新了,然后主表还要更新一下子表
懂了不,再看例子
Father表配置
<set name="son" inverse="true" cascade="all" >
Hibernate: insert into FATHER (F_NAME) values (?)//inverse="true",代表指定Son维护这个关联关系,即主表更新了,子表自己更新Hibernate: insert into SON (S_NAME, f_id) values (?, ?)
Father表配置
<set name="son" inverse="false" cascade="all" >
Hibernate: insert into FATHER (F_NAME) values (?)Hibernate: insert into SON (S_NAME, f_id) values (?, ?)//这里多了一次更新操作,为啥?//因为inverse="false",代表指定Father维护这个关联关系,即主表更新了,子表更新了,主表还要去更新一下子表//所以主表更新了自己之后,又去更新了一下子表的F_IDHibernate: update SON set f_id=? where S_ID=?
- cascade与inverse
- 这个时候,大家可能又懵逼了,什么鬼啊,cascade=all的时候,不是已经级联了吗,不是主表更新子表也跟着更新了吗?!!
- 其实是这样的,主表更新,子表也跟着更新,这个是cascade级联的作用
- 粗暴的理解:让主表还是从表去控制子表的更新,这个是inverse的作用。配合上面的例子慢慢体会
3、主键的类别:
Assigned
- 由用户生成主键值,并且要在save()之前指定否则会抛出异常
Hilo
- 使用高低位算法生成主键,高低位算法使用一个高位值和一个低位值,然后把算法得到的两个值拼接起来作为数据库中的唯一主键
Increment
- 对主键值采取自动增长的方式生成新的主键值,但要求底层数据库的主键类型为long,int等数值型。主键按数值顺序递增,增量为1。
- 主键值由Hibernate本身维护,适用于所有的数据库,不适合多进程并发更新数据库
Identity
- 根据底层数据库,来支持自动增长
- 例如MySQl中是auto_increment, SQL Server 中是Identity
Sequence
- 需要底层数据库支持序列,例如Oracle数据库等
Native
- 根据不同的底层数据库自动选择Identity、Sequence、Hilo主键生成方式
- 便于程序移植,项目中如果用到多个数据库时,可以使用这种方式。
UUID
- 使用128位UUID算法生成主键,能够保证网络环境下的主键唯一性,也就能够保证在不同数据库及不同服务器下主键的唯一性
Foreign GUID
- 用于一对一关系中。GUID主键生成方式使用了一种特殊算法,保证生成主键的唯一性,支持SQL Server和MySQL
- Hibernate的映射关系与级联(一对一、一对多、多对多)
- hibernate一对一,一对多,多对一,多对多关系映射与级联
- Hibernate 关系映射 (一对一,一对多,多对多)
- Hibernate 关系映射案例(一对一关系,一对多关系,多对多关系)
- Hibernate双向一对一,一对多,多对多关系映射
- Hibernate关联关系映射 一对一 一对多 多对一
- Hibernate 基于注解的方式 实现的关系映射案例(一对一关系,一对多关系,多对多关系)
- hibernate对象关系映射(一对一,一对多,多对一,多对多的单向,双向映射)
- hibernate对象关系映射( 一对一,一对多,多对一,多对多的单向,双向映射 ——)
- Hibernate的关联关系注解映射(一对一、一对多、多对一、多对多、自连接)
- Hibernate关联关系映射配置(一对多,多对多,一对一)
- Hibernate的关系映射-------多对一与一对多
- 【Symfony】 Doctrine 关系映射,一对一、一对多、多一对、多对多的关系映射(Association Mapping)
- hibernate关系映射管理(一对多,多对一,一对一,多对多)
- hibernate关系映射管理(一对多,多对一,一对一,多对多)
- Hibernate 一对一,一对多关系
- Hibernate关系(双向一对一、一对多、多对多和自身多对多)映射注解整理
- Hibernate.hbm.xml映射关系(一对一、一对多、多对多)
- 【二叉树】二分查找树,节点删除【Add to List 450. Delete Node in a BST】
- 查看Linux下端口占用情况的命令
- vue-cli——人员管理实例
- 实时公交到站数计算
- UNITY 开发日记/教程 俄罗斯方块 (四) 方块下落和落地判定
- Hibernate的映射关系与级联(一对一、一对多、多对多)
- Android GreenDao3.0入门学习
- 复制带随机指针的链表-LintCode
- 异步执行SQL
- 小白心酸的JDK安装历程
- windows 下npm Cannot find module 'semver'
- 构造方法能被继承吗
- 深入浅出单实例SINGLETON设计模式
- 关于在编译比特币或者qtum中遇到的一点小问题