扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
尽管有不少选择可以用来来构建应用程序的持久化层,但是并没有一个统一的标准可以用在Java EE环境和Java SE环境中。EJB3Java持久化API为我们带来了好消息,作为EJB 3.0规范(JSR-220)中的一部分,它标准化了Java平台下的持久化API。JSR-220已经被O-R Mapping软件生产商广泛支持,例如TopLink 和 Hibernate,同时它还被一些应用服务器生产商和JDO生产商所支持。EJB3规范为Java企业应用构建持久化层提供了一个强制性的选择。
在这篇文章中,笔者将会使用一个简单的对象模型作为例子来介绍EJB3 Java 持久化 API。
领域模型
当你构建一个企业应用时,你首先设计需要持久化到数据库中的领域模型。然后,你需要和数据库设计人员一起设计好数据库结构。领域模型是持久化对象或者实体的代表。一个实体可以是一个人,一个地方,或者任何其他你想要存储的数据。它同时包含了数据和行为。一个rich领域模型具有所有的OO的特征,例如继承和多态(inheritance and polymorphism)。
我们作为示例使用的这个简单的领域模型如下,部门(Department)和雇员(Employee)实体之间具有双向的一对多关系,而全职员工(FullTime)和承包工(Contractor)实体都是从雇员实体继承而来。
图 1. 示例领域对象模型
O-R映射框架和EJB3 JPA的基础
如果你使用过Oracle TopLink这样的O-R映射框架构建过应用程序的持久化层,那么你会注意到每个持久化框架都会提供3种机制
1. 声明性的O-R 映射方法。这种方法,叫做O-R映射元数据,使得你可以将一个对象映射到数据库中的一个或者多个表。大部分的O-R映射框架都使用XML来存储O-R映射的元数据。
2. 用来操作实体的API(例如,来执行CRUD操作)。API让你用来持久化,获取,更新或者删除对象。基于O-R映射元数据和API的使用,O-R映射框架代替你来完成各种数据库操作。API将你从繁琐的JDBC和SQL代码中解救出来。
3. 一种查询语言来获取对象。这是持久化操作的很重要的一个方面,因为不合适的SQL语句会使得你的数据库操作变慢。这种查询语言避免了在应用程序中混杂大量SQL语句的现象。
EJB3 Java 持久化API标准化了Java平台下的持久化操作,它提供一种标准的O-R映射机制,一组EntityManager API来进行CRUD操作,以及一种扩展的EJB-QL语言来获取实体。笔者将在分别讨论这3个方面。
元数据标注
Java SE 5.0 引入了元数据标注。Java EE的所有组件,包括 EJB3 JPA,都大量使用了元数据标注来简化企业Java应用的开发。想要了解更多的元数据标注方面的知识,请参阅Kyle Downey 写的Bridging the Gap: J2SE 5.0 Annotations文章。在EJB3 JPA中,元数据可以用来定义对象,关系,O-R映射以及持久化上下文(Context)的注入。JPA同样提供了使用XML描述符来提供持久化元数据的方法。而笔者则会重点讲述使用标注的方式,因为这使得开发变得更加简单。但是,在产品部署阶段,你或许会倾向于使用XML描述符,你可以使用XML描述符来覆盖标注定义的持久化行为。
在JPA中将O-R映射标准化
定义持久化对象:实体
一个实体是一个轻量级的领域对象――一个需要持久化到关系数据库中的POJO。与其他的POJO一样,一个实体可以是抽象类或者具体类,而且它可以从其他POJO扩展得到。可以使用javax.persistence.Entity标注将一个POJO标注成一个实体
下面是如何将领域模型中的Department对象变成一个实体的例子:
package onjava; import java.io.Serializable; import java.util.Collection; import javax.persistence.*; @Entity @NamedQuery(name="findAllDepartment", query="select o from Department o") @Table(name="DEPT") public class Department implements Serializable { @Id @Column(nullable=false) protected Long deptNo; @Column(name="DNAME") protected String name; @Column(name="LOC") protected String location; @OneToMany(mappedBy="department") protected Collection<Employee> employees; public Department() { } ... public Collection<Employee> getEmployees() { return employees; } public void setEmployees(Collection<Employee> employees) { this.employees = employees; } public Employee addEmployee(Employee employee) { getEmployees().add(employee); employee.setDepartment(this); return employee; } public Employee removeEmployee(Employee employee) { getEmployees().remove(employee); employee.setDepartment(null); return employee; } } |
每一个实体都有一个主键;可以在一个持久化字段(field)或者属性上使用Id标注来将它作为一个主键。一个实体可以通过使用字段或者属性(通过 setter and getter 方法)来维护自己的状态。这取决于你在何处使用Id标注。上面的例子中采用了基于字段的访问,我们在deptNo字段上使用了Id标注。如果要使用基于属性的访问,你需要在属性上使用Id这样的标注。
@Id public Long getDeptNo() { return deptNo; } public void setDeptNo(Long deptNo) { this.deptNo = deptNo; } |
必须注意,在一个实体继承体系中的所有实体,都必须使用通用的访问类型,或者都使用字段,或者都使用属性。
一个实体中定义的所有字段,默认情况下,都会进行持久化。如果你不想存储某个字段(或者属性),你必须将字段(或者属性)定义成临时的,通过采用@Transient标注或者采用transient修饰符。
嵌入对象
一个嵌入对象是一个自己不具有id的持久化对象。它是另外一个实体的一部分。例如,我们可以假定Address对象没有自己的id,并且它作为Employee实体的一部分进行存储。因此,Address是一个嵌入对象。
你可以采用如下的方法来创建一个嵌入对象
@Embeddable public class Address { protected String streetAddr1; protected String streetAddr2; protected String city; protected String state; .. } |
下面是如何将一个对象嵌入到一个目标对象中
@Entity public class Employee { @Id @GeneratedValue(strategy=GenerationType.AUTO) protected Long id; ... @Embedded protected Address address; ... } |
关系
在一个典型的领域模型中,实体可能互相联系或者存在一定的关系。两个实体之间的关系可能是一对一(one-to-one),一对多(one-to-many),多对一(many-to-one),多对多(many-to-many)。实体之间的这些关系可以分别使用OneToOne, OneToMany, ManyToOne, or ManyToMany标注来描述。我们的示例在Department和Employee实体上采用了双向的OneToMany关系。
既然我们在实体中使用了基于字段的访问,那么我们就在Department实体的关系字段上使用标注,如下:
@OneToMany(mappedBy="department") protected Collection<Employee> employees ; |
对于一个双向的关系来说,你必须指定mappedBy元素,就像上面那样,通过指明拥有这个关系的字段名或者属性名来指出反向的关系如何进行映射。
标准化O-R 映射
你可以使用Java标注或者XML来进行实体的O-R映射的定义。EJB3 JPA定义了几个标注来进行O-R映射,例如Table,SecondaryTable, Column, JoinColumn, 以及 PrimaryKeyJoinColumn等标注。参考EJB3 JPA规范来获取所有标注的信息。
在我们的例子中,你可以使用Table标注来定义该实体应该映射到那个表中,例如:
@Table(name="DEPT") public class Department implements Serializable { |
EJB3 JPA中,各种映射都普遍具有默认值。如果你不定义表映射的话,持久化提供者会假定这个实体将要映射到和实体类类名同名的表中(在这个例子中就是Deparment表)。如果你的实体需要映射到多个表中,你可以使用SecondaryTable标注。
你可以使用Column标注将一个字段或者属性映射到数据库中的一个字段,就像下面这样:
@Column(name="DNAME") protected String name; |
这里,DNAME是需要持久化的字段name要映射的数据库中的字段名。如果你不使用Column定义字段的映射的话,持久化引擎会尝试使用与字段名或者属性名相同的数据库字段名来进行持久化。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者