Java 持久化/ElementCollection
JPA 2.0 定义了 ElementCollection
映射。它旨在处理几种非标准关系映射。ElementCollection
可用于定义与 Embeddable
对象的 一对多 关系,或 Basic
值(例如 String
集合)。ElementCollection
也可以与 Map
结合使用来定义关系,其中键可以是任何类型的对象,而值是 Embeddable
对象或 Basic
值。
在 JPA 中,ElementCollection
关系通过 @ElementCollection
注解或 <element-collection>
元素定义。
ElementCollection
值始终存储在单独的表中。该表通过 @CollectionTable
注解或 <collection-table>
元素定义。CollectionTable
定义了表的 name
和 @JoinColumn
或 @JoinColumns
(如果使用复合主键)。
ElementCollection
映射可用于定义 Embeddable
对象的集合。这不是 Embeddable
对象的典型用法,因为这些对象不会嵌入到源对象的表中,而是存储在单独的集合表中。这类似于 OneToMany
,只是目标对象是 Embeddable
而不是 Entity
。这允许轻松定义简单对象的集合,而无需要求简单对象定义 Id
或 ManyToOne
逆映射。ElementCollection
还可以覆盖其集合的映射或表,因此您可以让多个实体引用同一个 Embeddable
类,但每个实体将它们相关的对象存储在单独的表中。
使用 ElementCollection
而不是 OneToMany
的局限性在于,目标对象无法独立于其父对象进行查询、持久化、合并。它们严格来说是私有拥有的(依赖)对象,与 Embedded
映射相同。ElementCollection
上没有 cascade
选项,目标对象始终与其父对象一起持久化、合并、删除。ElementCollection
仍然可以使用 fetch
类型,并且默认情况下为 LAZY
,与其他集合映射相同。
EMPLOYEE(表)
EMP_ID | F_NAME | L_NAME | SALARY |
1 | Bob | Way | 50000 |
2 | Joe | Smith | 35000 |
PHONE(表)
OWNER_ID | TYPE | AREA_CODE | P_NUMBER |
1 | home | 613 | 792-0001 |
1 | work | 613 | 494-1234 |
2 | work | 416 | 892-0005 |
@Entity
public class Employee {
@Id
@Column(name="EMP_ID")
private long id;
...
@ElementCollection
@CollectionTable(
name="PHONE",
joinColumns=@JoinColumn(name="OWNER_ID")
)
private List<Phone> phones;
...
}
@Embeddable
public class Phone {
private String type;
private String areaCode;
@Column(name="P_NUMBER")
private String number;
...
}
<entity name="Employee" class="org.acme.Employee" access="FIELD">
<attributes>
<id name="id">
<column name="EMP_ID"/>
</id>
<element-collection name="phones">
<collection-table name="PHONE">
<join-column name="OWNER_ID"/>
</collection-table>
</element-collection>
</attributes>
</entity>
<embeddable name="Phone" class="org.acme.Phone" access="FIELD">
<attributes>
<basic name="number">
<column name="P_NUMBER"/>
</basic>
</attributes>
</embeddable>
ElementCollection
映射可用于定义 Basic
对象的集合。Basic
值存储在单独的集合表中。这类似于 OneToMany
,只是目标是 Basic
值而不是 Entity
。这允许轻松定义简单值的集合,而无需为该值定义类。
ElementCollection
上没有 cascade
选项,目标对象始终与其父对象一起持久化、合并、删除。ElementCollection
仍然可以使用 fetch
类型,并且默认情况下为 LAZY
,与其他集合映射相同。
EMPLOYEE(表)
EMP_ID | F_NAME | L_NAME | SALARY |
1 | Bob | Way | 50000 |
2 | Joe | Smith | 35000 |
PHONE(表)
OWNER_ID | PHONE_NUMBER |
1 | 613-792-0001 |
1 | 613-494-1234 |
2 | 416-892-0005 |
@Entity
public class Employee {
@Id
@Column(name="EMP_ID")
private long id;
...
@ElementCollection
@CollectionTable(
name="PHONE",
joinColumns=@JoinColumn(name="OWNER_ID")
)
@Column(name="PHONE_NUMBER")
private List<String> phones;
...
}
<entity name="Employee" class="org.acme.Employee" access="FIELD">
<attributes>
<id name="id">
<column name="EMP_ID" />
</id>
<element-collection name="phones" target-class="java.lang.String">
<column name="PHONE_NUMBER" />
<collection-table name="PHONE">
<join-column name="OWNER_ID" />
</collection-table>
</element-collection>
</attributes>
</entity>
- JPA 2.0 规范没有提供定义
Embeddable
中Id
的方法。但是,为了删除或更新ElementCollection
映射的元素,通常需要一些唯一的键。否则,JPA 提供者将需要在每次更新时从Entity
的CollectionTable
中删除所有内容,然后再插入值。因此,JPA 提供者很可能会假设Embeddable
中所有字段的组合与外键(JoinColumn
(s))组合是唯一的。然而,如果Embeddable
很大或很复杂,这可能效率低下或不可行。
- 一些 JPA 提供者可能允许在
Embeddable
中指定Id
来解决此问题。请注意,在这种情况下,Id
只需要在集合中是唯一的,而不是在表中是唯一的,因为外键包含在内。一些 JPA 提供者也可能允许使用CollectionTable
上的unique
选项来实现这一点。否则,如果Embeddable
很复杂,您可能需要将其改为Entity
,并使用OneToMany
代替。