Java EE5到底有什么系列 – Java Persistence API 1.0( EJB3 Entity Bean)
作者:黃海波(Charles Huang)
Java EE5作為新一代Java企業(yè)開發(fā)平臺的規(guī)范,從開始設(shè)計(jì)就引來了整個(gè)java開發(fā)社區(qū)的注目,引起無數(shù)的辯論和帶來了眾多的期盼。Java EE5作為J2EE平臺誕生幾近6年后的第4代規(guī)范重點(diǎn)關(guān)注的是目前java開發(fā)的幾個(gè)熱點(diǎn):開發(fā)效率,運(yùn)行效率和企業(yè)應(yīng)用整合。目標(biāo)也是讓J2EE開發(fā)簡單,簡單再簡單。那我們就看看J2EE5規(guī)范到底有什么,是否真的能給開發(fā)者/企業(yè)帶來真正的實(shí)惠?
Java EE5規(guī)范是一個(gè)所謂的雨傘規(guī)范(Umbrella),在其下是一系列的子規(guī)范,主要包括:
Java Persistence API 1.0( EJB3 Entity Bean) 在Java EE5中, Entity Bean做為EJB規(guī)范中負(fù)責(zé)持久化的組件將逐漸成為一個(gè)歷史名詞了,作為J2EE 4規(guī)范中最為人所垢病的Entity Bean在Java EE5中被推到重來,取而代之的是java開發(fā)的通用持久化規(guī)范Java Persistence API 1.0, 其實(shí)就是完全重新定義了的Entity Bean規(guī)范(目前在很多場合中,由于歷史原因我們?nèi)匀皇褂胑jb3持久化來稱呼這個(gè)規(guī)范)。JPA作為java中負(fù)責(zé)關(guān)系數(shù)據(jù)持久化的組件已經(jīng)完全獨(dú)立出來成為一個(gè)單獨(dú)的規(guī)范,而不再屬于Enterprise Java Bean的范疇(EJB更多的是指Stateless/Stateful session bean和Message Driven Bean)。
Java Persistence AP(JPA)可以說是java持久化技術(shù)的一個(gè)集大成者,它吸取了Hiberante,JDO,TopLink等優(yōu)秀技術(shù)和框架,將這幾年發(fā)展成熟起來的基于POJO模型的O/R Mapping技術(shù)標(biāo)準(zhǔn)化,成為在J2EE和J2SE環(huán)境中通用的java持久化API。值得注意的是Java Persistence API并不是J2EE環(huán)境專用,而是在java中的通用API。意味著我們可以在任何需要訪問關(guān)系數(shù)據(jù)庫的地方使用JPA,甚至包括swing開發(fā)的桌面應(yīng)用。JPA也不要求一定在J2EE容器中才能運(yùn)行,而是任何有JVM的環(huán)境都可以運(yùn)用。 這就使得我們可以很容易的把JPA作為一個(gè)持久化組件自由的和各種容器/框架(EJB3容器, Spring等等)組合。
JPA如何簡化原來EJB2中Entity Bean的開發(fā),看一個(gè)簡單對比:
在JPA 中,ejb3的Entity Bean就是一個(gè)簡單的java bean,即POJO( Plain Old Java Object)。不象EJB2中的EntityBean需要跟容器有密切的關(guān)聯(lián)(EJB2中必須有EntityContext),EJB3 中的entityBean和容器無關(guān),事實(shí)上在JPA中,EntityBean也不再稱為EntityBean,而是Entity,和Session Bean/Message Driven Bean的仍然存在的EJB區(qū)別開來。
為了簡化O/R Mapping的配置,JPA大量采用JDK1.5的最重要的新特性annotaion直接在java代碼中進(jìn)行配置的標(biāo)注。 采用annotation標(biāo)注O/R Mapping配置可以大幅度減少以往使用xml配置O/R Mapping工作量,提高效率和可維護(hù)性。
下面是一個(gè)最簡單的一對一關(guān)聯(lián)關(guān)系采用annotation和xml的配置比較。

