浅析Spring Data JPA的owning side及foreign key的关系-下

2.1 OneToMany mapping的owning side

对于OneToMany mapping的owning side确认,基本和OneToOne一致,唯一不同的是unidirectional OneToMany mapping,具体情况我们可以到源码中确认。

2.1.1 Bidrection OneToMany的owning side

在OneToMany的关系中,通常我们会将foreign key建在Many的一方,即owning side为Many的一方

从上图中的描述也可以看出,在bidirectional OneToMany关联关系中,mappedBy标识出Customer为non-owning side,因此Order class为OneToMany关联关系的owning side

2.1.2 Unidirection OneToMany的owning side

可能让大家困惑的应该是unidirectional OneToMany关联关系,为何foreign key建在Many的一方,但作为non-owning side的Customer class中没有mappedBy标识,而且还引用(使用)了@JoinColumn注解呢?

首先我们来解释一下为什么unidirectional OneToMany中没有mappedBy标识。

1
2
3
4
5
6
7
8
9
10
11
12
// from @OneToMany source code
public @interface OneToMany {
......

/**
* The field that owns the relationship. Required unless
* the relationship is unidirectional.
*/
String mappedBy() default "";

......
}

从源码解释中,可以看出mappedBy标识不需要在unidiretional OneToMany中显式使用。

接下来我们将解释为什么unidirectional OneToMany会在non-owning side的entity中引用@JoinColumn注解。

2.1.3 浅析@JoinColumn中的source entity和target entity

我们可以看一下@JoinColumn的源码解释

可以看到红色圈的部分,意味着如果是unidirectional OneToMany,那么foreign key应该在target entity中。

此处source entitytarget entity又如何区分呢?

我们可以看到@JoinColumn源码解释中的第一段话:

If the join is for a OneToOne or ManyToOne mapping using a foreign key mapping strategy, the foreign key column is in the table of the source entity or embeddable.

这段话表明OneToOne和ManyToOne mapping中,foreign key会在source entity中,然后我们可以观察之前bidirectional OneToMany mapping的代码:

1
2
3
4
5
6
7
8
9
10
11
// In Customer class:

@OneToMany(cascade=ALL, mappedBy="customer")
public Set<Order> getOrders() { return orders; }

// In Order class:
// foreign key exist in order table

@ManyToOne
@JoinColumn(name="CUST_ID", nullable=false)
public Customer getCustomer() { return customer; }

可以看出foreign key存在于Order entity中,即Order entity为@JoinColumn的source entity

那么也就意味着在ManyToOne mapping中引用(使用) @JoinColumn的entity为source entity(例子中的Order class),而被@JoinColumn注解的Entity为target entity (例子中的Customer class)。


回到我们分析的unidirectional OneToMany的代码例子:

1
2
3
4
5
6
7
// Example 3: Unidirectional One-to-Many association using a foreign key mapping

// In Customer class:

@OneToMany(orphanRemoval=true)
@JoinColumn(name="CUST_ID") // join column is in table for Order
public Set<Order> getOrders() {return orders;}
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
// from @JoinColumn source code

public @interface JoinColumn {

/**
* (Optional) The name of the foreign key column.
* The table in which it is found depends upon the
* context.
*
* <li> If the join is for a unidirectional OneToMany mapping
* using a foreign key mapping strategy, the foreign key is in the
* table of the target entity.
*/
String name() default "";

/**
* (Optional) The name of the column referenced by this foreign
* key column.
*
* <li> When used with a unidirectional OneToMany foreign key
* mapping, the referenced column is in the table of the source
* entity.
*/
String referencedColumnName() default "";
}

根据上述分析,对于unidirectional OneToMany,Customer class为non-owning sideforeign key存在于Order class 中。

那么Customer class为unidirectional OneToMany情况下@JoinColumn的source entity

Order class为unidirectional OneToMany情况下@JoinColumn的target entity

所以我们需要将@JoinColumn加在getOrders()方法上。

到此解释了unidirectional OneToMany中@JoinColumn如此设定的原因。

3. 总结

以上我们分析了OneToOne和OneToMany的owning side设定策略,对于ManyToMany,基本与前面分析的两种mapping一致。

在分析owning side的过程中,会发现源码中有些解释是较为模糊的,比如source entity, taraget entity。这个时候就需要我们动手写一些demo代码去验证,毕竟“实践是检验真理的唯一标准”。