Hibernate 关联映射
1. 单向N-1关联
单向N-1关联只需要从N的一端访问1的一端.
如,多人住同一地址,只需要在人的实体端找到地址实体,无需关心一个地址的所有人.
(Person类有个Address类型的Field,而Address类无需关联Person)
1.1 无连接表的N-1关联(基于外键)
Person端增加了Address,该属性不是一个普通的组件,而是引用另一个持久化的类.
使用<many-to-one>在N端(Person端)增加一个外键(关联到address表的主键address_id).
<!-- N-1无连接关联 cascade指定哪些持久化操作会从主表记录级联到从表记录 -->
<many-to-one name="address" cascade="all" class="Address" column="address_id" />
mysql> desc address_inf;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| address_id | int(11) | NO | PRI | NULL | auto_increment |
| addressDetail | varchar(255) | YES | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
mysql> desc person_senior_inf;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| person_id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| age | int(11) | YES | | NULL | |
| address_id | int(11) | YES | MUL | NULL | |
+------------+--------------+------+-----+---------+----------------+
1.2 有连接表的N-1关联
(基于连接表,需要第三张表,保存两个表的id对应关系,这个表中,personid是唯一的,
因为一个person只能属于一个address)
join (P436)
<!-- 有连接表的N-1关联(基于连接表,需要第三张表,保存两个表的id对应关系) -->
<join table="person_address">
<!-- 本表主键 -->
<key column="person_id" />
<!-- 1端表的主键 -->
<many-to-one name="address" cascade="all" class="Address" column="address_id" />
</join>
mysql> desc address_inf;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| address_id | int(11) | NO | PRI | NULL | auto_increment |
| addressDetail | varchar(255) | YES | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
mysql> desc person_senior_inf;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| person_id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| age | int(11) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
mysql> desc person_address;
+------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+-------+
| person_id | int(11) | NO | PRI | NULL | |
| address_id | int(11) | YES | MUL | NULL | |
+------------+---------+------+-----+---------+-------+
2. 单向1-1关联
2.1 基于外键的1-1关联(无连接的)
基本同无连接的N-1,但是需要增加一个unique=true.表示在person表中,address_id也是唯一的(不可重复).
这样一个person对应一个address,一个address也只能对应一个person
mysql> desc address_inf;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| address_id | int(11) | NO | PRI | NULL | auto_increment |
| addressDetail | varchar(255) | YES | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
mysql> desc person_senior_inf;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| person_id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| age | int(11) | YES | | NULL | |
| address_id | int(11) | YES | UNI | NULL | |
+------------+--------------+------+-----+---------+----------------+
2.2 有连接表的单向1-1
基本同1.2,需要在many-to-one中新增一个属性,unique=true
mysql> desc address_inf;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| address_id | int(11) | NO | PRI | NULL | auto_increment |
| addressDetail | varchar(255) | YES | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
mysql> desc person_senior_inf;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| person_id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| age | int(11) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
mysql> desc person_address;
+------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+-------+
| person_id | int(11) | NO | PRI | NULL | |
| address_id | int(11) | YES | UNI | NULL | |
+------------+---------+------+-----+---------+-------+
2.3 基于主键的1-1关联.
后端的1先有了,前端的1后生产,前端的1的主键有关联的实体负责生成.
此时address作为主表,有主键,再生成person是,person的主键根据address的主键生成.
因为person和address是一一对应的,所以他们两个可以共享一个id.
因为先生成address,所以person的主键由关联的address来提供.
此时生成的表,只有person_id了,值就是address表中address_id
mysql> desc address_inf;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| address_id | int(11) | NO | PRI | NULL | auto_increment |
| addressDetail | varchar(255) | YES | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
mysql> desc person_senior_inf;
+-----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| person_id | int(11) | NO | PRI | NULL | |
| name | varchar(255) | NO | | NULL | |
| age | int(11) | YES | | NULL | |
+-----------+--------------+------+-----+---------+-------+
此种方式导致 新建一个person后,其对应的address无法更新?
配置:
<!-- 主键 单向1-1关联 -->
<id name="id" column="person_id">
<!-- 主键生成策略是foreign,表示根据关联类的主键来生成该实体的主键 -->
<generator class="foreign">
<param name="property">address</param>
</generator>
</id>
<!-- 下面映射基于主键的1-1关联 -->
<one-to-one name="address" />
3. 单向1-N关联
持久化类里需要使用集合属性,而这个集合属性数据来自其他的表,且该表已经有映射的对象了.
(与之前的映射集合元素不一样,之前是使用element,且没有映射对象.,比如之前没有Address这个类),
这里element改成one-to-many
3.1 无连接表的单向1-N
<!-- 无连接表的单向 1-N -->
<set name="allAddress" >
<key column="person_id"/>
<one-to-many class="Address" />
</set>
//创建新地址
Address address2 = new Address("三山街");
Address address3 = new Address("夫子庙");
session.persist(address2);//这里需要先持久化否则后面add会报错
session.persist(address3);
aGuy.getAllAddress().add(address2);
aGuy.getAllAddress().add(address3);
mysql> desc address_inf;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| address_id | int(11) | NO | PRI | NULL | auto_increment |
| addressDetail | varchar(255) | YES | | NULL | |
| person_id | int(11) | YES | MUL | NULL | |
+---------------+--------------+------+-----+---------+----------------+
mysql> desc person_senior_inf;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| person_id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| age | int(11) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
一个person对应N个address
mysql> select * from address_inf;
+------------+---------------+-----------+
| address_id | addressDetail | person_id |
+------------+---------------+-----------+
| 1 | 新街口 | NULL |
| 2 | 三山街 | 1 |
| 3 | 夫子庙 | 1 |
+------------+---------------+-----------+
3.2 有连接表的单向1-N关联
<!-- 有连接表的单向 1-N 使用 many-to-many,但需要设置unique="true" -->
<set name="allAddress" table="person_address">
<key column="person_id"/>
<many-to-many class="Address" column="address_id" unique="true"/>
</set>
mysql> desc address_inf;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| address_id | int(11) | NO | PRI | NULL | auto_increment |
| addressDetail | varchar(255) | YES | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
mysql> desc person_senior_inf;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| person_id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| age | int(11) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
mysql> desc person_address;
+------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+-------+
| person_id | int(11) | NO | PRI | NULL | |
| address_id | int(11) | NO | PRI | NULL | |
+------------+---------+------+-----+---------+-------+
4. 单向N-N关联
和有连接的单向1-N关联类似,配置时去掉many-to-many的unique=true
<!-- 单向N-N关联 -->
<set name="allAddress" table="person_address">
<key column="person_id"/>
<many-to-many class="Address" column="address_id"/>
</set>
mysql> desc address_inf;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| address_id | int(11) | NO | PRI | NULL | auto_increment |
| addressDetail | varchar(255) | YES | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
mysql> desc person_senior_inf;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| person_id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| age | int(11) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
mysql> desc person_address;
+------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+-------+
| person_id | int(11) | NO | PRI | NULL | |
| address_id | int(11) | NO | PRI | NULL | |
+------------+---------+------+-----+---------+-------+
5. 双向1-N管理
5.1 无连接表的双向1-N关联
N端需要修改<many-to-one>,1的一段需要增加<set>,<set>里面增加
<key>子元素映射外键,并使用<one-to-many>子元素映射关联属性.
<!-- 无连接表 双向1-N关联,1的那端 -->
<set name="allAddressWithPerson" inverse="true">
<key column="person_id"/>
<one-to-many class="AddressWithPerson"/>
</set>
<!-- 无连接表 双向1-N关联,N 的那端 -->
<!-- 必须指定列名为person_id,与关联实体中key元素的column值相同 -->
<many-to-one name="person" class="Person" column="person_id" not-null="true" />
mysql> desc address_inf;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| address_id | int(11) | NO | PRI | NULL | auto_increment |
| addressDetail | varchar(255) | YES | | NULL | |
| person_id | int(11) | NO | MUL | NULL | |
+---------------+--------------+------+-----+---------+----------------+
mysql> desc person_senior_inf;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| person_id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| age | int(11) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
因为N的那端已经有了1端的引用,所有在Java代码中的步骤如下(P446):
(1) 创建一个1端实例,并持久化
(2) 创建一个N端的实例,使用set方法将(1) 中实例set进来
(3) 持久化(2)中N端实例
public void test()
{
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
Person aGuy = new Person();
aGuy.setName("Frank");
aGuy.setAge(28);
//持久化1端实例
session.save(aGuy);
//创建N端实例
AddressWithPerson addressWithPerson1 = new AddressWithPerson("新街口 with person");
addressWithPerson1.setPerson(aGuy);
//这里表示通过N端对象来设置关联关系,因为在1端配置了,inverse="true"
//表示不能用Person的Set的add方法设置关联关系了.
session.persist(addressWithPerson1);
tx.commit();
HibernateUtil.closeSession();
}
5.2 有 连接表的双向1-N关联
有 连接表 的双向1-N,1端使用set,N端,相当于N-1,使用join.
<!-- 有连接表的 双向1-N关联 1的那端 -->
<set name="allAddressWithPerson" inverse="true" table="person_address">
<key column="person_id"/>
<many-to-many class="AddressWithPerson" column="address_id" unique="true" />
</set>
<!-- 有 连接表 的 双向1-N关联 N的那端 -->
<join table="table_address">
<key column="address_id" />
<many-to-one name="person" column="person_id" not-null="true" />
</join>
mysql> desc address_inf;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| address_id | int(11) | NO | PRI | NULL | auto_increment |
| addressDetail | varchar(255) | YES | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
2 rows in set (0.02 sec)
mysql> desc person_senior_inf;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| person_id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| age | int(11) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> desc person_address;
+------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+-------+
| address_id | int(11) | NO | PRI | NULL | |
| person_id | int(11) | NO | MUL | NULL | |
+------------+---------+------+-----+---------+-------+
mysql> select * from person_address;
+------------+-----------+
| address_id | person_id |
+------------+-----------+
| 1 | 1 |
| 2 | 1 |
+------------+-----------+
2 rows in set (0.00 sec)
6. 双向 N-N关联
双向N - N 关联需要在两端都使用set集合.双向N - N关联只能采用连接表
<!-- 双向N-N关联 只能采用连接表 -->
<set name="allAddressWithPerson" table="person_address">
<key column="person_id"/>
<many-to-many class="AddressWithPerson" column="address_id" />
</set>
<!-- 双向N-N关联 只能采用连接表 -->
<set name="persons" table="person_address">
<key column="address_id"/>
<many-to-many class="Person" column="person_id" />
</set>
mysql> desc address_inf;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| address_id | int(11) | NO | PRI | NULL | auto_increment |
| addressDetail | varchar(255) | YES | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
mysql> desc person_senior_inf;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| person_id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| age | int(11) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
mysql> desc person_address;
+------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+-------+
| person_id | int(11) | NO | PRI | NULL | |
| address_id | int(11) | NO | PRI | NULL | |
+------------+---------+------+-----+---------+-------+
双向N-N时,在两个id上创建联合主键:primary key (address_id, person_id)
7. 双向1-1关联
修改两个类,存放对对方的引用,并加上getter和setter
7.1 基于外键的双向 1-1关联
<!-- 基于外键的 双向 1-1 关联 person端-->
<one-to-one name="addressWithPerson" property-ref="person" />
<!-- 基于外键的 双向 1-1 关联 address端-->
<many-to-one name="person" unique="true" column="person_id" not-null="true" />
以下略.(P450)
7.2 基于主键的 双向 1-1 关联(P451)
7.3 有连接表的双向1-1关联(P452)