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,即是