文章承自JPA使用指南-2
本文介绍映射关联关系
单向多对一;单向一对多;双向多对一;双向一对一;双向多对多
1.单向多对一
- 创建Order实体类,Order与Customer的关系为多对一
1 | package com.sust.entity; |
运行测试后会自动创建jpa_orders
表:
1 | CREATE TABLE `jpa_orders` ( |
2. 单向一对多
- 准备
- 注释掉Order实体中的Customer字段
- 更改Customer,在其中添加Order集合字段
- 运行测试后同样创建与单向多对一相同的
jpa_orders
表
1 | //省略掉了无关代码,完整Customer请见 指南-1 |
3.双向多对一
双向多对一
即单向多对一
+单向一对多
注意相关联的
JoinColumn
的name
字段,也就是多端外键列名
应相等
- Customer:
1
2
3
private List<Order> orderList = new ArrayList<>();
- Order:
1
2
3
private Customer customer;这里注意,操作双向多对一时,我们应让
多
端,即Order来维护关联关系,这样可以有效减少向数据库发送的为了维护关联关系所产生的sql语句简而言之就是持久化操作时,应先持久化Customer,再持久化Order
以上所展示的写法可以实现双向多对一,但是JPA为了维护关联关系会产生额外的sql语句(这里Customer与Order都定义了需要维护的字段)
故我们在使用双向多对一
时应尽量采用以下方式:
Customer
1
2
3
4
5
6/**
*这里的mappedBy所指向的是被@JoinColumn注解过的字段在java中的变量名
*简单来说也就是将维护Customer与Order映射关系的任务都交给了Order
*/
private List<Order> orderList = new ArrayList<>();Order:
1
2
3
private Customer 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
public void testDoubleManyToMany() {
int i = 1;
Customer customer = new Customer();
customer.setAge(i);
customer.setEmail(i + "@sust.edu.cn");
customer.setLastName("testDoubleManyToMany_" + i);
Order order1 = new Order();
order1.setOrderName("Order_" + i);
Order order2 = new Order();
order2.setOrderName("Order_" + (i + 1));
customer.getOrderList().add(order1);
customer.getOrderList().add(order2);
order1.setCustomer(customer);
order2.setCustomer(customer);
entityManager.persist(customer);
entityManager.persist(order1);
entityManager.persist(order2);
customer.setEmail("!!!");
customer.getOrderList().get(1).setOrderName(">>>???");
List<Order> orderList = customer.getOrderList();
System.out.println("--------");
for (Order order : orderList) {
System.out.println(order.getOrderName() + " : " +
order.getCustomer().getEmail());
}
}console输出中的主要部分:
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71Hibernate:
create table jpa_customers (
id integer not null auto_increment,
age TINYINT,
birthday date,
create_time datetime(6),
email varchar(255),
last_name varchar(255) not null,
update_time datetime(6),
primary key (id)
) engine=InnoDB
八月 24, 2019 7:32:58 下午 org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@1d035be3] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
Hibernate:
create table jpa_orders (
id integer not null auto_increment,
order_name varchar(255),
customer_id integer,
primary key (id)
) engine=InnoDB
Hibernate:
alter table jpa_orders
add constraint FKd24gr5y1e7xklyx35mqxf51g8
foreign key (customer_id)
references jpa_customers (id)
八月 24, 2019 7:32:58 下午 org.hibernate.tool.schema.internal.SchemaCreatorImpl applyImportSources
INFO: HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@58faa93b'
Hibernate:
insert
into
jpa_customers
(age, birthday, create_time, email, last_name, update_time)
values
(?, ?, ?, ?, ?, ?)
Hibernate:
insert
into
jpa_orders
(customer_id, order_name)
values
(?, ?)
Hibernate:
insert
into
jpa_orders
(customer_id, order_name)
values
(?, ?)
--------
Order_1 : !!!
>>>??? : !!!
Hibernate:
update
jpa_customers
set
age=?,
email=?,
update_time=?
where
id=?
Hibernate:
update
jpa_orders
set
customer_id=?,
order_name=?
where
id=?
4.双向一对一
准备
创建Department
manager
字段用@OneToOne
来声明一对一关系,建议开启懒加载,否则查询会使用左外连接,效率比较差@JoinColumn(name = "manager_id", unique = true)
则用来声明外键列名为department_id
- 配置
unique
属性确保外键值唯一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Department {
private Manager manager;
private Integer id;
private String departmentName;
}创建Manager
department
字段用@OneToOne
来声明一对一关系,需同样配置懒加载- 配置
mappedBy
属性,将维护工作交给Department的manager
属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Manager {
private Department department;
private Integer id;
private String managerName;
private Integer age;
}
插入测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void testOneToOne() {
Manager manager = new Manager();
manager.setAge(19);
manager.setManagerName("菜虚鲲");
Department department = new Department();
department.setDepartmentName("天庭");
department.setManager(manager);
manager.setDepartment(department);
/**
* 这里一定注意先持久化不维护关联关系的实体
* 而由后持久化的对象负责维护关联关系
* 否则多出的update外键列的语句会对效率有较大影响
*/
entityManager.persist(manager);
entityManager.persist(department);
}5. 双向多对多
创建Item
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Item {
private List<Category> categoryList;
private Integer id;
private String itemName;
}@joinColumns
:映射关联表,name指定关联表表名joinColumns
属性:映射当前类所在表(jpa_items)在关联表(items_categories)中的外键(id)inverseJoinColumns
属性:映射关联的实体类所对应的表(jpa_categories)在关联表中的外键(id)- 注意
@JoinColumn
注解中,name属性只是对列的新命名,referencedColumnName才指定了主键,不过referencedColumnName可以省略,这样会默认映射到注释实体对应表的主键 - 默认启用懒加载哦~
创建Category
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Category {
private Integer id;
private String categoryName;
private List<Item> itemList;
}- 这里一定注意,多对多关系只配置一方(这里选择了Item)维护关系,故在另一方(Category),我们需要给
@ManyToMany
配置mappedBy属性(里面填存在维护关系注解的变量的名称)来托管维护
- 这里一定注意,多对多关系只配置一方(这里选择了Item)维护关系,故在另一方(Category),我们需要给
@JoinTable
所生成的表DDL:1
2
3
4
5
6
7
8
9
10CREATE TABLE `items_categories` (
`items_id` int(11) NOT NULL,
`categories_id` int(11) NOT NULL,
KEY `FKovymsahbi6ua0ubka76qbvb90` (`categories_id`),
KEY `FKn42kdeg73kpktyl367aw9ljck` (`items_id`),
CONSTRAINT `FKn42kdeg73kpktyl367aw9ljck`
FOREIGN KEY (`items_id`) REFERENCES `jpa_items` (`id`),
CONSTRAINT `FKovymsahbi6ua0ubka76qbvb90`
FOREIGN KEY (`categories_id`) REFERENCES `jpa_categories` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci