@OneToMany@ManyToOne可以相对存在,也可以只存在一方。

@OneToMany

用于表明一对多的关系,如一个支付合约下可以有多个支付单。

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
@Target({METHOD, FIELD}) 
@Retention(RUNTIME)
public @interface OneToMany {

/**
* (Optional) The entity class that is the target
* of the association. Optional only if the collection
* property is defined using Java generics.
* Must be specified otherwise.
*
* <p> Defaults to the parameterized type of
* the collection when defined using generics.
*/
Class targetEntity() default void.class;

/**
* (Optional) The operations that must be cascaded to
* the target of the association.
* <p> Defaults to no operations being cascaded.
*
* <p> When the target collection is a {@link java.util.Map
* java.util.Map}, the <code>cascade</code> element applies to the
* map value.
*/
CascadeType[] cascade() default {};

/** (Optional) Whether the association should be lazily loaded or
* must be eagerly fetched. The EAGER strategy is a requirement on
* the persistence provider runtime that the associated entities
* must be eagerly fetched. The LAZY strategy is a hint to the
* persistence provider runtime.
*/
FetchType fetch() default LAZY;

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

/**
* (Optional) Whether to apply the remove operation to entities that have
* been removed from the relationship and to cascade the remove operation to
* those entities.
* @since 2.0
*/
boolean orphanRemoval() default false;
}
  • targetEntity: 指明对应的Entity类,默认为集合参数上的类型,当集合参数上的类型为泛型时,则必须指定目标Entity类
  • cascade: 级联操作,默认则表示不存在级联
    • CascadeType.PERSIST: 级联保存
    • CascadeType.MERGE: 表示主体更新时,其关联的表也会随之更新
    • CascadeType.REMOVE: 当主体删除时,其关联的表记录也会随之删除
    • CascadeType.REFRESH: 级联刷新,当主体使用EntityManager刷新重新获取最新的对象时,其关联的记录也会随之刷新
    • CascadeType.DETACH: 级联托管,也是游离操作,当要删除一个实体被外键限制时,可以使用该注解,则在主体删除时会默认撤销所有的外键关联
    • CascadeType.ALL: 拥有以上所有级联操作
  • fetch: 表示获取策略
    • LAZY: 当使用到时,才会请求数据库进行查询
    • EAGER: 当主体类查询时,则顺带把其下属的关联记录一并查出
  • mappedBy: 表示该类放弃主键的维护,也就是使用mappedBy的一方是放弃主键的维护,由类自己维护外键关系,当同时存在@OneToMany@ManyToOne时需要指定该字段,内容为One端的变量名称,如果不指示该字段,则程序运行后在数据库会多生成一个关系表
    • mappedBy不能与@JoinColumn或者@JoinTable同时使用
    • mappedBy的值是指另一方的实体里边属性的字段,而不是数据库字段,也不是实体的对象名字,即是另一方配置了@JoinColumn或者@JoinTable注解的属性的字段名称
  • orphanRemoval: 当为true时,表示当主体执行删除操作时,将对下属关联的记录进行级联删除,效果同CascadeType.REMOVE

@ManyToOne

用于表明多对一的关系,如多个支付单可以对应一个支付合约。

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
@Target({METHOD, FIELD}) 
@Retention(RUNTIME)
public @interface ManyToOne {

/**
* (Optional) The entity class that is the target of
* the association.
*
* <p> Defaults to the type of the field or property
* that stores the association.
*/
Class targetEntity() default void.class;

/**
* (Optional) The operations that must be cascaded to
* the target of the association.
*
* <p> By default no operations are cascaded.
*/
CascadeType[] cascade() default {};

/**
* (Optional) Whether the association should be lazily
* loaded or must be eagerly fetched. The EAGER
* strategy is a requirement on the persistence provider runtime that
* the associated entity must be eagerly fetched. The LAZY
* strategy is a hint to the persistence provider runtime.
*/
FetchType fetch() default EAGER;

/**
* (Optional) Whether the association is optional. If set
* to false then a non-null relationship must always exist.
*/
boolean optional() default true;
}
  • optional: 是否返回optional的结果,默认为false,则必须存在此关联关系

@JoinColumn与mappedBy

@JoinColumn注解表示该类是关系的所有者,比如上述的一个支付合约与多个支付单,那么@JoinColumn则应该配合@ManyToOne加在支付单的合约字段上,如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
public class PaymentOrderContract{
@OneToMany(mappedBy = "paymentOrderContract")
private List<PaymentOrder> paymentOrders;
}

@Entity
public class PaymentOrder {
@ManyToOne(fetch = FecthType.LAZY)
@JoinColumn(name = "payment_order_contract_id")
private PaymentOrderContract paymentOrderContract;
}

实际上@JoinColumn也可以同时在两端进行使用,这种情况只限于@OneToMany端为关系的拥有侧,此时就不需要使用mappedBy了,但是需要注意的是,当同时使用时,需要注意在关系维护端的@JoinColumn加上insertable = false, updatable = false,如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
public class PaymentOrderContract {
@OneToMany
@JoinColumn(name = "foreign_key")
private List<PaymentOrder> paymentOrders;
}

@Entity
public class PaymentOrder {
@ManyToOne
@JoinColumn(name = "foreign_key", insertable = false, updatable = false)
private PaymentOrderContract paymentOrderContract;
}

结论

  • 单向绑定 (支付合约 -> 支付单)
    • 仅在支付合约使用@OneToMany配合@JoinColumn
      • 此处@JoinColumn关联的外键字段需要设置为nullable=false或者将数据表中外键字段设置为nullable,因为其底层实际上是执行完insert语句后再次执行update语句更新外键字段
  • 双向绑定 (支付合约 <-> 支付单)
    • 在支付合约使用@OneToMany配合mappedBy字段且在支付单使用@ManyToOne配合@JoinColumn
    • 在支付合约和支付单同时使用@JoinColumn配合@OneToMany@ManyToOne
  • 单向绑定 (支付合约 <- 支付单)
    • 在支付单使用@ManyToOne配合@JoinColumn