一对多
建表原则:在多的一方创建外键指向一的一方的主键
在Java中采用对象关系来描述:一对多的关系描述的是一个A对应多个B类类型的情况,需要在A类以Set集合的方式引入B类型的对象,在B类中定义A类类型的属性a;1
2
3
4
5
6
7class A{
Set<B> bs; //B的集合
}
class B{
A a;
}
根据SQL创建实体
SQL
1 | CREATE TABLE `cst_customer` ( |
客户表实体
1 | // 客户表(customer) |
联系人表实体
1 | // 联系人表(linkman) |
配置关系映射
Customer.hbm.xml
1 | <?xml version="1.0" encoding="UTF-8"?> |
Linkman.hbm.xml
1 | <?xml version="1.0" encoding="UTF-8"?> |
hibernate.cfg.xml
1 | <?xml version="1.0" encoding="UTF-8"?> |
小试牛刀
双向关联新增操作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
31
32
33
34
35
36
37
38
39
40
41
42
43package me.yanrs.service;
import org.hibernate.Session;
import org.hibernate.Transaction;
import me.yanrs.domain.Customer;
import me.yanrs.domain.Linkman;
import me.yanrs.utils.HibernateUtils;
public class one_to_many {
public static void main(String[] args) {
one2many_base();
}
private static void one2many_base() {
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction beginTransaction = session.beginTransaction();
//创建一条客户的数据
Customer customer = new Customer();
customer.setCust_name("c1");
//创建两条联系人的数据
Linkman linkman1 = new Linkman();
linkman1.setLkm_name("l1");
Linkman linkman2 = new Linkman();
linkman2.setLkm_name("l2");
//一对多两者相互映射
customer.getLinkmans().add(linkman1);
customer.getLinkmans().add(linkman2);
linkman1.setCustomer(customer);
linkman2.setCustomer(customer);
session.save(customer);
session.save(linkman1);
session.save(linkman2);
//最后别忘了commit
beginTransaction.commit();
}
}
运行上面代码就能在数据库中看见新增的一个客户和两个联系人
以上的操作是双向的,即客户关联了联系人,联系人关联了客户。1
2
3
4
5
6
7//客户关联了联系人
customer.getLinkmans().add(linkman1);
customer.getLinkmans().add(linkman2);
//联系人关联了客户
linkman1.setCustomer(customer);
linkman2.setCustomer(customer);
以上操作在保存的时候还保存了双方,那如果只保存一方会怎么样呢?也就是说建立了双向维护的关系,但是我们只保存联系人或者客户。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
31
32
33
34
35
36
37
38
39package me.yanrs.service;
import org.hibernate.Session;
import org.hibernate.Transaction;
import me.yanrs.domain.Customer;
import me.yanrs.domain.Linkman;
import me.yanrs.utils.HibernateUtils;
public class one_to_many {
public static void main(String[] args) {
// one2many_base();
one2many_no_cascade();
}
private static void one2many_no_cascade() {
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction beginTransaction = session.beginTransaction();
// 创建一条客户的数据
Customer customer = new Customer();
customer.setCust_name("c11");
//创建一条联系人的数据
Linkman linkman1 = new Linkman();
linkman1.setLkm_name("l11");
//建立关系
customer.getLinkmans().add(linkman1);
linkman1.setCustomer(customer);
//这里只保存客户,不保存联系人
session.save(customer);
//最后别忘了commit
beginTransaction.commit();
}
}
运行上面代码,会出现一下错误(错误为瞬时对象异常,持久态对象关联了一个瞬时对象的异常。)1
Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: me.yanrs.domain.Linkman
无论从哪一方保存都会出现这个异常,要解决这样的问题,即实现只保存一方,那么我们就需要级联操作
级联关系
当主控方执行保存,更新或者删除操作时,其关联对象(被控方)也执行相同的操作。在映射文件通过cascade属性的设置来控制来设置是否关联对象采用级联操作。
级联新增
级联是有方向性的,在进行级联操作之前,我们要确认主控方。例如我们要保存客户,所以客户就是主控方,那么就需要在客户的映射文件中进行配置cascade
1 | <?xml version="1.0" encoding="UTF-8"?> |
这样,以客户为主控方的单项的保存就能实现了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
31
32
33
34
35
36
37
38
39package me.yanrs.service;
import org.hibernate.Session;
import org.hibernate.Transaction;
import me.yanrs.domain.Customer;
import me.yanrs.domain.Linkman;
import me.yanrs.utils.HibernateUtils;
public class one_to_many {
public static void main(String[] args) {
// one2many_base();
one2many_no_cascade();
}
private static void one2many_cascade() {
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction beginTransaction = session.beginTransaction();
// 创建一条客户的数据
Customer customer = new Customer();
customer.setCust_name("c11");
//创建一条联系人的数据
Linkman linkman1 = new Linkman();
linkman1.setLkm_name("l11");
//建立关系
customer.getLinkmans().add(linkman1);
linkman1.setCustomer(customer);
//这里只保存客户,不保存联系人
session.save(customer);
//最后别忘了commit
beginTransaction.commit();
}
}
如果我们要保存联系人,所以联系人就是主控方,那么就需要在联系人的映射文件中进行配置cascade1
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<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="me.yanrs.domain">
<class name="me.yanrs.domain.Linkman" table="cst_linkman">
<id name="lkm_id">
<generator class="native"></generator>
</id>
<property name="lkm_name" column="lkm_name"></property>
<property name="lkm_gender" column="lkm_gender"></property>
<property name="lkm_phone" column="lkm_phone"></property>
<property name="lkm_mobile" column="lkm_mobile"></property>
<property name="lkm_email" column="lkm_email"></property>
<property name="lkm_qq" column="lkm_qq"></property>
<property name="lkm_position" column="lkm_position"></property>
<property name="lkm_memo" column="lkm_memo"></property>
<!--
many-to-one:
name: 属性的名称
class: 类的全路径
column:数据库中的名称
-->
<many-to-one cascade="save-update" name="customer" class="me.yanrs.domain.Customer" column="lkm_cust_id"></many-to-one>
</class>
</hibernate-mapping>
同理,以联系人为主控方的单项的保存就能实现了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
31
32
33
34
35
36
37
38
39package me.yanrs.service;
import org.hibernate.Session;
import org.hibernate.Transaction;
import me.yanrs.domain.Customer;
import me.yanrs.domain.Linkman;
import me.yanrs.utils.HibernateUtils;
public class one_to_many {
public static void main(String[] args) {
// one2many_base();
one2many_no_cascade();
}
private static void one2many_cascade() {
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction beginTransaction = session.beginTransaction();
// 创建一条客户的数据
Customer customer = new Customer();
customer.setCust_name("c111");
//创建一条联系人的数据
Linkman linkman1 = new Linkman();
linkman1.setLkm_name("l111");
//建立关系
customer.getLinkmans().add(linkman1);
linkman1.setCustomer(customer);
//这里只保存客户,不保存联系人
session.save(linkman1);
//最后别忘了commit
beginTransaction.commit();
}
}
普通删除
删除customer中id为94的数据1
2
级联删除
级联删除也是有方向性的,删除客户的同时级联删除联系人,也可以删除联系人的同时级联删除客户(这种情况很少)
- 删除客户的时候同时删除客户的联系人。
删除的主控方为客户,所以在Customer.hbm.xml设置cascade=”delete”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
31
32
33<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="me.yanrs.domain">
<class name="me.yanrs.domain.Customer" table="cst_customer">
<id name="cust_id">
<generator class="native"></generator>
</id>
<property name="cust_name" column="cust_name"></property>
<property name="cust_user_id" column="cust_user_id"></property>
<property name="cust_create_id" column="cust_create_id"></property>
<property name="cust_source" column="cust_source"></property>
<property name="cust_industry" column="cust_industry"></property>
<property name="cust_level" column="cust_level"></property>
<property name="cust_phone" column="cust_phone"></property>
<property name="cust_mobile" column="cust_mobile"></property>
<!--
set:
name:属性的名称
key:
columns:数据库中外键的名称
one-to-many:
class: 类的全路径
-->
<set cascade="delete" name="linkmans">
<key column="cust_linkman"></key>
<one-to-many class="me.yanrs.domain.Linkman"/>
</set>
</class>
</hibernate-mapping>
删除代码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
31package me.yanrs.service;
import java.awt.print.Printable;
import org.hibernate.Session;
import org.hibernate.Transaction;
import me.yanrs.domain.Customer;
import me.yanrs.domain.Linkman;
import me.yanrs.utils.HibernateUtils;
public class one_to_many {
public static void main(String[] args) {
// one2many_base();
// one2many_no_cascade();
one2many_delete_customer_no_cascade();
}
private static void one2many_delete_customer_cascade() {
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction beginTransaction = session.beginTransaction();
//查询ID为94的customer
Customer customer = session.get(Customer.class, 94l);
System.out.println(customer);
//删除
session.delete(customer);
//不要忘记提交
beginTransaction.commit();
}
}
这样就能实现删除customer中id为94的customer,且删除linkman中有关联的数据。
- 删除联系人的时候删除客户
删除的主控方为联系人,所以在Linkman.hbm.xml设置cascade=”delete”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<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="me.yanrs.domain">
<class name="me.yanrs.domain.Linkman" table="cst_linkman">
<id name="lkm_id">
<generator class="native"></generator>
</id>
<property name="lkm_name" column="lkm_name"></property>
<property name="lkm_gender" column="lkm_gender"></property>
<property name="lkm_phone" column="lkm_phone"></property>
<property name="lkm_mobile" column="lkm_mobile"></property>
<property name="lkm_email" column="lkm_email"></property>
<property name="lkm_qq" column="lkm_qq"></property>
<property name="lkm_position" column="lkm_position"></property>
<property name="lkm_memo" column="lkm_memo"></property>
<!--
many-to-one:
name: 属性的名称
class: 类的全路径
column:数据库中的名称
-->
<many-to-one cascade="delete" name="customer" class="me.yanrs.domain.Customer" column="lkm_cust_id"></many-to-one>
</class>
</hibernate-mapping>
删除代码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
31package me.yanrs.service;
import java.awt.print.Printable;
import org.hibernate.Session;
import org.hibernate.Transaction;
import me.yanrs.domain.Customer;
import me.yanrs.domain.Linkman;
import me.yanrs.utils.HibernateUtils;
public class one_to_many {
public static void main(String[] args) {
// one2many_base();
// one2many_no_cascade();
one2many_delete_customer_no_cascade();
}
private static void one2many_delete_customer_no_cascade() {
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction beginTransaction = session.beginTransaction();
//查询ID为94的customer
Linkman linkman = session.get(Linkman.class, 5l);
System.out.println(linkman);
//删除
session.delete(linkman);
//不要忘记提交
beginTransaction.commit();
}
}
这样就能实现删除linkman中id为5的linkman,且删除customer中有关联的数据。
inverse
通常我们在使用Hibernate的时候,如果是一对多的情况,一的一方都会放弃外键的维护权。inverse的默认值是false,代表不放弃外键的维护,配置值为true,代表放弃了外键的维护权。
在上面的例子中,我们让customer放弃维护外键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
31
32
33<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="me.yanrs.domain">
<class name="me.yanrs.domain.Customer" table="cst_customer">
<id name="cust_id">
<generator class="native"></generator>
</id>
<property name="cust_name" column="cust_name"></property>
<property name="cust_user_id" column="cust_user_id"></property>
<property name="cust_create_id" column="cust_create_id"></property>
<property name="cust_source" column="cust_source"></property>
<property name="cust_industry" column="cust_industry"></property>
<property name="cust_level" column="cust_level"></property>
<property name="cust_phone" column="cust_phone"></property>
<property name="cust_mobile" column="cust_mobile"></property>
<!--
set:
name:属性的名称
key:
columns:数据库中外键的名称
one-to-many:
class: 类的全路径
-->
<set inverse="true" name="linkmans">
<key column="cust_linkman"></key>
<one-to-many class="me.yanrs.domain.Linkman"/>
</set>
</class>
</hibernate-mapping>
区分cascade和inverse
cascade强调的是操作一个对象的时候,是否操作其关联对象
inverse强调的是外键的维护权