技术开发 频道

了解 Java EE 5

  JPA API 的更新包括以下内容:

  类型:实体和表

  实例:Java 对象

  属性:Java 属性和列标注

  依赖对象:嵌入式 Java 对象

  派生属性:瞬态标注

  键属性:标注的字段和键类

  关系:标注和联合列

  约束:标注和数据库

  继承:标注——单个表、联合表和按类表

  类型:实体和表

  JPA 的类型是实体(不再称为实体 Bean),这些实体可以映射到表。映射的主要机制是通过标注实体类。下面是客户 Java 对象和 CUSTOMER 表之间的一个映射示例:

  清单 13

1 @Entity
2
3   @Table(name="CUSTOMER")
4
5   public class Customer implements Serializable {
6
7   ...
8
9  

  类被标注为一个实体并使用客户标注。部署描述符可用作备用或覆盖机制。

  实例:Java 对象

  应用程序在运行时与 Java 对象交互。通过使用称为实体管理器的特殊对象,应用程序可以查询或保持对象。使用 EJB 容器中的依赖项注入将实体管理器注入到应用程序(在 Java SE 环境中也可以通过 EntityManagerFactory 查找)。下面是一个示例:

  清单 14

1 @PersistenceContext (unitName="db2")
2
3   private EntityManager em;
4
5

  应用程序可以检索对象或者将它们传递到实体管理器。下面是使用实体管理器通过主键查找对象的应用程序示例:

  清单 15

1 @PersistenceContext (unitName="db2")
2
3   private EntityManager em;
4
5

  下面是另一个示例,说明创建 Java 对象,并通过将其传递到实体管理器而保存到数据库:

  清单 16

1 CustomerOrder newOrder = new CustomerOrder();
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

1 @Entity(access=FIELD)
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

1 @Embeddable
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

1 @Entity
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

1 @Transient
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

1 @Transient
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

1 public class LineItemId implements Serializable {
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

1 @Entity
2
3   @Table(name="LINEITEM")
4
5   @IdClass(LineItemId.class)
6
7   public class LineItem implements Serializable {
8
9

  您的实体类必须在类上具有匹配字段,或者它可以作为可嵌入键嵌入该键:

  清单 24

1 @Entity
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

1 @Entity
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

1 @Entity
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

  1 public class CustomerOrder implements Serializable {
  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

  1 public class CustomerOrder implements Serializable {
  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

1 @Entity
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

1 @OneToMany(mappedBy="customerOrder", cascade=CascadeType.ALL ,
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

1 @ManyToOne(fetch=FetchType.EAGER,optional=false)
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

1 @Entity
2
3   @Table(
4
5   name="EMPLOYEE",
6
7   uniqueConstraints=
8
9   {@UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})}
10
11   )
12
0
相关文章