Hibernate关系映射

Hibernate关系映射

一对多关系映射

数据库:在多的一方添加外键来描述关联关系。
实体对象:在一的一方,增加一个集合用来存放多的一方。在多的一方添加一个一的一方的属性。
映射文件配置:
User.hbm.xml

1
2
3
4
5
6
7
8
9
10
11
<!--
set代表用的什么集合
name代表多的一方的集合的属性名
-->
<set name="carSet">
<!--
key:多的一方的外键名称
-->
<key column="user_id"></key>
<one-to-many class="pojo.Car"></one-to-many>
</set>

Car.hbm.xml

1
2
3
4
5
6
<!--
name:一的一方的属性名
class:一的一方的类全路径
colum:表中的外键外键名称
-->
<many-to-one name="user" class="pojo.User" column="user_id"></many-to-one>

编写测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Test
public void test_HiernateYinshe(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();

User user = new User();
user.setName("cl");
user.setPassword("123456");

Car car1 = new Car();
car1.setCar_name("byd");
car1.setColor("red");
Car car2 = new Car();
car2.setCar_name("baoma");
car2.setColor("blue");

//建立关系
user.getCarSet().add(car1);
user.getCarSet().add(car2);
car1.setUser(user);
car2.setUser(user);

session.save(user);
// 以下两句在测试只保存一方时注释
session.save(car1);
session.save(car2);

tx.commit();
session.close();
}

通过观察控制台,会发现控制台输出了三条insert语句和两条update语句。
既然两个对象以及进行了双向关联,那我们只保存一方是否可以呢?
将上面的测试代码中保存Car的两条语句注释。会报一下错误:
http://ocx5m3vc3.bkt.clouddn.com/%E7%9E%AC%E6%97%B6%E5%AF%B9%E8%B1%A1%E5%BC%82%E5%B8%B8.png
这样保存是不行的,无论是从哪一方保存都会报错:瞬时对象异常。一个持久化对象关联了一个瞬时态对象。
如果我们只想保存一个方向,那么我们可以使用Hibernate的级联操作。
首先要确定我们要保存的的主控方式哪一方,用户拥有车,所以用户是主控方。我们需要在用户的映射文件中进行以下配置。

1
2
3
4
<set name="carSet" cascade="save-update">
<key column="user_id" ></key>
<one-to-many class="pojo.Car"></one-to-many>
</set>

当我们在一方配置了级联配置后,保存时会将其级联的对象也保存在数据库。此时运行测试代码,会发现还是三条insert和两条update语句。因为在Car中还会进行维护外键的操作。其实这是没必要的了。所以我们必须选择一方放弃维护关系。
我们选择User放弃维护,因为一个老师要记住所有学生是比较困难的,但是让学生记住老师确是很容易的。
我们再修改User.hbm.xml

1
2
3
4
5
6
7
<!--
inverse默认值为false,代表不放弃维护。
-->
<set name="carSet" cascade="save-update" inverse="true">
<key column="user_id" ></key>
<one-to-many class="pojo.Car"></one-to-many>
</set>

此时再执行测试代码。会发现只剩下了三条insert语句

  • 区分cascade和inverse
    cascade强调的是操作一个对象时,是否操作其关联对象。
    inverse强调的是外键的维护权

多对多

数据库:通过一张中间表来描述其对应关系。
实体对象:在两个实体类中都创建一个集合来代表拥有的另一方。
User.hbm.xml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--
name:关联的另一方的集合名称
table:中间表的名称
cascade:级联维护相关对象
-->
<set name="roleSet" table="tb_user_role" cascade="save-update">
<!--
colum:当前对象在中间表的外键名称
-->
<key column="user_id"></key>
<!--
class:关联另一方的类的全路径。
colum:关联的另一方在中间表的外键
-->
<many-to-many class="pojo.Role" column="role_id"></many-to-many>
</set>

Role.hbm.xml

1
2
3
4
5
6
7
<!--
inverse:多对多的关系映射中必须要有一方放弃维护外键。
-->
<set name="userSet" table="tb_user_role" inverse="true">
<key column="role_id"></key>
<many-to-many class="pojo.User" column="user_id"></many-to-many>
</set>

我们很容易就发现多对多的两方的映射文件差不多。
加载映射配置后编写测试代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Test
public void test_many2Many(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();

User user1 = new User("u1","u1");
User user2 = new User("u2","u2");

Role role1 = new Role("前台");
Role role2 = new Role("人事");
Role role3 = new Role("助理");

//建立关系,如果建立了双向关系,一定要有一方放弃外键维护。
user1.getRoleSet().add(role1);
user1.getRoleSet().add(role2);
user2.getRoleSet().add(role1);
user2.getRoleSet().add(role3);

role1.getUserSet().add(user1);
role1.getUserSet().add(user2);
role2.getUserSet().add(user1);
role3.getUserSet().add(user2);

session.save(user1);
session.save(user2);

tx.commit();
session.close();
}

在多对多的关系映射中,进行了双向维护,此时必须有一方需要放弃维护。