Hibernate【关联映射】
Hibernate可以以面向对象的方式进行数据库访问,既然是面向对象,我们知道在客观世界中,对象不会孤立的存在,在Hibernate中,Hibernate把这种对象与对象之间的联系称为关联关系,如果在我们设计实体类的时候,可以良好的映射这些关联关系,便可以大大简化持久层数据的访问
关联关系分为两类:
单向关系:假如学生,老师这个例子,单向关系就是指只能从老师可以访问学生,或者只能从学生访问老师
双向关系:还是上面的例子,双向关系是指 既能从老师访问学生,又能从学生访问老师
单向关系中具体的还有: 双向关系中具体的有:
1 -> 1 1 <-> 1
1 -> N 1 <-> N
N -> N N <-> N
N -> 1
(一) 【单向】1 -> N关联
在单向1 -> N关联中,一的持久化类中需要一个集合属性来关联到N的一方,表示有多个N。
持久化类结构如下:
public class Person implements Serializable{ private Integer id; //标识符属性 private String name; //一般属性 private Set<Address> addresses; //关联对象 ..... } ---1 -> N(从一个人身上可以知道了他多个住址,而只找到住址,住址不会告诉你这是谁住的)---- public class Address implements Serializable { private Integer id; private String place; ... }
-
无连接表的关联(外键表示关联关系)
配置关联关系的两种方式:
方式一: 使用映射文件(重点)
(1)*.hbm.xml中核心配置
<!-- set元素针对的就是持久化类中的Set属性 cascade操作的是一般属性 inverse针对的是外键操作--> <set name="addresses" cascade="save-update" > <!-- 外键字段名,告诉Hibernate,personId就是外键,用这个外键建立与Address持久化类的关联 --> <key column="personId"></key> <!-- 告诉Hibernate,Person持久化类中的set集合,放的是哪个元素 --> <one-to-many class="domain.Address" /> </set>
(2)创建了外键来维护关联关系
从数据库查看Address表的结构:
方式二:使用注解
略
-
有连接表的关联(第三方表维护关联关系)
配置关联关系的方式:
方式一。使用*.hbm.xml(重点)
(1)*.hbm.xml中核心配置
<set name="addresses" cascade="save-update" table="person_address" > <!-- 外键表主键为这两个外键id列的组合 --> <key column="personId"></key> <!-- unique的目的为防止多对一的出现,确保是单向的一对多 --> <many-to-many class="domain.Address" column="adresssId" unique="true" /> </set>
(2)创建了第三方表的方式维护关联关系
在数据库查看第三方表person_address:
方式二。使用注解
略
【ps: session.flush()时,进行了哪些操作】
1。Hibernate会在一级缓存中检查所有持久化状态对象,决定发出update语句或者insert语句
2。检查所有的持久化对象的关联对象,如果关联对象是由临时状态转换过来的,发出insert语句,如果是从脱管对象转换过来的,判断副本,再决定发不发出update语句
1 -> N 操作
从1的一端维护时,不需要解除关系,可以直接建立关系。
从1的一端维护关系的时候,总会发出维护关系的update语句。该update语句就是更新外键,所以1的一方维护效率并不高
(二) 【单向】N -> 1关联
概述:常见的多对一关系,是父子关系。父类不能访问子类,而其子类们都可以访问父类。
下面用到的例子:是人和住址的关系,不论你问小区里的那个居民,都可以得到小区所在的具体地址。但通过具体地址你却不能知道小区有哪些人
-
无连接表的 N -> 1关联
配置关联关系的两种方式:
方式一:使用映射文件
<class name="domain.Person" table="PERSON"> <id name="id"> <column name="ID"></column> <generator class="increment"></generator> </id> <property name="name"> <column name="NAME"></column> </property> <many-to-one name="address" column="ADDRESS_ID" class="domain.Address" cascade="save-update"></many-to-one> //外键在PERSON中 </class>
方式二:使用注解
略
-
有连接表的N -> 1关联
配置关联关系的两种方式:
方式一。使用映射文件配置
<join table="person_address"> <key column="pID"></key> <many-to-one name="address" class="domain.Address" column="aID"></many-to-one> </join>
方式二。使用注解
略
(三) 【双向】1 <-> N关联
-
无连接表的关联
配置关联关系的两种方式:
方式一。使用*.hbm.xml(重点)
(1.1)对于1的一端*.hbm.xml采用的配置文件
<!-- 一对多,使用集合 --> <set name="addresses" cascade="save-update"> <key column="personId"></key> <one-to-many class="domain.Address"/> </set>
(1.2)对于N的一端*.hbm.xml采用的配置文件
<many-to-one name="person" class="domain.Person" column="personId" cascade="save-update"></many-to-one>
(2) 查看两张表的数据库中的结构
方式二。适用注解
-
有连接表的关联
配置关联关系的方式:
方式一。使用*.hbm.xml(重点)
(1.1)对于1的一端*.hbm.xml采用的配置文件
<set name="addresses" cascade="save-update" table="person_address"> <key column="personId"></key> <many-to-many class="domain.Address" column="addressId"/> </set>
(1.2)对于N的一端*.hbm.xml采用的配置文件
<!-- join元素强制使用连接表 --> <join table="person_address"> <key column="addressId"></key> <many-to-one name="person" class="domain.Person" column="personId" cascade="save-update"></many-to-one> </join>
(2) 查看两张表的数据库中的结构
方式二。使用注解
总结:
用1的一方维护关系,1的一方始终会发出维护关系的sql语句,
如果用多的一方维护关系,他操作的就是N的本身。没有维护关系之说。
所以多的一方维护效率比较高
(四) 【单向】N -> N关联
-
无连接表的关联
N -> N 没有外键关联方式。
-
有连接表的关联
配置关联关系的方式:
方式一:使用*.hbm.xml(重点)
<set name="stations" cascade="save-update" table="BUS_STATION"> <key column="bId"></key> <many-to-many class="domain.Station" column="sID"></many-to-many> </set>
方式二:使用注解
略
(五) 【双向】N -> N关联
-
无连接表的关联
N -> N 没有外键关联方式
-
有连接表的关联
配置关联关系的两种方式:
方式一。使用*.hbm.xml(重点)
(1)在N的两端配置
<set name="stations" cascade="save-update" table="BUS_STATION"> <key column="bId"></key> <many-to-many class="domain.Station" column="sId"></many-to-many> </set> <set name="bus" cascade="save-update" table="BUS_STATION"> <key column="sId"></key> <many-to-many class="domain.Bus" column="bId"></many-to-many> </set>
(2)查看数据库中的结构:ps:如果在<many-to-many>没有指定column的话,会在第三方表中生一个elt的列
方式二:使用注解
略
(六) 【单向】1 -> 1关联
-
无连接表的关联-主键关联
方式一:使用映射文件配置
例子:Station -> Bus。Station的主键与Bus的主键关联
Bus映射文件不做任何改变,只需改变Station的映射文件
<class name="domain.Station" table="STATION"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="foreign"> <param name="property">bus</param> //该值就是Station持久化类中的属性值,也就是<one-to-one>中的值 </generator> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <one-to-one name="bus" class="domain.Bus" constrained="true" cascade="save-update"></one-to-one> </class>
其中<one-to-one>中,constrained属性只能在<o-t-o>的映射中使用,如果constrained为true,则表明存在外键与关联表对应,并且关联表中肯定存在对应的的键于其对应,另外该选项最关键的是影响save和delete的先后顺序,例如在save的时候,如果constrained为true,则会先增加关联表,然后增加本表。delete的时候反之
<one-to-one>的单向关联中,如果constrained为false,则会在查询的时候全部取出用left outer join
的方式。如果constrained=true
,hibernate
即会延迟加载sql
,只把主表的查出来,等有用到关联表的再发sql
取。
one-to- one
的双向关联中,必须设置constrained=true
,要不然会有重复数据读,如2
个表user
,car
;在位false
时sql
如
下:select * from user a left outer join car b on a.id=b.id left outer join on user c on a.id=c.id where a.id=?
删除的时候最好删除从表,删除主表会先查询下主表,在联合查询下
方式二:使用注解
略
-
无连接表的关联-外键关联
方式一:使用映射文件
<many-to-one name="bus" class="domain.Bus" cascade="save-update" unique="true" column="bID"></many-to-one>
方式二:使用注解
略
-
有连接表的关联
方式一:使用映射文件
<join table="bus_station"> <key column="sID"></key> <many-to-one name="bus" class="domain.Bus" column="bID" cascade="save-update" unique="true"></many-to-one> </join>
方式二:使用注解
略
(七)【双向】1 <-> 1
-
主键关联
方式一:使用映射文件
bus.hbm.xml
<one-to-one name="station" class="domain.Station" cascade="save-update"></one-to-one> station.hbm.xml
<one-to-one name="bus" class="domain.Bus" cascade="save-update"></one-to-one>
方式二:适用注解
略
-
无连接表关联
方式一:使用映射文件
一开始我试了两个<many-to-one>的方式,试图在两张表都创建外键,但实际结果是建表虽然成功,但只能从一端维护,查阅网上给出的解决方案就是主键关联与外键关联结合的方式,就是如下所示
<many-to-one name="bus" class="domain.Bus" cascade="save-update" column="bID" unique="true"></many-to-one>
<one-to-one name="station" class="domain.Station" property-ref="bus"></one-to-one>
方式二:使用注解
略
-
有连接表关联
看了上面的例子不难写出,故不再复述