Java EE5到底有什么系列 – Java Persistence API 1.0( EJB3 Entity Bean)
作者:黄海波(Charles Huang)
Java EE5作为新一代Java企业开发平台的规范,从开始设计就引来了整个java开发社区的注目,引起无数的辩论和带来了众多的期盼。Java EE5作为J2EE平台诞生几近6年后的第4代规范重点关注的是目前java开发的几个热点:开发效率,运行效率和企业应用整合。目标也是让J2EE开发简单,简单再简单。那我们就看看J2EE5规范到底有什么,是否真的能给开发者/企业带来真正的实惠?
Java EE5规范是一个所谓的雨伞规范(Umbrella),在其下是一系列的子规范,主要包括:
EJB 3.0 (JSR 220) Java Persistence API 1.0 (JSR 220) JSP 2.1 (JSR 245) JSF 1.2 (JSR 252) JAX-WS 2.0 (JSR 224) StAX 1.0 (JSR 173) JAXB 2.0 (JSR 222) Web Services Annotations 1.0 (JSR 181) Common Annotations 1.0 (JSR 250) SAAJ 1.3 maintenance | JTA 1.1 maintenance JavaMail 1.4 & JAF 1.1 maintenance JSTL 1.2 maintenance Java EE Mgmt maintenance JACC maintenance Servlet maintenance Java EE Deployment maintenance WSEE maintenance |
Java Persistence API 1.0( EJB3 Entity Bean) 在Java EE5中, Entity Bean做为EJB规范中负责持久化的组件将逐渐成为一个历史名词了,作为J2EE 4规范中最为人所垢病的Entity Bean在Java EE5中被推到重来,取而代之的是java开发的通用持久化规范Java Persistence API 1.0, 其实就是完全重新定义了的Entity Bean规范(目前在很多场合中,由于历史原因我们仍然使用ejb3持久化来称呼这个规范)。JPA作为java中负责关系数据持久化的组件已经完全独立出来成为一个单独的规范,而不再属于Enterprise Java Bean的范畴(EJB更多的是指Stateless/Stateful session bean和Message Driven Bean)。
Java Persistence AP(JPA)可以说是java持久化技术的一个集大成者,它吸取了Hiberante,JDO,TopLink等优秀技术和框架,将这几年发展成熟起来的基于POJO模型的O/R Mapping技术标准化,成为在J2EE和J2SE环境中通用的java持久化API。值得注意的是Java Persistence API并不是J2EE环境专用,而是在java中的通用API。意味着我们可以在任何需要访问关系数据库的地方使用JPA,甚至包括swing开发的桌面应用。JPA也不要求一定在J2EE容器中才能运行,而是任何有JVM的环境都可以运用。 这就使得我们可以很容易的把JPA作为一个持久化组件自由的和各种容器/框架(EJB3容器, Spring等等)组合。
JPA如何简化原来EJB2中Entity Bean的开发,看一个简单对比:
| EJB2.0 | EJB3.0(JPA) |
Business Interface | public inerface HelloWold extends EJBLocalObject{
Public String getResult();
}
| 无需定义接口 |
映射配置文件 | 编写EJB3 Deployment descriptor | 可选 |
EJB实现 | public class HelloWorldEntityBean
implements HelloWold, EntityBean{
private int id;
private String result;
private EntityContext txt;
public HelloWorldEntityBean(){}
public void setEntityContext( EntityContext text ){
txt = text;
}
public String getResult(){
Return result;
}
public int getId(){
return id;
}
public void setResult( String result ){
this.result = result;
}
public String cretaeByName( String name ) throws EJBException{
.....
}
}
| @Entity
@Table(name=”hellotable”)
public class HelloWoldEntity{
@Id
private int id; p
private String result;
public HelloWoldEntity(){}
public String getResult(){
return result;
}
public int getId(){
return id;
}
public void setResult( String result ){
this.result = result;
}
}
|
在JPA 中,ejb3的Entity Bean就是一个简单的java bean,即POJO( Plain Old Java Object)。不象EJB2中的EntityBean需要跟容器有密切的关联(EJB2中必须有EntityContext),EJB3 中的entityBean和容器无关,事实上在JPA中,EntityBean也不再称为EntityBean,而是Entity,和Session Bean/Message Driven Bean的仍然存在的EJB区别开来。
为了简化O/R Mapping的配置,JPA大量采用JDK1.5的最重要的新特性annotaion直接在java代码中进行配置的标注。 采用annotation标注O/R Mapping配置可以大幅度减少以往使用xml配置O/R Mapping工作量,提高效率和可维护性。
下面是一个最简单的一对一关联关系采用annotation和xml的配置比较。
| Java Persistence API(EJB3 Persistence) | Hiberante |
配置文件 | 可选 | 需要 |
One-To-One配置 | 可选 | <one-to-one
name="address"
class="com.foo.Address"
cascade="All"
lazy="false"/>
|
Java代码 | public class Order{
@OneToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZYL)
Address address;
......
}
| public class Order{
Address address;
......
}
|
采用annotation的优势在于:
- 减少了配置文件的数量,特别是在实体(Entity)比较多的系统中,维护大量的O/R Mapping xml配置文件是不少的工作量。
- 减少了配置需要标注的内容。由于annotation由java compiler来编译解析,很多需要在xml配置中显式声明的内容不再需要(比如变量名称,类型,集合中的对象类型等)。
- Annotation的编译期检查可以避免xml文件中容易出现的配置语法错误,在IDE中及时发现和纠正。
- 无需在xml配置文件和java代码中切换,较少思维的跳跃,提高了开发效率。
- annotation被编译到java bytecode中,省略了xml的解析过程,极大的提升应用的启动速度和内存占用(特别是Entity多的情况)。
JPA在启动上做了很多程度的简化,使我们能够很容易地在容器内(container)和J2SE环境中使用JPA。JPA拥有一个最基本的工厂类EntityManagerFactory。通过调用这个工厂类的createEntityManager()方法获得EntityManager。所有对实体(Entity)的操作包括持久化,查询,删除等等操作都都定义EntityManager上。
public interface EntityManager {
public void persist(Object entity);
public T merge(T entity);
public void remove(Object entity);
public T find(Class entityClass, Object primaryKey);
public T getReference(Class entityClass, Object primaryKey);
public void flush();
public void setFlushMode(FlushModeType flushMode);
public FlushModeType getFlushMode();
public void lock(Object entity, LockModeType lockMode);
public void refresh(Object entity);
public void clear();
public boolean contains(Object entity);
public Query createQuery(String ejbqlString);
public Query createNamedQuery(String name);
public Query createNativeQuery(String sqlString);
public Query createNativeQuery(String sqlString, Class result-
Class);
public Query createNativeQuery(String sqlString, String result-
SetMapping);
public void close();
public boolean isOpen();
public EntityTransaction getTransaction();
}
那又如何获得EntityManagerFactory呢?不管是在J2EE或者J2SE中,都需要通过一个persistence.xml配置文件对EntityMangaerFactory进行配置。下面是一个最简单的persistence.xml的范例。
<entity-manager>
<name>myEntityManager>/name>
<provider>com.redsoft.ejb3.PersistenceProviderImpl>/provider>
<class>com.redsoft.samples.HelloEntityBean>/class>
<properties>
<property name="ConnectionDriverName" value="com.mysql.jdbc.Driver"/>
<property name="ConnectionURL" value="jdbc:mysql://localhost/EJB3Test"/>
<property name="ConnectionUserName" value="ejb3"/>
<property name="ConnectionPassword" value="ejb3"/>
>/properties>
</entity-manager>
}
name – 定了当前这个EntityMangaerFactory的名字,我们可以在一个persistence.xml中定义多个EntityManagerFactory。
Provider – 定了提供EntityManagerFactory的具体实现类。这个实现类由不同的持久化产品开发商提供。例子中采用的是国产红工场的ejb3持久化实现的 EntityManagerFactory实现类。如如果我们需要更换成其他厂商的产品,就需要更换具体的实现类。
class – 列出所有需要被JPA管理的实体类。为了保证在J2SE/J2EE中的通用性和可移植性,JPA要求这里必须列出所有被JPA管理的实体类。
properties – 由持久化厂商自行定义的属性。
如果使用JTA事务,也可以使用
myDataSource定义。
在J2EE容器环境中和J2SE环境中,都是通过读取这个配置文件来初始化EntityMangaerFactory。在J2EE容器环境下,ejb3容器负责读取persistence.xml并初始化EntityManagerFactory,并将EntityManagerFactory帮定到JDNI中,这样我们就可以通过访问JNDI获得EntityManagerFactory, 进而获得EntityManager。由于EJB3容器支持IOC模式,我们也可以通过IOC将EntityMangerFactory直接注射给需要的使用JPA持久化的java类。通过IOC注射的方式获得EntityManagerFactory或者EntityManager是更方便,合理和推荐的方式。
而在J2SE环境中,我们可以通过标准的javax.persistence.Persistence类来获得EntityManagerFactory。Javax.persistence.Persistence会在当前classpath或者jar包的META-INF/下搜索并读persistence.xml后初始化EntityManagerFactory。
下面是一个简单的示例如何在J2SE环境中获得EntityManagerFactory并获得EntityManager,运用EntityManager持久化HelloWorldEntityBean.
public class HelloWorld {
public static void main( final String[] args ){
/*
* Obtain an EJB3 entity manager
*/
final EntityManagerFactory emf = Persistence.createEntityManagerFactory();
final EntityManager entityManager = emf.createEntityManager();
// Construct a HelloEntityBean
final HelloEntityBean hello = new HelloEntityBean( 1, "foo" );
EntityTransaction trans = entityManager.getTransaction();
trans.begin();
entityManager.persist( hello );
trans.commit();
System.out.println( "Successfully persist hello: " + hello );
// Look up the HelloEntityBean by primarky key
final HelloEntityBean anotherHello = entityManager.find( HelloEntityBean.class, new Integer( hello.getId() ) );
System.out.println( "Found hello: " + anotherHello );
// close the EntityManager
entityManager.close();
emf.close();
}
}
事实上不管是在J2SE还是J2EE中我们都可以这样通过javax.persistence.Persistence来初始化EntityManagerFactory。
在上面HelloWorld的例子中我们需要显式调用javax.persistence.Persistence.createEntityManagerFactory, 并且显式地开始事务和关闭事务。在今天大量使用IOC托管容器的时代,这样的编码已经显得落后。
作为J2EE一个部分的JPA自然可以利用EJB3的IOC容器托管事务和注射资源,同样的也可以使用开源IOC容器spring来托管事务和注射资源。红工场也提供了一个开源的spring DAO扩展 http://sourceforge.net/projects/ejb3daosupport 是来支持JPA和Spring的结合。
下面是一个如何在Spring中托管事务和在DAO中注入EntityManager的配置范例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "file://spring-beans.dtd">
<beans>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
<property name="url"><value>jdbc:mysql://localhost/EJB3Test</value></property>
<property name="username"><value>ejb3</value></property>
<property name="password"><value>ejb3</value></property>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.ejb3.LocalEntityManagerFactoryBean">
<property name="persistenceInfo"><ref local="persistenceInfo"/></property>
</bean>
<bean id="persistenceInfo" class="com.redsoft.ejb3.PersistenceInfoImpl">
<property name="nonJtaDataSource"><ref local="dataSource"/></property>
<property name="entityManagerName"><value>myEntityManager</value></property>
<property name="persistenceProviderClassName">
<value>
com.redsoft.ejb3.PersistenceProviderImpl
</value>
</property>
<property name="entityClassNames">
<list>
<value>com.redsoft.ejb3.spring.Child</value>
<value>com.redsoft.ejb3.spring.Father</value>
</list>
</property>
<property name="properties">
<props>
<prop key="javax.jdo.PersistenceManagerFactoryClass">
com.redsoft.jdo.PersistenceManagerFactoryImpl
</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.ejb3.EJB3TransactionManager"
singleton="true">
<property name="entityManagerFactory">
<ref local="entityManagerFactory" />
</property>
</bean>
<bean id="dao"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
singleton="true">
<property name="transactionManager">
<ref local="transactionManager" />
</property>
<property name="target">
<bean class="com.redsoft.ejb3.spring.DAOImpl">
<property name="entityManagerFactory">
<ref local="entityManagerFactory" />
</property>
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="del*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
</beans>
文中的例子可以从红工场主页下载 http://www.redsoftfactory.com/chinese/index.html