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代替。
