持久性对于大多数企业应用程序都非常关键,因为它们需要访问关系数据库(例如Oracle Database 10g)。如果您正在使用Java开发应用程序,您可能需要完成一些常规任务(例如数据库更新和检索),这是通过编写JDBC和SQL来完成的。最近几年,几种对象关系(O-R)映射框架(例如Oracle TopLink、JBoss Hibernate和BEA Kodo)开始流行,因为它们简化了持久性问题,将Java开发人员从编写JDBC代码的工作中解放出来,从而使他们能够将精力集中于业务逻辑。一些Java标准(例如EJB 2.x容器管理持久性(CMP)实体bean)也试图解决持久性挑战,但是不那么成功。
虽然存在多种构建应用程序持久层的选择,但是还没有一种面向Java平台的、在Java EE和Java SE环境下均可使用的持久性标准。好消息是EJB3 Java Persistence API (JPA)(它是EJB 3.0规范JSR-220的一部分)的出现,它标准化了面向Java平台的持久性API。JSR-220为O-R映射供应商(例如TopLink、Hibernate和Kodo)以及其他领先的应用服务器供应商和JDO供应商所广泛接受。EJB3规范提供了一种极有吸引力的选择,用于构建企业Java应用程序的持久层。
在本文中,我将介绍EJB3 Java Persistence API,我将使用一个简单的域对象模型作为示例。
域模型
在构建企业应用程序时,我们首先会设计希望将其保存在数据库中的域对象模型;然后,与数据库设计人员合作,确定数据库模式。域模型表示了持久性对象或实体。实体可以是人、地方或事物,您存储关于它们的数据。它包含数据和行为。富域模型具有所有面向对象的行为特征,例如继承性和多态性。
我们的简单域模型(图1)具有Department与Employee实体之间的双向一对多关系。FullTime和Contractor实体继承自Employee实体。
图1.示例域对象模型
O-R框架和EJB3 JPA基础知识
如果使用过O-R映射框架(例如Oracle TopLink)构建应用程序持久层,您就会注意到,每种框架都提供三种工具:
- 一种声明式地执行O-R映射的方式。这种方法(称为O-R映射元数据)允许将对象映射到一个或多个数据库表。通常,大多数O-R框架使用XML存储O-R映射元数据。
- 一个用于操作实体(例如,执行CRUD操作)的API。此API允许持久化、检索、更新或移除对象。基于API和O-R映射元数据的使用,O-R框架代表开发人员执行数据库操作。此API将开发人员从编写JDBC或SQL代码以持久化域对象的工作中解放出来。
- 一种用于检索对象的查询语言。这是持久性最重要的方面,因为非法的SQL语句可能会降低数据库的速度。此方法也对应用程序屏蔽了混乱地遍布应用程序的的专有SQL。查询语言允许检索实体或对象,并将开发人员从编写SQL SELECT语句的工作中解放出来。
EJB3 Java Persistence API (JPA)提供一种标准O-R映射机制、一个执行CRUD操作的EntityManager API以及一种扩展EJB-QL以检索实体的方式,从而标准化了面向Java平台的持久性的使用。我将在后面讨论这三个方面。
启用元数据注释
Java SE 5.0引入了元数据注释。Java EE的所有组件(包括EJB3 JPA)大量使用元数据注释以简化企业Java开发。要了解关于元数据注释的更多信息,请参阅Kyle Downey所著的Bridging the Gap: J2SE 5.0 Annotations。在EJB3 JPA中,注释可以用于定义对象、关系、O-R映射和持久性上下文的注入。JPA还提供使用XML描述符来代替的选择。我将主要介绍元数据注释的使用,因为它们大大简化了开发。不过,您可能更倾向于在生产部署环境中使用XML描述符,因为可以使用它们重写注释。
标准化JPA中的O-R映射
定义持久对象:实体
实体是轻量级的域对象——您希望将其保存在关系数据库中的Plain Old Java Object (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></employee> employees;
public Department() {
}
...
public Collection<employee></employee> getEmployees() {
return employees;
}
public void setEmployees(Collection<employee></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;
}
}
每个实体都有一个主键;可以在持久字段或属性上使用Id注释将其标记为主键。实体通过使用字段或属性(通过setter和getter方法)来保存其状态。这取决于在哪里使用O-R映射注释。以上示例使用基于字段的访问;我们已经使用了具有deptNo字段的Id注释。要使用基于属性的访问,就要使用属性标记注释(例如Id),如下所示:
@Id
public Long getDeptNo() {
return deptNo;
}
public void setDeptNo(Long deptNo) {
this.deptNo = deptNo;
}
请记住,对一个实体层次结构中的所有实体,必须应用相同的访问类型(字段或属性)。
默认情况下,定义在实体中的每个字段天然就是持久的;如果不希望保存字段/属性的状态,则必须将字段/属性定义为瞬态的,方法是使用@Transient注释或transient修饰符标记它。
可嵌入对象
可嵌入对象是不具有自己标识的持久对象;它是另一个实体的一部分。例如,我们可以假定Address没有自己的标识,且作为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;
...
}
关系
在一个典型的域模型中,实体是彼此关联的,或者它们相互之间存在着关系。两个实体之间的关系可以是一对一、一对多、多对一和多对多的。这些关系可以分别使用OneToOne、OneToMany、ManyToOne或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严重依赖于默认设置,因此如果未定义表映射,则持久性提供程序会假定此实体被映射到与实体类同名的表(在我们的示例中是DEPARTMENT)。如果实体被映射到多个表,则可以使用SecondaryTable注释。
可以使用Column注释将持久字段或属性映射到数据库列,如下所示:
@Column(name="DNAME")
protected String name;
这里,DNAME是持久字段名称所映射到的列的名称。如果未使用Column注释定义O-R映射,则持久性引擎会尝试将其状态保存在列中(使用与字段或属性相同的名称)。
实体继承性
EJB3 JPA支持多种实体继承性方法。它需要两种类型的继承性表映射策略:Single-table-per-entity层次结构策略和Joined-Subclass策略。最好避免使用可选的table-per-class层次结构。
Single-table-per-entity (SINGLE_TABLE)层次结构策略允许将层次结构中的所有实体映射到一个表。在我们的示例中,FullTime和Contractor扩展了Employee,所有这些都可以映射到一个名为EMP的表。换句话说,所有与Employee、FullTime和Contractor有关的数据都被存储于相同的表内。
如果使用Joined Subclass策略,则可以将公共持久数据存储在超类所映射到的表中(例如Employee),并且可以为层次结构中每个子类创建表,以便存储特定于子类的持久字段。
必须在超类中使用Inheritance注释,以指定继承类型,如以下代码所示。此示例展示了使用single-table-per-entity层次结构策略的实体层次结构。
@Entity
@Table(name="EMP")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="EMPLOYEE_TYPE",
discriminatorType=DiscriminatorType.STRING, length=1)
public abstract class Employee implements Serializable {
...
}
每个子类必须指定用于该实体类型的鉴别器值,如下所示:
@Entity
@DiscriminatorValue(value="F")
public class FullTime extends Employee {
@Column(name="SAL")
protected Double salary;
@Column(name="COMM")
protected Double commission;
@Column(name="DESIG")
protected String designation;
...
}
Entity Manager API:用于实体操作的标准API
javax.persistence.EntityManager管理实体生命周期,并公开了多个在实体上执行CRUD操作的方法。
EntityManager API在事务上下文中调用。可以在EJB容器外部(例如,从一个Web应用程序)调用它,而无需会话bean外观。
在执行任何实体操作之前,必须获取EntityManager实例。可以使用容器管理或应用程序管理的实体管理器,可以使用JNDI查找或依赖注入来获取EntityManager实例。正如其名称所暗示的,Java EE容器管理着容器管理实体管理器的生命周期。它可能主要在企业Java应用程序中使用。
可以使用PersistenceContext注入获取容器管理实体管理器实例,如下所示:
@PersistenceContext(unitName="onjava")
private EntityManager em;
如果使用应用程序管理的实体管理器,则必须管理其生命周期。可以创建一个应用程序管理实体管理器实例,如下所示:
@PersistenceUnit(unitName="onjava")
private EntityManagerFactory emf;
private EntityManager em = emf.createEntityManager();
然后可以使用EntityManager实例在实体上执行CRUD操作。要关闭应用程序管理实体管理器实例,请在完成工作后调用em.close()方法。
如前所述,必须在事务上下文中执行涉及任何数据库更改的实体管理器操作。
下表列出了EntityManager接口的一些用于执行实体操作的关键方法。
方法
用途
public void persist(Object entity); |
持久化实体实例。 |
public <T> T merge(T entity); |
合并分离的实体实例。 |
public void remove(Object entity); |
移除实体实例。 |
public <T> T find(Class<T> entityClass, Object primaryKey); |
通过主键检索实体实例。 |
public void flush(); |
使实体状态与数据库同步。 |
可以使用persist()方法持久化实体实例。例如,如果想持久化Contractor实例,请使用以下代码:
@PersistenceContext(unitName="onjava")
private EntityManager em;
...
Contractor pte = new Contractor();
pte.setName("Nistha")
pte.setHourlyRate(new Double(100.0));
em.persist(pte);
在持久化实体时,如果此关系的CascadeType被设置为PERSIST或ALL,则任何对关联实体的状态更改也将被持久化。除非正在使用扩展的持久上下文,否则实体将在事务终止后分离。合并操作允许将分离的实体实例与持久上下文合并;分离实体的状态将与数据库同步。这将有助于摆脱EJB 2.x中常见的数据传输对象(Data Transfer Object,DTO)反模式,因为作为POJO的实体可以在层与层之间传输。惟一的要求是实体类必须实现java.io.Serializable接口。
查询API
对实体的检索是持久性的一个重要方面。使用EJB3 JPA时,使用Java持久化查询语言(Java Persistence Query Language,JPQL)表示查询。JPQL是EJBQL的扩展,它是作为EJB 2.0规范的一部分而引入的。然而,EJB3 JPA解决了EJBQL的所有局限性,并添加了许多新特性,从而成为一种功能强大的查询语言。
JPQL较之EJBQL 2.x的改进
以下是EJB3 JPA中的JPQL的新特性:
- 简化了的查询语法
- JOIN操作
- Group By和Having Clause
- 子查询
- 动态查询
- 指定参数(named parameter)
- 批量更新和删除
此外,如果希望从特定于数据库的查询扩展中获益,则必须对查询实体使用原生(native ) SQL。
动态查询与指定查询
可以使用动态查询或指定查询(named query)。指定查询随实体存储,可从应用程序重用。
要创建动态查询,请使用实体管理器接口的createQuery方法,如下所示:
Query query = em.createQuery(
"select e from Employee e where e.empNo > 1");
query.setParameter(1,100);
return query.getResultList();
如果希望将此查询用作指定查询,请在实体中使用NamedQuery注释,如下所示:
@Entity
@NamedQuery(name="findAllEmployee",
query="select e from Employee e where e.empNo > 1")
public abstract class Employee implements Serializable {
}
要执行指定查询,首先使用EntityManager接口上的createNamedQuery方法创建一个Query实例,如下所示:
query = em.createNamedQuery(" findAllEmployee");
query.setParameter(1,100);
return query.getResultList();
指定参数
可以在EJBQL查询中使用指定参数(named parameter)代替位置参数(positional parameter)。例如,可以将以上查询重写如下:
"select e from Employee e where e.empNo > :empNo "
如果在查询中使用指定参数,则必须设置此参数如下:
query = em.createNamedQuery("findAllEmployee");
query.setParameter("empNo",100);
return query.getResultList();
打包
EJB3 JPA标准化了POJO持久性。因此,实体并不局限于EJB模块;它们能够打包到Web模块、ejb-jar模块、EAR级中的库模块或标准jar文件中。也可以在Java SE中使用实体。必须在包含实体的档案文件中打包描述符(persistence.xml),如下所示:
<persistence>
<persistence-unit name="onjava">
<provider>oracle.toplink.essentials.PersistenceProvider</provider>
<jta-data-source>jdbc/OracleDS</jta-data-source>
...
</persistence-unit>
</persistence>
此描述符标识持久性提供程序、持久单元和持久单元所使用的数据源。正如其名称所暗示的,持久单元是集中管理的实体的集合。如果有一个定义在特定模块中的持久单元,就不需要在persistence.xml中标识实体类;它将由持久性提供程序动态发现。
参考实现
BEA Kodo以及Oracle TopLink的TopLink Essentials都提供了EJB3 JPA的参考实现。它可分别从Open JPA和GlassFish开源项目中得到。
您可以在参考实现服务器或其他任何服从EJB3 JPA的应用服务器上使用本文中的代码。
EJB3 JPA工具
开发工具确实能够帮助构建更好的应用程序,而如果使用XML实现O-R映射,情况可能就不太妙了。Eclipse Dali O-R映射项目,Oracle JDeveloper 10.1.3和BEA Workshop studio之类的工具都支持EJB3 JPA。
结束语
EJB3 Java Persistence API标准化了面向Java平台的持久性API。它通过使用元数据注释和异常配置方法,简化了透明持久性的使用。多种应用服务器已支持EJB3规范(编者注:BEA已经发布WebLogic Server EJB 3.0 Tech Preview )。随着Java EE 5.0和EJB 3.0规范的完成,您将很快看到许多一流的应用服务器和持久性提供程序会实现EJB3 Java Persistence API。您可以使用来自GlassFish项目的 参考实现 来启用EJB3 Persistence。
参考资料
分享到:
相关推荐
《APress Pro EJB 3 Java Persistence API》一书由Mike Keith和Merrick Schincariol共同编写,出版于2006年,是关于Java Persistence API(JPA)与Enterprise JavaBeans(EJB)3的深入研究。本书旨在为读者提供关于...
总之,《Pro EJB 3 Java Persistence API》是Java EE开发者深入学习EJB 3.0和JPA的宝贵资源,它可以帮助读者掌握如何利用JPA简化企业级应用的数据持久化,提升开发效率,并确保应用程序的稳定性和性能。通过阅读本书...
EJB3的持久化特性基于Java Persistence API(JPA),它是Java EE和Java SE中用于对象/关系映射的标准。JPA允许开发者使用面向对象的方式来操作数据库,通过注解(Annotations)和XML配置文件定义对象与数据库表之间...
`ejb3-persistence-api`是Java企业版(Java EE)的一部分,它定义了用于持久化对象到数据库的标准API,即Java Persistence API(JPA)。这个API在ejb3-persistence.jar中实现,提供了强大的ORM(对象关系映射)能力...
`ejb3-persistence.jar`包含了JPA(Java Persistence API)的实现,JPA是一个标准的API,它定义了如何在Java应用程序中管理和持久化对象。JPA通过提供ORM(Object-Relational Mapping)工具,使得开发者可以使用面向...
ejb3-persistence还引入了JPA(Java Persistence API),这是Java EE中的标准持久化API,它提供了一种统一的方式来管理和查询数据。JPA允许开发者使用JPQL(Java Persistence Query Language)进行面向对象的查询,...
本书《Pro.EJB.3.Java Persistence API》深入探讨了如何利用EJB 3.0和Java Persistence API来构建高效且可维护的企业级Java应用程序。通过对这些技术的详细讲解,读者可以更好地理解和应用这些强大的工具来解决实际...
通过本文的介绍,我们可以看到Java Persistence API (JPA) 是一项重要的Java持久化技术,它极大地简化了Java应用程序的数据持久化过程。JPA 提供了一套标准的对象关系映射机制,使得开发者能够在Java EE 和 Java SE ...
Java持久化API(Java Persistence API,简称JPA)是Java平台上的一个标准,用于管理应用程序中的对象和数据库之间的关系,实现了对象关系映射(ORM)。JPA2.0是JPA的第二个主要版本,相较于1.0,它引入了许多新特性...
- **JPA**:Java Persistence API,是一种用于管理Java应用中的持久性和对象/关系映射的标准API。 - **EJB 3.0**:Enterprise JavaBeans 3.0版本,是一个Java企业级应用编程模型的一部分,它简化了Java应用服务器上...
`ejb3-persistence.jar`是EJB3中关于持久化的重要库,它是JPA(Java Persistence API)的一部分。JPA是Java EE平台中的一个规范,用于处理对象-关系映射(ORM),允许开发者以面向对象的方式操作数据库,而无需直接...
Java Persistence API(简称 JPA)是 Java 社区规范 JSR 220 的一部分,它定义了一种对象关系映射工具的标准,允许 Java 开发人员将 Java 应用程序对象持久化到数据库表中。JPA 首次在 EJB 3.0 版本中作为可选特性...
EJB 3是Java平台企业级应用开发的重要组成部分,它提供了对数据库操作的标准化框架,简化了数据存储和检索的复杂性。JPA作为ORM(对象关系映射)解决方案,使得开发者可以使用面向对象的编程模型来处理关系数据库,...
Java™ Persistence API (JPA) 是一种用于管理 Java 应用程序中对象持久化的标准接口。它最初作为简化 Enterprise JavaBeans (EJB) 实体 Bean 的一部分而出现,并逐渐演进成为面向普通 Java 对象 (POJO) 的持久化...
综上所述,EJB 3.0持久性API通过简化ORM,提高了开发效率,并提供了强大的查询和事务管理功能,是Java企业级应用中不可或缺的一部分。理解和熟练运用这些知识点,将有助于构建高效、稳定的企业级系统。
《Apress.Pro.EJB.3.Java.Persistence.API.May.2006》是一部关于企业级JavaBeans(EJB)3.0版本的专著,主要聚焦于Java持久性API(JPA)。EJB 3.0是Java EE平台的一个重要组成部分,它极大地简化了企业级应用的开发...
《Java持久化与Hibernate实战详解》 在Java开发中,数据持久化是一个至关重要的环节,它使得应用程序能够存储和检索数据,即使在程序关闭后也能保持数据的完整性。本示例将聚焦于Java Persistence API(JPA)以及其...
JPA(Java Persistence API)是Java EE 5.0平台中定义的标准对象关系映射(ORM)规范,它允许开发人员将应用程序中的实体对象与数据库中的表格进行映射,并提供了一种统一的方式来管理和持久化这些对象。JPA的设计...