JPA API 的更新包括以下内容:
类型:实体和表
实例:Java 对象
属性:Java 属性和列标注
依赖对象:嵌入式 Java 对象
派生属性:瞬态标注
键属性:标注的字段和键类
关系:标注和联合列
约束:标注和数据库
继承:标注——单个表、联合表和按类表
类型:实体和表
JPA 的类型是实体(不再称为实体 Bean),这些实体可以映射到表。映射的主要机制是通过标注实体类。下面是客户 Java 对象和 CUSTOMER 表之间的一个映射示例:
清单 13
2
3 @Table(name="CUSTOMER")
4
5 public class Customer implements Serializable {
6
7 ...
8
9
类被标注为一个实体并使用客户标注。部署描述符可用作备用或覆盖机制。
实例:Java 对象
应用程序在运行时与 Java 对象交互。通过使用称为实体管理器的特殊对象,应用程序可以查询或保持对象。使用 EJB 容器中的依赖项注入将实体管理器注入到应用程序(在 Java SE 环境中也可以通过 EntityManagerFactory 查找)。下面是一个示例:
清单 14
2
3 private EntityManager em;
4
5
应用程序可以检索对象或者将它们传递到实体管理器。下面是使用实体管理器通过主键查找对象的应用程序示例:
清单 15
2
3 private EntityManager em;
4
5
下面是另一个示例,说明创建 Java 对象,并通过将其传递到实体管理器而保存到数据库:
清单 16
2
3 newOrder.setStatus("OPEN");
4
5 newOrder.setTotal(new Integer(0));
6
7 newOrder.setCustomerId(customerId);
8
9 em.persist(newOrder);
10
11
属性:Java 属性和列标注
属性是类中的 Java 属性。Java 属性可以通过 @column 标注映射到数据库列。共有两种属性映射形式:字段或属性(缺省):
清单 17
2
3 @Table(name="PRODUCT")
4
5 public class Product implements Serializable {
6
7 @Id
8
9 @Column(name="SKU")
10
11 Integer sku;
12
13 @Column(name="DESC")
14
15 String description;
16
17 @Column(name="PRICE")
18
19 Integer cost;
20
21
依赖对象:嵌入式 Java 对象
JPA 支持依赖对象。可以创建一个特殊的可嵌入对象,可以通过使用 @Embeddable 标注类对其进行定义:
清单 18
2
3 public class CustomerAddress {
4
5 private int streetAddress;
6
7 private String city;
8
9 private String state;
10
11 private String country;
12
13 ...
14
15 }
16
17
然后可以将该对象定义为实体类上的一个字段:
清单 19
2
3 @Table(name="CUSTOMER")
4
5 public class Customer {
6
7 private String name;
8
9 private CustomerAddress ca;
10
11 @Embedded
12
13 @AttributeOverrides({
14
15 @AttributeOverride(name="streetAddress", column=@Column("
16
17 STRT_ADD")),
18
19 @AttributeOverride(name="city", column=@Column("CITY"))
20
21 ... //more
22
23 })
24
25 public CustomerAddress getCustomerAddress()
26
27 {
28
29 ...
30
31 }
32
33
使用特殊的属性覆盖,可以将可嵌入类的字段映射到实体中。备用方法是直接将列映射到可嵌入类。
派生属性:瞬态标注
缺省情况下,所有字段在 JPA 中都是持久性的;但是,您可以将一个字段标注为瞬态的,然后可以使用逻辑派生任何字段。下面是执行派生字段的查询:
清单 20
2
3 public Integer getTotal() {
4
5 Query totalLineItems = em.createNamedQuery("getOrderTotal");
6
7 totalLineItems.setParameter("orderId",orderId);
8
9 Integer result = (Integer)totalLineItems.getSingleResult();
10
11 return result;
12
13 }
14
15
键属性:标注的字段和键类
JPA 支持多种主键以及各种键的生成。在下面的简单示例中,您可以使用 @Id 标注将实体上的字段标注为主键:
清单 21
2
3 public Integer getTotal() {
4
5 Query totalLineItems = em.createNamedQuery("getOrderTotal");
6
7 totalLineItems.setParameter("orderId",orderId);
8
9 Integer result = (Integer)totalLineItems.getSingleResult();
10
11 return result;
12
13 }
14
15
还可以为具有组合键的实体创建一个主键类:
清单 22
2
3 private Integer orderId;
4
5 private Integer productId;
6
7 public LineItemId() {
8
9 super();
10
11 // TODO Auto-generated constructor stub
12
13 }
14
15 @Column(name="ORDER_ID")
16
17 public Integer getOrderId() {
18
19 return orderId;
20
21 }
22
23 public void setOrderId(Integer orderId) {
24
25 this.orderId = orderId;
26
27 }
28
29 @Column(name="PRODUCT_ID")
30
31 public Integer getProductId() {
32
33 return productId;
34
35 }
36
37 public void setProductId(Integer productId) {
38
39 this.productId = productId;
40
41 }
42
43 public boolean equals(Object arg0) {
44
45 if (arg0 == this) return true;
46
47 if (!(arg0 instanceof LineItemId)) return false;
48
49 LineItemId other = (LineItemId)arg0;
50
51 if(other.getOrderId().equals(orderId) &&
52
53 other.getProductId().equals(productId))
54
55 {
56
57 return true;
58
59 }
60
61 return false;
62
63 }
64
65 public int hashCode() {
66
67 return orderId + productId.hashCode();
68
69 }
70
71 }
72
73
然后可以使用 @IdClass 标注在实体上定义组合键:
清单 23
2
3 @Table(name="LINEITEM")
4
5 @IdClass(LineItemId.class)
6
7 public class LineItem implements Serializable {
8
9
您的实体类必须在类上具有匹配字段,或者它可以作为可嵌入键嵌入该键:
清单 24
2
3 @Table(name="LINEITEM")
4
5 @IdClass(LineItemId.class)
6
7 public class LineItem implements Serializable {
8
9 private LineItemId lineItemId;
10
11 @EmbeddedId
12
13 public LineItemId getLineItemId()
14
15 {
16
17 return lineItemId;
18
19 }
20
21 ...
22
23
另一个主要增强是支持生成主键。通过使用 @Id 标注的生成属性,可以选择一个不同的策略。例如,您可以选择向 DB2 标识列委派主键生成,如下所示:
清单 25
2
3 @Table(name="LINEITEM")
4
5 @IdClass(LineItemId.class)
6
7 public class LineItem implements Serializable {
8
9 private LineItemId lineItemId;
10
11 @EmbeddedId
12
13 public LineItemId getLineItemId()
14
15 {
16
17 return lineItemId;
18
19 }
20
21 ...
22
23
其他受支持机制包括序列和表生成。
关系:标注和联合列
JPA 对实体之间的关系提供强大的支持。JPA 支持一对一、一对多、多对一和多对多关系。在 JPA 中,关系不是双向的,这与 EJB 2.x 中相同。相反,对象将其他实体声明为成员,并添加标注来定义关系。可以使用特殊属性将关系转换为双向的。下面是具有两个不同关系的 CustomerOrder 类的示例:
清单 26
2
3 @Table(name="CUSTOMER")
4
5 public class Customer implements Serializable {
6
7 private Integer id;
8
9 private String name;
10
11 private CustomerOrder currentOrder;
12
13 @Id
14
15 @Column(name="CUST_ID")
16
17 public Integer getId() {
18
19 return id;
20
21 }
22
23 public void setId(Integer id) {
24
25 this.id = id;
26
27 }
28
29 @Column(name="NAME")
30
31 public String getName() {
32
33 return name;
34
35 }
36
37 public void setName(String name) {
38
39 this.name = name;
40
41 }
42
43 @OneToOne(cascade=CascadeType.ALL , fetch=FetchType.EAGER )
44
45 @JoinColumn(name="OPEN_ORDER_ID",referencedColumnName="ORDER_ID")
46
47 public CustomerOrder getCurrentOrder() {
48
49 return currentOrder;
50
51 }
52
53 public void setCurrentOrder(CustomerOrder currentOrder) {
54
55 this.currentOrder = currentOrder;
56
57 }
58
59 }
60
61
在此关系中,通过在 Customer 类中标注 CustomerOrder 属性,将 Customer 与 CustomerOrder 定义为一对一关系。还定义了 JoinColumn 信息。名称属性在 Customer 类映射到的表上定义外键,参考列定义 Table CustomerOrder 映射到的主键。对关系的任何约束都定义为 @OneToOne 标注的属性。CustomerOrder 类如下所示。CustomerOrder 定义了一个 Customer 属性;不过 CustomerOrder 对象有一个返回到 Customer 的一对多关系。这是因为 Customer 只能有一个当前顺序;实际上,一个客户可以有多个顺序,因此 CustomerOrder 显示了这一点。不过,此处的 mappedBy 属性用于定义由 Customer 类将 CustomerOrder 映射到关系的另一侧。此类如下所示:
清单 27
2
3 private Integer orderId;
4
5 private String status;
6
7 private Integer total;
8
9 private Integer customerId;
10
11 private Collection lineItems;
12
13 private Customer customer;
14
15 @Column(name="CUSTOMER_ID")
16
17 public Integer getCustomerId() {
18
19 return customerId;
20
21 }
22
23 public void setCustomerId(Integer customerId) {
24
25 this.customerId = customerId;
26
27 }
28
29 @Id(generate=GeneratorType.IDENTITY)
30
31 @Column(name="ORDER_ID")
32
33 public Integer getOrderId() {
34
35 return orderId;
36
37 }
38
39 public void setOrderId(Integer orderId) {
40
41 this.orderId = orderId;
42
43 }
44
45 @Column(name="STATUS")
46
47 public String getStatus() {
48
49 return status;
50
51 }
52
53 public void setStatus(String status) {
54
55 this.status = status;
56
57 }
58
59 @Column(name="TOTAL")
60
61 public Integer getTotal() {
62
63 return total;
64
65 }
66
67 public void setTotal(Integer total) {
68
69 this.total = total;
70
71 }
72
73 @ManyToOne(fetch=FetchType.EAGER,optional=false)
74
75 @JoinColumn(name="CUSTOMER_ID",
76
77 referencedColumnName="CUST_ID",insertable=false,updatable=false,
78
79 nullable=false,unique=true)
80
81 public Customer getCustomer() {
82
83 return customer;
84
85 }
86
87 public void setCustomer(Customer customer) {
88
89 this.customer = customer;
90
91 }
92
93 @OneToMany(mappedBy="customerOrder", cascade=CascadeType.ALL ,
94
95 fetch=FetchType.EAGER)
96
97 public Collection getLineItems() {
98
99 return lineItems;
100
101 }
102
103 public void setLineItems(Collection lineItems) {
104
105 this.lineItems = lineItems;
106
107 }
108
109 }
110
111
CustomerOrder 中的另一关系包含 LineItem 对象的集合。这是一个一对多关系。注意,JRE 5 定义泛型的使用,指定集合的类型。此处还使用了特殊的 mappedBy 属性,使关系的另一侧映射成为双向关系。
清单 28
2
3 private Integer orderId;
4
5 private String status;
6
7 private Integer total;
8
9 private Integer customerId;
10
11 private Collection lineItems;
12
13 private Customer customer;
14
15 @Column(name="CUSTOMER_ID")
16
17 public Integer getCustomerId() {
18
19 return customerId;
20
21 }
22
23 public void setCustomerId(Integer customerId) {
24
25 this.customerId = customerId;
26
27 }
28
29 @Id(generate=GeneratorType.IDENTITY)
30
31 @Column(name="ORDER_ID")
32
33 public Integer getOrderId() {
34
35 return orderId;
36
37 }
38
39 public void setOrderId(Integer orderId) {
40
41 this.orderId = orderId;
42
43 }
44
45 @Column(name="STATUS")
46
47 public String getStatus() {
48
49 return status;
50
51 }
52
53 public void setStatus(String status) {
54
55 this.status = status;
56
57 }
58
59 @Column(name="TOTAL")
60
61 public Integer getTotal() {
62
63 return total;
64
65 }
66
67 public void setTotal(Integer total) {
68
69 this.total = total;
70
71 }
72
73 @ManyToOne(fetch=FetchType.EAGER,optional=false)
74
75 @JoinColumn(name="CUSTOMER_ID",
76
77 referencedColumnName="CUST_ID",insertable=false,updatable=false,
78
79 nullable=false,unique=true)
80
81 public Customer getCustomer() {
82
83 return customer;
84
85 }
86
87 public void setCustomer(Customer customer) {
88
89 this.customer = customer;
90
91 }
92
93 @OneToMany(mappedBy="customerOrder", cascade=CascadeType.ALL ,
94
95 fetch=FetchType.EAGER)
96
97 public Collection getLineItems() {
98
99 return lineItems;
100
101 }
102
103 public void setLineItems(Collection lineItems) {
104
105 this.lineItems = lineItems;
106
107 }
108
109 }
110
111
LineItem 类有一个 CustomerOrder 属性。这里定义了一个多对一关系,如上面所示。类似地,LineItem 类与产品对象也具有多对一关系。
另一映射类型是,表可能有一对一关系,但对象模型只有一个对象。换句话说,您希望在跨多个表映射单一对象(在某种程度上与依赖对象相反)。可以通过添加一个或多个辅助表做到这一点。下面是跨 Customer 和 Order 表映射客户对象的一个示例。
清单 29
2
3 @Table(name="CUSTOMER")
4
5 @SecondaryTable(name="ORDER ",
6
7 pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID"))
8
9 public class Customer { ... }
10
11
约束:标注和数据库
JPA 除支持各种数据库约束外,还可以使用它定义对各种关系的约束:
清单 30
2
3 fetch=FetchType.EAGER)
4
5 public Collection getLineItems() {
6
7 return lineItems;
8
9 }
10
11 public void setLineItems(Collection lineItems) {
12
13 this.lineItems = lineItems;
14
15 }
16
17
在本例中显示了级联影响。例如,如果删除了客户,也将删除客户的顺序。下面是一个具有更多约束的示例:
清单 31
2
3 @JoinColumn(name="CUSTOMER_ID", referencedColumnName="CUST_ID",
4
5 insertable=false,updatable=false, nullable=false,unique=true)
6
7 public Customer getCustomer() {
8
9 return customer;
10
11 }
12
13 public void setCustomer(Customer customer) {
14
15 this.customer = customer;
16
17 }
18
19
在上例中,跨外键(也是主键的一部分)定义了多对一关系。在本例中,不允许任何人更新或插入同样标记为唯一和不得为空的列。此关系也指定为不可选。JPA 还可让您在特定实体上定义唯一约束,如下所示:
清单 32
2
3 @Table(
4
5 name="EMPLOYEE",
6
7 uniqueConstraints=
8
9 {@UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})}
10
11 )
12