摘要
Java Persistence API(JPA)和Spring Framework的2.0版本已经引起开发人员广泛的兴趣。本文将考察如何将Spring 2.0和JPA与BEA WebLogic Server一起使用。具体来说,我们将使用Spring和JPA对WebLogic Server的病历示例应用程序进行更新。本文将展示Spring和JPA这个功能强大的组合如何形成基于POJO的简化应用架构的基础。使用的技术主要 包括WebLogic Server 9.1、Spring 2.0和Kodo JPA。
简介
病历示例应用程序(MedRec)是一个综合应用程序,它演示了如何在WebLogic Server中使用多种技术。这些技术包括开源技术(如Spring和Struts)以及WebLogic Server所使用的技术(如Web services、JSP、消息驱动bean和JDBC)。
本文描述在MedRec更新版本中Java Persistence API(JPA)是如何与Spring Framework配合使用的。本文的一个重要目的是向开发人员展示Spring 2.0、WebLogic Server 9.1和Kodo 4.0如何一起使用。通过阅读本文,开发人员将获得对有关使用JPA和Spring 2.0版本中新增的JPA支持的了解。本文还将讨论在企业应用的多个层次中重用JavaBean(POJO)时可能出现的挑战。重用是基于Spring和 JPA的应用架构所带来的重要获益之一。
对于不熟悉Java Persistence API的人来说,JPA是一个指定如何将Java对象保存在数据库中的新增简化API。JPA正在作为EJB 3.0(JSR 220)的一部分而被开发,因为它将取代EJB 2.x实体bean,但是它在J2EE和J2SE应用程序中均可使用。JPA的一个最重要的特性是:它是基于POJO的。JPA还使用Java 5.0注释来简化指定从Java对象到关系数据库的映射的方式。BEA已经宣告建立OpenJPA(一个很快就可用的基于Kodo的开源项目),但是您现在就可以开始使用Kodo 4.0的早期访问版本。
本文将从对Spring中的POJO和数据访问的概述开始。接下来概述MedRec的架构,并对MedRec数据访问层进行更详细的描述。然后我们将详细 考察JPA persistent类,并讨论它们需要遵循的设计模式。最后,我们将介绍Spring和Kodo JPA的XML配置,以便将所有内容联系起来。MedRec的完整源代码可随本文下载。
Spring中的POJO和数据访问
Spring Framework最著名的优点可能是简化了的业务组件开发。熟悉Spring的人都知道,通过使用反向控制(IoC)和面向方面编程(AOP), Spring允许开发人员编写功能强大的业务组件,这些业务组件仍然是常规JavaBean或POJO (Plain Old Java Object)——正如它们通常被称作的那样。
对于需要访问数据库的企业应用程序,Spring也提供了一个框架来简化对封装持久化数据访问的数据访问对象(DAO)的创建。Spring的POJO功能还涉及数据访问领域,因为它们查询和修改的数据访问对象和域模型对象均可以是POJO。
Spring对POJO模式的使用具有一些重要的实际优点。首先,就业务组件而言,POJO减轻了开发人员为实现应用程序而必须做的工作。它不仅减少了代 码行,而且由于这些代码本身就是标准Java,所以它们更简单。其次,使用POJO意味着应用程序调用数据访问对象的其余代码不依赖于特定的持久化技术。 例如,如果应用程序正在使用原始JDBC或JDO,那么使用POJO则会使转换到使用JPA作为持久化解决方案的行为相对容易。
第三个优点涉及域模型对象本身。在MedRec中,域模型对象代表诸如病人、医生和处方之类的实体。由于这些类是POJO,而且不像传统的EJB 2.x实体bean那样依赖于特定的持久化环境,所以这些类可以在多个应用程序代码需要访问域模型的层次中重用。在MedRec中,这包括JSP使用域模 型对象呈现用户界面的Web层,以及使用域模型类作为参数并将类型返回给Web服务方法的Web服务层。
MedRec概览
如果希望全面概览MedRec应用程序的架构及其使用Spring的方式,请阅读Spring Integration with WebLogic Server(中文版,dev2dev,2006年3月)。这篇文章很好地描述了Spring和WebLogic Server之间的常规集成。这里我们将简要讨论MedRec架构,以说明JPA是如何用于MedRec环境的。
图1展示了MedRec的整体架构。在最高层,MedRec实际上是两个独立的J2EE应用(EAR文件):MedRec应用程序和Physician应用程序。所有的数据访问都由MedRec部分完成,所以我们将关注这部分。
图1:MedRec应用程序架构
如图1所示,MedRec应用程序分为几个层:一个包含Web服务的表示层、一个服务层、一个包括数据访问的集成层以及数据库层。作为集成层一部分的 Spring数据访问对象(DAO)主要由服务层的业务对象调用。DAO和服务对象都是由Spring应用程序上下文配置的Spring bean。服务对象使用Spring的声明式事务支持来控制事务。
MedRec数据访问
Medical Records应用程序使用4种不同的DAO,每一种对应于一个需要保存的域模型类——Patient、Prescription、Record和 User。DAO实现类分别被称为JpaPatientDAO、JpaPrescriptionDAO、JpaRecordDAO和 JpaUserDAO,可以在com.bea.medrec.dao.orm包中找到。
让我们更详细地考察Patient DAO,以便弄清楚它是如何工作的。以下是用于处理病历应用程序中病人数据的检索和更新的数据访问对象接口:
public interface PatientDao {
public Patient getById(Integer patientId)
throws DataAccessException;
public List getByEmail(String email)
throws DataAccessException;
public List getByLastName(String lastName)
throws DataAccessException;
public List getByLastNameFirstName(String lastName,
String firstName) throws DataAccessException;
public List getByLastNameWild(String lastName)
throws DataAccessException;
public Patient getBySsn(String ssn)
throws DataAccessException;
public List getByStatus(String status)
throws DataAccessException;
public Patient getByUsername(String username)
throws DataAccessException;
public Patient save(Patient patient)
throws DataAccessException;
public Patient update(Patient patient)
throws DataAccessException;
}
请注意,Patient DAO接口是plain old Java interface(POJI)。它不扩展或导入任何特定于所使用的持久化技术(本例中是JPA)的Java类型。大多数Patient DAO方法执行查询并返回病人列表或单个病人对的。最后两个方法分别用于在数据库中保存新病人的信息或者更新现有病人的信息。
该接口上的每个方法都声明了一个DataAccessException,它是由Spring framework定义的运行时异常。关于DataAccessException,有两件事需要注意。首先,它是运行时异常,所以使用数据访问对象的应 用程序代码不需要像在JDBC和EJB 2.x实体bean的情况下那样使用try-catch块包装每次调用。第二,DataAccessException是有用的,因为它包装底层持久化技 术所使用的特定异常类,从而使应用程序的其他部分独立于持久化层。
关于接口就说这么多了。接下来,让我们考察一下Patient DAO的实现。以下是基类BaseDAO,MedRec中的所有数据访问对象都对它进行扩展:
package com.bea.medrec.dao.orm;
import org.springframework.orm.jpa.support.JpaDaoSupport;
public abstract class BaseDao extends JpaDaoSupport {
...
}
在我们的例子中,这是一个非常简单的类,但是一般来说,它可用于在不同数据访问对象实现上所共有的任何代码。BaseDAO扩展了Spring的JpaDaoSupport类。这是必须的,因为它为我们的DAO实现类提供到Spring的特定于JPA的API的访问。
Patient DAO实现类JpaPatientDao扩展了BaseDao:
public class JpaPatientDao extends BaseDao implements PatientDao {
public Patient getById(Integer pId)
throws DataAccessException {
return getJpaTemplate().find(Patient.class, pId);
}
public List getByLastName(String pLastName)
throws DataAccessException {
List patients = getJpaTemplate().find(
"SELECT p " +
"FROM " + Patient.class.getSimpleName() + " p " +
"WHERE p.lastName LIKE ?1", pLastName);
return (patients.isEmpty())?
Collections.EMPTY_LIST : patients;
}
...
}
此代码示例还展示了两个示范查询方法实现。第一个是getById(),它通过病人的惟一标识字段查找病人。这是通过调用Spring的 JpaTemplate上的专用find方法完成的,它将Patient类和惟一的id作为参数,并返回具有此id的Patient对象。 getByLastName()方法返回具有相似的姓的病人列表。getByLastName()使用JpaTemplate.find()的替代版本, 它将后跟查询参数的EJB QL查询作为参数,并返回匹配查询的对象列表。以下是patient DAO的保存和更新方法:
public class JpaPatientDao extends BaseDao implements PatientDao {
...
public Patient save(Patient pPatient)
throws DataAccessException {
getJpaTemplate().persist(pPatient);
return pPatient;
}
public Patient update(Patient pPatient)
throws DataAccessException {
return getJpaTemplate().merge(pPatient);
}
}
在此patient DAO的保存和更新方法的实现中,save()方法用于将新病人的记录插入数据库,而update()则修改现有的病人记录。每个方法返回一个对新的或更 新后的Patient对象的引用。重点是要理解保存和合并不会立即导致数据库更新。更改通常由JPA缓存直到当前事务提交。然后更改被批量发送到数据库, 但是只要有查询在运行,未决更改也可以常常被刷新。
请注意一件有趣的事,我们的Patient DAO对象对JPA API的依赖很少,因为Spring模板被用于所有数据访问操作,它又内部委托给JPA。事实上,到目前为止,我们看到的对JPA的惟一直接依赖是 getByLastName()方法使用的EJB QL查询字符串。还请注意,虽然早些时候我们将Spring DAO称作POJO,但是这并不完全正确,因为DAO需要扩展Spring的JpaDaoSupport类。然而,重要的是,暴露给应用程序其他部分的接 口DAO是不依赖于JPA或Spring类型的plain old Java interface。
JPA持久化类
到目前为止,我们已经看到一些处理查询和更新的示例数据访问代码,那么持久化类又是什么呢?想想POJO和元数据。但是,持久化POJO需要遵 循许多规则,或者说是设计模式。这些规则部分由JPA指定,而其他的是根据整个应用架构的要求而产生的,正如我们下面将看到的。
首先是一条关于元数据的注意事项。JPA为开发人员提供了几种选项来指定与持久化和对象-关系映射相关的JPA元数据:在外部XML文件中指定、在 Java 5.0注释中指定,或者使用这二者的组合。病历应用程序使用JPA元数据的Java注释。这样做的好处是:元数据与应用该元数据的Java类、字段和方法 一起配置,这样可以帮助理解。如果想使元数据与代码分离,可以使用XML来代替。
让我们考察一下Patient类及其字段声明,看看示例注释JPA类是什么样的:
package com.bea.medrec.domain;
import javax.persistence.*;
import kodo.persistence.jdbc.ElementJoinColumn;
@Entity()
public class Patient implements Serializable
{
@Id()
@GeneratedValue(strategy=IDENTITY)
private Integer id;
private Date dob;
@Column(name = "first_name")
private String firstName;
private String gender;
@Column(name = "last_name")
private String lastName;
@Column(name = "middle_name")
private String middleName;
private String phone;
private String ssn;
@ManyToOne(cascade={PERSIST, MERGE})
@JoinColumn (name="address_id")
private Address address;
@OneToOne(cascade={PERSIST, MERGE})
@JoinColumn (name="email")
private User user;
@OneToMany(tar on getEntity = Prescription.class)
@ElementJoinColumn(name="pat_id")
private Set prescriptions=null;
@OneToMany (targetEntity=Record.class)
@ElementJoinColumn(name="pat_id")
private Set records =null;
...
}
请注意,Patient类使用Entity注释进行注释。这是使用注释的所有持久化类必须遵循的。Entity注释的存在告诉持久化引擎该类是 JPA实体。通常不要求Entity类是公有的,但是MedRec的持久化类需要是公有的,以便Web和Web服务层可以使用它们。
实体类还需要实现Serializable,但是由于Patient类由MedRec struts操作置于HTTP会话中,所以它需要是可串行化的。这样应用程序可以在集群环境中工作,其中会话状态可以在集群中各节点之间进行串行化。关于 实体类的其他要求是:它必须是顶级类且不是final。请参阅JPA规范(JSR220)以获得完整的规则列表。
持久化字段
JPA实现需要在运行时读取和写入实体类的状态(持久化字段)。JPA规范允许实现或者通过直接访问实体字段的方式或者通过调用 JavaBean风格的accessor方法(getter/setter)的方式来进行此操作。使用的访问方式由持久化注释的位置决定。例如,如果注释 位于类的字段上,则使用字段访问。
您可能要问:为什么JPA支持字段和方法两种访问方式呢?答案是灵活性。一些类在它们的公有accessor方法中执行验证。如果由于数据库中的值溢出而 抛出异常,这可能导致JPA实现出现问题。如果类在其setter中执行验证,则最好对字段进行注释。另一方面,如果希望支持虚拟持久化属性之类的功能, 那么就注释accessor方法。
在JPA实体中,所有未使用Transient注释(由JPA定义)进行注释的非瞬态字段都是持久化字段。“持久化”意味着字段被映射到数据库中的列。请 注意,Patient类中的一些持久化字段没有注释。这是由于JPA定义的默认值(如默认的列名称)对于这些字段来说是正确的。如果字段名称与其映射的数 据库列名称不同,则必须使用Column注释来为数据库列指定一个不同的列名称。
每个实体必须具有主键。JPA支持简单(单字段)和复合(多字段)的主键。如果可以,最好一直使用简单主键,并让数据库生成主键值。这是因为,一旦对象被 保存,JPA就不允许更改主键值。由于此原因,病历应用程序中的大多数持久化类(包括Patient类)都使用简单的生成主键。关于具有复合主键的类的示 例,请参考Group类。Id注释用于标记主键字段或标识字段。
GeneratedValue注释用于表示在插入新的Patient时主键值是由数据库生成的。数据库可以以数种方式生成主键值,可使用strategy属性来选择其中一个。Patient类使用标识策略,这意味着id字段应被映射到数据库自动-增量或标识列。
在使用标识策略时,需要注意这对类的equals和hashcode方法所产生的影响。例如,如果equals方法比较id字段的值,请确保使用对象的方 式不会导致equals方法在进行数据库插入操作之前(通常在事务提交时)被调用,因为主键值直到这时才被分配。避免这种情况的一种方法是,(如果类有的 话)在equals方法中使用自然键而不是生成键。例如,Patient Social Security number是可用于Patient equals方法中的很好的自然键。
关系
大多数实体具有与域模型中其他的单个或多个实体的关系,Patient类也不例外。图2展示了Patient类及其关系图。
图2.Patient类关系
JPA使用一对一、多对一、一对多和多对多注释来指定关系及其基数。每个关系字段必须使用这些注释中的一个进行注释。例如,Patient具有与 Address的多对一关系。这意味着许多病人可能具有相同的地址。Patient与User类具有一对一关系,该类包含病人的用户名和密码信息。这意味 着每个Patient与单个的User相关。
Patient具有与Prescriptions和Records的一对多关系,因为特定的病人可以有多个处方,并可能多次看医生(这将导致创建病历)。相反,每个Prescription或Record只可以与一个Patient关联。
级联操作
JPA关系的一个有趣特性是操作的级联。您可能已经熟悉了大多数关系数据库所支持的“级联删除”特性。JPA采用了类似于删除的级联操作的理 念,并将其应用于其他操作(例如插入和更新)。Patient类将插入和关系操作级联到相关的Address和User。在JPA中插入被视为持久化,因 为数据库插入是在对对象持久化时执行的。更新被视作合并,因为更新将对象中的更改合并到当前JPA持久化上下文中。这看上去有些混乱,但是请别担心;只需 记住:在希望更新已经被保存的对象时,要调用合并;当要在数据库中插入新的持久化对象时,要调用持久化。
确定是否对特定操作(例如持久化、合并或移除)进行级联是需要技巧的。在Patient类的情况中,持久化被级联到Address和User,因为应用程 序也需要在插入Patient的地方插入新的Address和User。类似地,在更新Patient的时候,Address和User可能也需要更新。 Prescriptions和Records则不同。应用程序具有显式持久化(以及更新)Prescription或Record的操作,所以不需要将这 些操作从Patient级联到这些类。
您可能还记得前一节中我们曾说多个Patient被允许引用相同的Address——从Patient到Address的关系是多对一。但是, 我们刚刚也说了Patient将插入级联到Address,这导致为数据库中的每位病人创建一个新地址。这是怎么回事呢?您在这里看到的是在 Patient和Address之间使用生成的主键和级联插入的有趣效果。因为用于地址的主键由数据库自动生成,所以每个Address将有惟一的主键, 指向相同实际地址的两个不同Address对象有可能都被插入到数据库中。这对于许多应用程序(包括MedRec)来说没什么问题,但是如果您希望阻止复 制这样的地址,则DAO需要执行一些附加的逻辑。
Web Services注意事项
JPA将MedRec的实体类映射到数据库模式,而WebLogic Server 9.1 Web services实现将其映射到XML,这引发了一些影响实体类设计的其他问题。
JPA支持单向和双向关系,但是如果考察MedRec中的实体类,会看到所有关系都是单向的。这是必要的,因为Web services绑定不支持双向关系。
由于JPA要求Java Collection类型(如java.util.Set)用于多值关系字段,这引发了另一个问题。实践表明9.1中的Web services不支持Collection类型。那么我们如何解决这一问题呢?
不知您是否还记得,对象-关系映射可以在实体的字段或方法上定义。实践表明,JPA的此灵活性非常重要。另一方面,Web services使用类的公有属性accessor方法(getXXX()方法)定义被封送为XML的属性。通过使用字段定义持久化映射以及由类上的方法 定义XML绑定,我们能够将两个映射分离!多值关系为了持久化在内部使用Set,但是可以通过属性访问方法将其暴露为XML绑定能够处理的数组。
为了具体说明,下面是一个来自Record类的执行此转换的属性访问方法示例:
public Prescription[] getPrescriptions() {
if (prescriptions == null)
return null;
return (Prescription[]) prescriptions.toArray(new
Prescription[prescriptions.size()]);
}
此方法在内部访问具有java.util.Set类型的持久化处方字段。此Set被转换为Web services实现能够处理的数组。关于Web services集成的体验使我们对JPA及其与现有应用程序和基础架构集成的能力印象深刻。
现在对持久化类的讨论就结束了。看起来似乎需要遵循许多规则和限制,但事实上大多数JPA只是将好的编码实践具体化了,您可能会发现您没有过多考虑就在遵循它们。
使用DAO实现服务
现在我们看到了如何使用Spring 2.0和JPA实现数据访问对象,接下来让我们快速考察一下使用DAO的MedRec服务对象。以下的代码示例显示了PatientServiceImpl类及其业务方法之一,processNewRegistration方法:
public class PatientServiceImpl
extends BaseService implements PatientService {
protected PatientDao patientDao;
public void setPatientDao(PatientDao patientDao) {
this.patientDao = patientDao;
}
public void processNewRegistration(Registration registration) {
try {
User user = registration.getUser();
Patient patient = registration.getPatient();
patient.setUser(user);
user.setStatus(MedRecConstants.USER_NEW);
patientDao.save(pPatient);
} catch (Exception ex) {
...
}
}
...
}
当新病人使用MedRec Web接口自我注册时会调用processNewRegistration方法。它将保存新病人注册信息的Registration对象作为参数。 ProcessNewRegistration首先从Registration对象获取Patient和User,并初始化它们之间的关系。User对象 包含病人的用户名和密码信息,还保存被设置为USER_NEW的病人状态,因为这是位必须由管理员批准的新病人。然后调用Patient DAO将新的病人插入到数据库。
针对MedRec服务对象对Spring声明式事务进行配置,以便在调用业务方法(如processNewRegistration)之前由Spring 启动事务,并在此方法完成时提交该事务。这确保了DAO作为可自动提交的现有事务的一部分而运行。还请注意Spring所使用的、将对DAO对象的引用注 入服务bean中的setPatientDao方法。
配置
到目前为止,我们已经考察了一些构成此版本的病历应用程序的Java代码。本节将为您介绍Spring和Kodo要求的外部配置。我们从Spring开始。
Spring配置
我们使用一组模块化XML配置文件来配置Spring ApplicationContext。包含数据访问对象配置的配置文件是applicationContext-orm.xml,它位于MedRec安 装目录下的src/medrecEar/APP-INF/classes目录中。以下是applicationContext-orm.xml中声明 Patient DAO的片段:
<bean id="patientDao"
class="com.bea.medrec.dao.orm.JpaPatientDao"
autowire="byType"/>
Spring的JPA DAO具有需要被注入的单一依赖,即JPA EntityManagerFactory。DAO使用此EntityManagerFactory创建执行实际持久化工作的JPA EntityManagers。这里使用Spring自动绑定特性完成绑定,这就是为什么我们在XML中看不到对 EntityManagerFactory的显式依赖。自动绑定特性自动将DAO绑定到基于DAO的EntityManagerFactory属性(继承 自所有MedRec DAO类对其进行扩展的Spring JpaDaoSupport类)类型的EntityManagerFactory。
以下代码是声明创建EntityManagerFactory的Spring工厂bean的片段。由于这里讨论了两个工厂,可能听起来有些混乱,但是请记 住:Spring工厂bean的目的就是提供一个中间层,以便允许我们执行某些自定义代码以创建EntityManagerFactory。此 EntityManagerFactory是DAO对象惟一知道或关心的工厂。
<bean id="entityManagerFactory"
class="com.bea.medrec.utils.KodoEntityManagerFactoryBean">
<property name="jndiName">
<value>kodo-jpa</value>
</property>
</bean>
因为在撰写本文的时候,WebLogic Server不支持与JPA的标准集成,所以EntityManagerFactory通过使用自定义工厂bean而创建。 EntityManagerFactory需要知道在JNDI中的什么地方查找Kodo资源适配器。该信息被配置为Spring工厂bean上的属性,后 者将其传送到EntityManagerFactory。请记住,工厂bean的任务是创建EntityManagerFactory事件并将其返回到 Spring,以使Spring能够将EntityManagerFactory注入DAO bean。用于创建实体管理器工厂实例的工厂bean代码如下所示:
public class KodoEntityManagerFactoryBean
implements FactoryBean, InitializingBean {
private String jndiName = "kodo-jpa";
private EntityManagerFactory entityManagerFactory = null;
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
}
public Object getObject() throws Exception {
return entityManagerFactory;
}
public Class getObjectType() {
return EntityManagerFactory.class;
}
public boolean isSingleton() {
return true;
}
public void afterPropertiesSet() throws Exception {
try {
entityManagerFactory =
KodoPersistence.createEntityManagerFactory(
jndiName, new InitialContext());
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
}
KodoEntityManagerFactoryBean是一个相当简单的Spring工厂bean。在运行时,Spring首先调用默认的 构造函数创建bean实例。然后,Spring调用setJndiName()方法设置Kodo资源适配器的JNDI名称。一旦所有属性被注入, Spring就调用创建EntityManagerFactory实例的afterPropertiesSet()。最后,Spring调用 getObject()来获取将被注入DAO的EntityManagerFactory。
我们已经看到了如何使用Kodo EntityManagerFactory(DAO使用它来完成所有持久化工作)注入Spring DAO。然后Kodo EntityManagerFactory通过JNDI连接到Kodo资源适配器。接下来,我们将考察Kodo资源适配器的配置,但是首先需要知道一点关 于Spring配置的其他内容,这将有助于将所有事情联系起来。
Spring需要被告知使用WebLogic JTA事务管理器来启动和提交事务。此操作在以下的XML片段中完成:
<bean id="transactionManager"
class="org.springframework.transaction.jta.WebLogicJtaTransactionManager">
<property name="transactionManagerName"
value="javax.transaction.TransactionManager"/>
</bean>
这里要明白一件重要的事情,即,需要专门告诉Spring将事务工作委托给WebLogic JTA实现。稍后,我们将看到Kodo也被告知在其配置中使用JTA事务。这将安排好事情以便MedRec服务对象启动WebLogic JTA事务,然后在此事务中调用DAO。随后DAO调用Kodo,它作为现有WebLogic JTA事务的一部分执行数据库读出和写入。
Kodo配置
我们使用Kodo 4.0 EA4,它是Kodo的JPA实现的早期访问版本。在撰写本文的时候,定义JPA的EJB 3.0规范尚未完成,所以Kodo支持JPA的pre-final版本。Kodo作为资源适配器被部署到WebLogic Server。此资源适配器在JNDI中进行注册。资源适配器的配置在标准ra.xml描述符文件中进行,它位于资源适配器RAR文件的META-INF 目录中。
如果考察ra.xml文件,会看到Kodo支持多种配置选项(成熟产品的标志)。然而,示例应用程序仅需要我们修改其中的一小部分。让我们看看示例应用程序需要的属性:
<config-property>
<config-property-name>ConnectionFactory2Name</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>jdbc/MedRecGlobalDataSource
</config-property-value>
</config-property>
<config-property>
<config-property-name>ConnectionFactoryName</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>jdbc/MedRecGlobalDataSourceXA
</config-property-value>
</config-property>
<config-property>
<config-property-name>TransactionMode</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>managed</config-property-value>
</config-property>
<config-property>
<config-property-name>LicenseKey</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>XXXXXXXXXXXXXXXXXXXX</config-property-value>
</config-property>
<config-property>
<config-property-name>PersistentClasses</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>
com.bea.medrec.domain.Patient,com.bea.medrec.domain.Address,
com.bea.medrec.domain.User,com.bea.medrec.domain.Physician,
com.bea.medrec.domain.Prescription,com.bea.medrec.domain.Record,
com.bea.medrec.domain.Group,com.bea.medrec.domain.VitalSigns
</config-property-value>
</config-property>
Kodo需要被告知JDBC数据源的JNDI位置,以便它能够与数据库交互。事实上,Kodo需要知道两个数据源:一个数据源用于处理事务工 作,另一个用于非事务工作,因为Kodo有时访问数据库执行不属于全局事务的工作。您可能已经猜到了,ConnectionFactoryName和 ConnectionFactory2Name属性正是用于此目的。每个属性的值是WebLogic数据源的JNDI名称。请确保 ConnectionFactory2Name引用的数据源不将其连接加入全局JTA事务。
Kodo还需要知道此应用程序是否正在使用JTA管理的事务或本地事务。因为控制事务的MedRec服务bean被配置为使用JTA,所以我们将 TransactionMode属性值设为托管。此外,还需要在资源适配器文件中配置Kodo许可,并列出应用程序将使用的持久化类。用于此用途的属性应 该是自解释的。
如果您曾经考察过JPA规范,您可能听说过persistence.xml文件。这是包含与JPA持久化相关的元数据的标准配置文件。Kodo还支持 persistence.xml文件。然而,在应用服务器环境中使用Kodo的当前早期访问版本时,persistence.xml文件仅包含资源适配器 配置中指定的信息的子集,且只能通过工具(例如Kodo增强程序)使用。
下载
以下下载包括本文所讨论的MedRec版本的所有Java和其他源文件,以及Spring 2.0和Kodo二进制文件及其依赖性:medrec-spring-jpa.zip。您大概需要27MB的磁盘空间来存放下载文件。
结束语
本文详细考察了对Spring 2.0中的新增JPA支持的使用,该特性被用于重新实现MedRec示例应用程序的数据访问层。希望本文为您提供了开始使用这些新API所需的信息。如果 您希望进行更深入地研究,我们建议您考察本文所包含的实际MedRec代码。示例应用程序代码演示了如何以集成方式协同使用WebLogic 9.1、Spring 2.0和Kodo JPA。
我们已经看到新的JPA API易用、直观且具有灵活性。Java注释在降低指定数据库映射所需的元数据数量方面表现突出。虽然JPA是新增的,但是其强大功能足以允许我们继续在 持久化层、Web层和Web服务层中的应用程序中使用域模型类。此重用大大简化了应用架构,并显著降低了开发人员需要编写的Java类的数量。
而Spring 2.0方面提供了一个利用JPA创建数据访问对象的优秀工具。Spring的数据访问架构使得我们可以轻松地在不同持久化技术之间进行转换,而无需重写其他应用程序代码。
参考资料
- BEA WebLogic Server 9.1——用于WebLogic Server的联机文档
- The Spring Framework——Spring Framework官方站点
- SolarMetric Kodo——Kodo官方站点
- JSR 220——Enterprise JavaBeans 3.0,定义JPA的JSR
- Spring Integration with WebLogic Server,作者:Andy Piper等(中文版,dev2dev,2006年3月)
原文出处:http://dev2dev.bea.com/pub/a/2006/03/jpa-spring-medrec.html
![](http://dev2dev.bea.com.cn/images/_.gif)
作者简介 | |
Seth White | Seth White 是BEA的高级工程师,近6年来,他是WebLogic Server工程团队的成员。目前他是WebLogic Server开源团队的成员。 |