采用annotation的優(yōu)勢在于:
那又如何獲得EntityManagerFactory呢?不管是在J2EE或者J2SE中,都需要通過一個(gè)persistence.xml配置文件對EntityMangaerFactory進(jìn)行配置。下面是一個(gè)最簡單的persistence.xml的范例。
name – 定了當(dāng)前這個(gè)EntityMangaerFactory的名字,我們可以在一個(gè)persistence.xml中定義多個(gè)EntityManagerFactory。
Provider – 定了提供EntityManagerFactory的具體實(shí)現(xiàn)類。這個(gè)實(shí)現(xiàn)類由不同的持久化產(chǎn)品開發(fā)商提供。例子中采用的是國產(chǎn)紅工場的ejb3持久化實(shí)現(xiàn)的 EntityManagerFactory實(shí)現(xiàn)類。如如果我們需要更換成其他廠商的產(chǎn)品,就需要更換具體的實(shí)現(xiàn)類。
class – 列出所有需要被JPA管理的實(shí)體類。為了保證在J2SE/J2EE中的通用性和可移植性,JPA要求這里必須列出所有被JPA管理的實(shí)體類。
properties – 由持久化廠商自行定義的屬性。
如果使用JTA事務(wù),也可以使用myDataSource定義。
在J2EE容器環(huán)境中和J2SE環(huán)境中,都是通過讀取這個(gè)配置文件來初始化EntityMangaerFactory。在J2EE容器環(huán)境下,ejb3容器負(fù)責(zé)讀取persistence.xml并初始化EntityManagerFactory,并將EntityManagerFactory幫定到JDNI中,這樣我們就可以通過訪問JNDI獲得EntityManagerFactory, 進(jìn)而獲得EntityManager。由于EJB3容器支持IOC模式,我們也可以通過IOC將EntityMangerFactory直接注射給需要的使用JPA持久化的java類。通過IOC注射的方式獲得EntityManagerFactory或者EntityManager是更方便,合理和推薦的方式。
而在J2SE環(huán)境中,我們可以通過標(biāo)準(zhǔn)的javax.persistence.Persistence類來獲得EntityManagerFactory。Javax.persistence.Persistence會在當(dāng)前classpath或者jar包的META-INF/下搜索并讀persistence.xml后初始化EntityManagerFactory。
下面是一個(gè)簡單的示例如何在J2SE環(huán)境中獲得EntityManagerFactory并獲得EntityManager,運(yùn)用EntityManager持久化HelloWorldEntityBean.事實(shí)上不管是在J2SE還是J2EE中我們都可以這樣通過javax.persistence.Persistence來初始化EntityManagerFactory。
在上面HelloWorld的例子中我們需要顯式調(diào)用javax.persistence.Persistence.createEntityManagerFactory, 并且顯式地開始事務(wù)和關(guān)閉事務(wù)。在今天大量使用IOC托管容器的時(shí)代,這樣的編碼已經(jīng)顯得落后。
作為J2EE一個(gè)部分的JPA自然可以利用EJB3的IOC容器托管事務(wù)和注射資源,同樣的也可以使用開源IOC容器spring來托管事務(wù)和注射資源。紅工場也提供了一個(gè)開源的spring DAO擴(kuò)展 http://sourceforge.net/projects/ejb3daosupport 是來支持JPA和Spring的結(jié)合。
下面是一個(gè)如何在Spring中托管事務(wù)和在DAO中注入EntityManager的配置范例:
文中的例子可以從紅工場主頁下載 http://www.redsoftfactory.com/chinese/index.html
作者:黃海波(Charles Huang)
Java EE5作為新一代Java企業(yè)開發(fā)平臺的規(guī)范,從開始設(shè)計(jì)就引來了整個(gè)java開發(fā)社區(qū)的注目,引起無數(shù)的辯論和帶來了眾多的期盼。Java EE5作為J2EE平臺誕生幾近6年后的第4代規(guī)范重點(diǎn)關(guān)注的是目前java開發(fā)的幾個(gè)熱點(diǎn):開發(fā)效率,運(yùn)行效率和企業(yè)應(yīng)用整合。目標(biāo)也是讓J2EE開發(fā)簡單,簡單再簡單。那我們就看看J2EE5規(guī)范到底有什么,是否真的能給開發(fā)者/企業(yè)帶來真正的實(shí)惠?
Java EE5規(guī)范是一個(gè)所謂的雨傘規(guī)范(Umbrella),在其下是一系列的子規(guī)范,主要包括:
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規(guī)范中負(fù)責(zé)持久化的組件將逐漸成為一個(gè)歷史名詞了,作為J2EE 4規(guī)范中最為人所垢病的Entity Bean在Java EE5中被推到重來,取而代之的是java開發(fā)的通用持久化規(guī)范Java Persistence API 1.0, 其實(shí)就是完全重新定義了的Entity Bean規(guī)范(目前在很多場合中,由于歷史原因我們?nèi)匀皇褂胑jb3持久化來稱呼這個(gè)規(guī)范)。JPA作為java中負(fù)責(zé)關(guān)系數(shù)據(jù)持久化的組件已經(jīng)完全獨(dú)立出來成為一個(gè)單獨(dú)的規(guī)范,而不再屬于Enterprise Java Bean的范疇(EJB更多的是指Stateless/Stateful session bean和Message Driven Bean)。
Java Persistence AP(JPA)可以說是java持久化技術(shù)的一個(gè)集大成者,它吸取了Hiberante,JDO,TopLink等優(yōu)秀技術(shù)和框架,將這幾年發(fā)展成熟起來的基于POJO模型的O/R Mapping技術(shù)標(biāo)準(zhǔn)化,成為在J2EE和J2SE環(huán)境中通用的java持久化API。值得注意的是Java Persistence API并不是J2EE環(huán)境專用,而是在java中的通用API。意味著我們可以在任何需要訪問關(guān)系數(shù)據(jù)庫的地方使用JPA,甚至包括swing開發(fā)的桌面應(yīng)用。JPA也不要求一定在J2EE容器中才能運(yùn)行,而是任何有JVM的環(huán)境都可以運(yùn)用。 這就使得我們可以很容易的把JPA作為一個(gè)持久化組件自由的和各種容器/框架(EJB3容器, Spring等等)組合。
JPA如何簡化原來EJB2中Entity Bean的開發(fā),看一個(gè)簡單對比:
EJB2.0 | EJB3.0(JPA) | |
Business Interface | public inerface HelloWold extends EJBLocalObject{ Public String getResult(); } | 無需定義接口 |
映射配置文件 | 編寫EJB3 Deployment descriptor | 可選 |
EJB實(shí)現(xiàn) | 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就是一個(gè)簡單的java bean,即POJO( Plain Old Java Object)。不象EJB2中的EntityBean需要跟容器有密切的關(guān)聯(lián)(EJB2中必須有EntityContext),EJB3 中的entityBean和容器無關(guān),事實(shí)上在JPA中,EntityBean也不再稱為EntityBean,而是Entity,和Session Bean/Message Driven Bean的仍然存在的EJB區(qū)別開來。
為了簡化O/R Mapping的配置,JPA大量采用JDK1.5的最重要的新特性annotaion直接在java代碼中進(jìn)行配置的標(biāo)注。 采用annotation標(biāo)注O/R Mapping配置可以大幅度減少以往使用xml配置O/R Mapping工作量,提高效率和可維護(hù)性。
下面是一個(gè)最簡單的一對一關(guān)聯(lián)關(guān)系采用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; ...... } |
- 減少了配置文件的數(shù)量,特別是在實(shí)體(Entity)比較多的系統(tǒng)中,維護(hù)大量的O/R Mapping xml配置文件是不少的工作量。
- 減少了配置需要標(biāo)注的內(nèi)容。由于annotation由java compiler來編譯解析,很多需要在xml配置中顯式聲明的內(nèi)容不再需要(比如變量名稱,類型,集合中的對象類型等)。
- Annotation的編譯期檢查可以避免xml文件中容易出現(xiàn)的配置語法錯(cuò)誤,在IDE中及時(shí)發(fā)現(xiàn)和糾正。
- 無需在xml配置文件和java代碼中切換,較少思維的跳躍,提高了開發(fā)效率。
- annotation被編譯到j(luò)ava bytecode中,省略了xml的解析過程,極大的提升應(yīng)用的啟動(dòng)速度和內(nèi)存占用(特別是Entity多的情況)。
public interface EntityManager { public void persist(Object entity); publicT 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中,都需要通過一個(gè)persistence.xml配置文件對EntityMangaerFactory進(jìn)行配置。下面是一個(gè)最簡單的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 – 定了當(dāng)前這個(gè)EntityMangaerFactory的名字,我們可以在一個(gè)persistence.xml中定義多個(gè)EntityManagerFactory。
Provider – 定了提供EntityManagerFactory的具體實(shí)現(xiàn)類。這個(gè)實(shí)現(xiàn)類由不同的持久化產(chǎn)品開發(fā)商提供。例子中采用的是國產(chǎn)紅工場的ejb3持久化實(shí)現(xiàn)的 EntityManagerFactory實(shí)現(xiàn)類。如如果我們需要更換成其他廠商的產(chǎn)品,就需要更換具體的實(shí)現(xiàn)類。
class – 列出所有需要被JPA管理的實(shí)體類。為了保證在J2SE/J2EE中的通用性和可移植性,JPA要求這里必須列出所有被JPA管理的實(shí)體類。
properties – 由持久化廠商自行定義的屬性。
如果使用JTA事務(wù),也可以使用
在J2EE容器環(huán)境中和J2SE環(huán)境中,都是通過讀取這個(gè)配置文件來初始化EntityMangaerFactory。在J2EE容器環(huán)境下,ejb3容器負(fù)責(zé)讀取persistence.xml并初始化EntityManagerFactory,并將EntityManagerFactory幫定到JDNI中,這樣我們就可以通過訪問JNDI獲得EntityManagerFactory, 進(jìn)而獲得EntityManager。由于EJB3容器支持IOC模式,我們也可以通過IOC將EntityMangerFactory直接注射給需要的使用JPA持久化的java類。通過IOC注射的方式獲得EntityManagerFactory或者EntityManager是更方便,合理和推薦的方式。
而在J2SE環(huán)境中,我們可以通過標(biāo)準(zhǔn)的javax.persistence.Persistence類來獲得EntityManagerFactory。Javax.persistence.Persistence會在當(dāng)前classpath或者jar包的META-INF/下搜索并讀persistence.xml后初始化EntityManagerFactory。
下面是一個(gè)簡單的示例如何在J2SE環(huán)境中獲得EntityManagerFactory并獲得EntityManager,運(yùn)用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(); } }
在上面HelloWorld的例子中我們需要顯式調(diào)用javax.persistence.Persistence.createEntityManagerFactory, 并且顯式地開始事務(wù)和關(guān)閉事務(wù)。在今天大量使用IOC托管容器的時(shí)代,這樣的編碼已經(jīng)顯得落后。
作為J2EE一個(gè)部分的JPA自然可以利用EJB3的IOC容器托管事務(wù)和注射資源,同樣的也可以使用開源IOC容器spring來托管事務(wù)和注射資源。紅工場也提供了一個(gè)開源的spring DAO擴(kuò)展 http://sourceforge.net/projects/ejb3daosupport 是來支持JPA和Spring的結(jié)合。
下面是一個(gè)如何在Spring中托管事務(wù)和在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>