JPA重整ORM山河(l轉)
1.JPA概述JPA(Java Persistence API)作為Java EE 5.0平臺標準的ORM規范,將得到所有Java EE服務器的支持。Sun這次吸取了之前EJB規范慘痛失敗的經歷,在充分吸收現有ORM框架的基礎上,得到了一個易于使用、伸縮性強的ORM規范。從目前的開發社區的反應上看,JPA受到了極大的支持和贊揚,JPA作為ORM領域標準化整合者的目標應該不難實現。
JPA通過JDK 5.0注解或XML描述對象-關系表的映射關系,并將運行期的實體對象持久化到數據庫中,圖 1很好地描述了JPA的結構:
Sun引入新的JPA ORM規范出于兩個原因:其一,簡化現有Java EE和Java SE應用的對象持久化的開發工作;其二,Sun希望整合對ORM技術,實現天下歸一。
JPA由EJB 3.0軟件專家組開發,作為JSR-220實現的一部分。但它不囿于EJB 3.0,你可以在Web應用、甚至桌面應用中使用。JPA的宗旨是為POJO提供持久化標準規范,由此可見,經過這幾年的實踐探索,能夠脫離容器獨立運行,方便開發和測試的理念已經深入人心了。目前Hibernate 3.2、TopLink 10.1.3以及OpenJpa都提供了JPA的實現。
JPA的總體思想和現有Hibernate、TopLink,JDO等ORM框架大體一致。總的來說,JPA包括以下3方面的技術:
? ORM映射元數據,JPA支持XML和JDK 5.0注解兩種元數據的形式,元數據描述對象和表之間的映射關系,框架據此將實體對象持久化到數據庫表中;
? JPA 的API,用來操作實體對象,執行CRUD操作,框架在后臺替我們完成所有的事情,開發者從繁瑣的JDBC和SQL代碼中解脫出來。
? 查詢語言,這是持久化操作中很重要的一個方面,通過面向對象而非面向數據庫的查詢語言查詢數據,避免程序的SQL語句緊密耦合。
2.實體對象
訪問數據庫前,我們總是要設計在應用層承載數據的領域對象(Domain Object),ORM框架將它們持久化到數據庫表中。為了方便后面的講解,我們用論壇應用為例,建立以下的領域對象:
Topic是論壇的主題,而PollTopic是調查性質的論壇主題,它擴展于Topic,一個調查主題擁有多個選項PollOption。這三個領域對象很好地展現了領域對象之間繼承和關聯這兩大核心的關系。這3個領域對象將被映射到數據庫的兩張表中:
其中,Topic及其子類PollTopic將映射到同一張t_topic表中,并用topic_type字段區分兩者。而PollOption映射到t_polloption中。
具有ORM元數據的領域對象稱為實體(Entity),按JPA的規范,實體具備以下的條件:
? 必須使用javax.persistence.Entity注解或者在XML映射文件中有對應的<entity>元素;
? 必須具有一個不帶參的構造函數,類不能聲明為final,方法和需要持久化的屬性也不能聲明為final;
? 如果游離狀的實體對象需要以值的方式進行傳遞,如通Session bean的遠程業務接口傳遞,則必須實現Serializable接口;
? 需要持久化的屬性,其訪問修飾符不能是public,它們必須通過實體類方法進行訪問。
3.使用注解元數據
基本注解
首先,我們對Topic領域對象進行注解,使其成為一個合格的實體類:
代碼清單 1 Topic實體類的注解
package com.baobaotao.domain;
…
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
![]()
@Entity(name = "T_TOPIC") ①
public class Topic implements Serializable ...{
@Id ②-1
@GeneratedValue(strategy = GenerationType.TABLE) ②-2
@Column(name = "TOPIC_ID") ②-3
private int topicId;
![]()
@Column(name = "TOPIC_TITLE", length = 100) ③
private String topicTitle;
![]()
@Column(name = "TOPIC_TIME")
@Temporal(TemporalType.DATE) ④
private Date topicTime;
![]()
@Column(name = "TOPIC_VIEWS")
private int topicViews;
![]()
//省略get/setter方法
}
? @Id :對應的屬性是表的主鍵,如②-1所示;
? @GeneratedValue:主鍵的產生策略,通過strategy屬性指定。默認情況下,JPA自動選擇一個最適合底層數據庫的主鍵生成策略,如SqlServer對應identity,MySql對應auto increment。在javax.persistence.GenerationType中定義了以下幾種可供選擇的策略:
? 1) IDENTITY:表自增鍵字段,Oracle不支持這種方式;
? 2) AUTO: JPA自動選擇合適的策略,是默認選項;
? 3) SEQUENCE:通過序列產生主鍵,通過@SequenceGenerator注解指定序列名,MySql不支持這種方式;
? 4) TABLE:通過表產生主鍵,框架借由表模擬序列產生主鍵,使用該策略可以使應用更易于數據庫移植。不同的JPA實現商生成的表名是不同的,如OpenJPA生成openjpa_sequence_table表Hibernate生成一個hibernate_sequences表,而TopLink則生成sequence表。這些表都具有一個序列名和對應值兩個字段,如SEQ_NAME和SEQ_COUNT。
? @Column(name = "TOPIC_ID"):屬性對應的表字段。我們并不需要指定表字段的類型,因為JPA會根據反射從實體屬性中獲取類型;如果是字符串類型,我們可以指定字段長度,以便可以自動生成DDL語句,如③處所示;
? @Temporal(TemporalType.DATE):如果屬性是時間類型,因為數據表對時間類型有更嚴格的劃分,所以必須指定具體時間類型,如④所示。在javax.persistence.TemporalType枚舉中定義了3種時間類型:
? 1) DATE :等于java.sql.Date
? 2) TIME :等于java.sql.Time
? 3) TIMESTAMP :等于java.sql.Timestamp
? ? 繼承關系
? Topic和PollTopic是父子類,JPA 采用多種方法來支持實體繼承。在父類中必須聲明繼承實體的映射策略,如代碼清單 2所示:
? 代碼清單 2:繼承實體的映射策略
…
@Entity(name = "T_TOPIC")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) ①
@DiscriminatorColumn(name = "TOPIC_TYPE", discriminatorType =
DiscriminatorType.INTEGER, length = 1) ②@DiscriminatorValue(value="1")③
public class Topic implements Serializable ...{
…
}
? SINGLE_TABLE:父子類都保存到同一個表中,通過字段值進行區分。這是我們Topic實體所采用的策略,Topic和PollTopic都保存到同一張表中,通過TOPIC_TYPE字段進行區分,Topic在T_TOPIC表中對應TOPIC_TYPE=1的記錄,而PollTopic對應TOPIC_TYPE=2的記錄(稍后在PollTopic實體中指定);區別的字段通過@DiscriminatorColumn說明,如②所示,區分字段對應該實體的值通過@DiscriminatorValue指定,如③所示;
? JOINED:父子類相同的部分保存在同一個表中,不同的部分分開存放,通過表連接獲取完整數據;
? TABLE_PER_CLASS:每一個類對應自己的表,一般不推薦采用這種方式。
? ? 關聯關系
? 我們再繼續對PollTopic進行注解,進一步了解實體繼承的JPA映射定義:
? 代碼清單 3 PollTopic映射描述
package com.baobaotao.domain;
…
@Entity
@DiscriminatorValue(value="2") ①
public class PollTopic extends Topic ...{②繼承于Topic實體
private boolean multiple; ③
@Column(name = "MAX_CHOICES")
private int maxChoices;
@OneToMany(mappedBy="pollTopic",cascade=CascadeType.ALL) ④
private Set<PollOption> options = new HashSet<PollOption>();
//省略get/setter方法
}
? JPA規范規定任何屬性都默認映射到表中,所以雖然我們沒有給③處的multiple屬性提供注解信息,但JPA將按照默認的規則對該字段進行映射:字段名和屬性名相同,類型相同。如果我們不希望將某個屬性持久化到數據表中,則可以通過@Transient注解顯式指定:
? ? @Transient
? ? private boolean tempProp1;
? 在④處,我們通過@OneToMany指定了一個一對多的關聯關系,一個PollTopic包括多個PollOption對象(我們將在稍后的PollOption中通過ManyToOne描述PollOption和PollTopic的關系,以建立PollTopic和PollOption的雙向關聯關系)。
? @OneToMany中通過mappedBy屬性指定“Many”方類引用“One”方類的屬性名,這里mappedBy="pollTopic"表示PollOption實體擁有一個指定PollTopic的pollTopic屬性。
? 下面,我們來看一下Many方PollOption實體類的映射描述:
? 代碼清單 4 PollOption映射描述
package com.baobaotao.domain;
…
@Entity(name="T_POLL_OPTION")
public class PollOption implements Serializable ...{
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Column(name = "OPTION_ID")
private int optionId;
![]()
@Column(name = "OPTION_ITEM")
private String optionItem;
![]()
@ManyToOne ①
@JoinColumn(name="TOPIC_ID", nullable=false) ②
private PollTopic pollTopic;
}
? 當然也可以通過@OneToOne和@ManyToMany指定一對一和多以多的關系,方法差不多,不再贅述。
Lob字段
在JPA中Lob類型類型的持久化很簡單,僅需要通過特殊的Lob注解就可以達到目的。下面,我們對Post中的Lob屬性類型進行標注:
代碼清單 5 Post:標注Lob類型屬性
package com.baobaotao.domain;
…
import javax.persistence.Basic;
import javax.persistence.Lob;
@Entity(name = "T_POST")
public class Post implements Serializable ...{
…
@Lob ①-1
@Basic(fetch = FetchType.EAGER) ①-2
@Column(name = "POST_TEXT", columnDefinition = "LONGTEXT NOT NULL") ①-3
private String postText;
![]()
@Lob ②-1
@Basic(fetch = FetchType. LAZY) ②-2
@Column(name = "POST_ATTACH", columnDefinition = "BLOB") ②-3
private byte[] postAttach;
…
}
關于JPA注解的更多信息,你可以通過這篇文章進行更加深入的學習: http://www.oracle.com/technology/products/ias/toplink/jpa/resources/toplink-jpa-annotations.html
4.使用XML元數據
除了使用注解提供元數據信息外,JPA也允許我們通過XML提供元數據信息。條條道路通羅馬,路路都是安康道,開發者安全可以根據自己的習慣喜好擇一而從。按照JPA的規范,如果你提供了XML元數據描述信息,它將覆蓋實體類中的注解元數據信息。XML元數據信息以orm.xml命名,放置在類路徑的META-INF目錄下。
JPA盡量讓XML和注解的元數據在描述的結構上相近,降低學習曲線和轉換難度,所以我們在學習注解元數據后,學習XML元數據變得非常簡單。下面,我們給出以上實體的XML描述版本,你可以對照注解的描述進行比較學習:
代碼清單 6 XML元數據配置:orm.xml
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"version="1.0">
①實體對象所在的包
<package>com.baobaotao.domain</package>
<entity class="Topic"> ②Topic實體配置
<table name="T_TOPIC" />
<attributes>
<id name="topicId">
<column name="TOPIC_ID"/>
<generated-value strategy="TABLE" />
</id>
<basic name="topicTitle">
<column name="TOPIC_TITLE" length="30" />
</basic>
<basic name="topicTime">
<column name="TOPIC_TIME" />
<temporal>DATE</temporal>
</basic>
<basic name="topicViews">
<column name="TOPIC_VIEWS" />
</basic>
</attributes>
</entity>
<entity class="PollTopic"> ②PollTopic實體配置
<discriminator-value>2</discriminator-value>
<attributes>
<basic name="maxChoices">
<column name="MAX_CHOICES" />
</basic>
<one-to-many name="options" mapped-by="pollTopic">
<cascade>
<cascade-all/>
</cascade>
</one-to-many>
</attributes>
</entity>
<entity class="PollOption"> ②PollOption實體配置
<table name="T_POLL_OPTION" />
<attributes>
<id name="optionId">
<column name="OPTION_ID" />
<generated-value strategy="TABLE" />
</id>
<basic name="optionItem">
<column name="OPTION_ITEM"/>
</basic>
<many-to-one name="pollTopic" >
<join-column name="TOPIC_ID" nullable="false"/>
</many-to-one>
</attributes>
</entity>
<entity class="Post"> ②Post實體配置
<table name="T_POST" />
<attributes>
<id name="postId">
<column name="POST_ID" />
<generated-value strategy="TABLE" />
</id>
<basic name="postText" fetch="EAGER">
<column name="POST_TEXT" column-definition="LONGTEXT NOT NULL"/>
<lob/>
</basic>
<basic name="postAttach" fetch="LAZY">
<column name="POST_ATTACH" column-definition="BLOB"/>
<lob/>
</basic>
</attributes>
</entity>
</entity-mappings>
5.JPA的編程結構及重要的API
JavaEE 5.0中所定義的JPA接口個數并不多,它們位于javax.persistence和javax.persistence.spi兩個包中。javax.persistence包中大部分API都是注解類,除此之外還包括EntityManager、Query等持久化操作接口。而javax.persistence.spi包中的4個API,是JPA的服務層接口。下面,我們就來認識一下這些重要的接口。
EntityManager的類型
實體對象由實體管理器進行管理,JPA使用javax.persistence.EntityManager代表實體管理器。實體管理器和持久化上下文關聯,持久化上下文是一系列實體的管理環境,我們通過EntityManager和持久化上下文進行交互。
有兩種類型的實體管理器:
? 容器型:容器型的實體管理器由容器負責實體管理器之間的協作,在一個JTA事務中,一個實體管理器的持久化上下文的狀態會自動廣播到所有使用EntityManager的應用程序組件中。Java EE應用服務器提供的就是管理型的實體管理器;
? 應用程序型:實體管理器的生命周期由應用程序控制,應用程序通過javax.persistence.EntityManagerFactory的createEntityManager創建EntityManager實例。
EntityManager的創建過程
javax.persistence.spi.PersistenceProvider接口由JPA的實現者提供,該接口由啟動者調用,以便創建一個EntityManagerFactory實例。它定義了創建一個EntityManagerFactory實例的方法:
EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map)
javax.persistence.spi.PersistenceUnitInfo入參提供了創建實體管理器所需要的所有信息,這些信息根據JPA的規范,必須放置在META-INF/persistence.xml文件中。
PersistenceUnitInfo接口擁有了一個void addTransformer(ClassTransformer transformer)方法,通過該方式可以添加一個javax.persistence.spi.ClassTransformer,并通過PersistenceProvider開放給容器,以便容器在實體類文件加載到JVM之前進行代碼的增強,使元數據生效。JPA廠商負責提供ClassTransformer接口的實現。
圖 4描述了創建EntityManager的過程:
實體的狀態
實體對象擁有以下4個狀態,這些狀態通過調用EntityManager接口方法發生遷移:
? 新建態:新創建的實體對象,尚未擁有持久化主鍵,沒有和一個持久化上下文關聯起來。
? 受控態:已經擁有持久化主鍵并和持久化上下文建立了聯系;
? 游離態:擁有持久化主鍵,但尚未和持久化上下文建立聯系;
? 刪除態:擁有持久化主鍵,已經和持久化上下文建立聯系,但已經被安排從數據庫中刪除。
EntityManager 的API
下面是EntityManager的一些主要的接口方法:
? void persist(Object entity)
通過調用EntityManager的persist()方法,新實體實例將轉換為受控狀態。這意謂著當persist()方法所在的事務提交時,實體的數據將保存到數據庫中。如果實體已經被持久化,那么調用persist()操作不會發生任何事情。如果對一個已經刪除的實體調用persist()操作,刪除態的實體又轉變為受控態。如果對游離狀的實體執行persist()操作,將拋出IllegalArgumentException。
在一個實體 上調用persist()操作,將廣播到和實體關聯的實體上,執行相應的級聯持久化操作;
? void remove(Object entity)
通過調用remove()方法刪除一個受控的實體。如果實體聲明為級聯刪除(cascade=REMOVE 或者cascade=ALL ),被關聯的實體也會被刪除。在一個新建狀態的實體上調用remove()操作,將被忽略。如果在游離實體上調用remove()操作,將拋出IllegalArgumentException,相關的事務將回滾。如果在已經刪除的實體上執行remove()操作,也會被忽略;
? void flush()
將受控態的實體數據同步到數據庫中;
? <T> T merge(T entity)
將一個游離態的實體持久化到數據庫中,并轉換為受控態的實體;
? <T> T find(Class<T> entityClass, Object primaryKey)
以主鍵查詢實體對象,entityClass是實體的類,primaryKey是主鍵值,如以下的代碼查詢Topic實體:
Topic t = em.find(Topic.class,1);
? Query createQuery(String qlString)
根據JPA的查詢語句創建一個查詢對象Query,如下面的代碼:
Query q= em.createQuery(""SELECT t FROM Topic t WHERE t.topicTitle LIKE :topicTitle")");
? Query createNativeQuery(String sqlString)
使用本地數據庫的SQL語句創建一個Query對象,Query通過getResultList()方法執行查詢后,返回一個List結果集,每一行數據對應一個Vector。
Query
JPA使用javax.persistence.Query接口代表一個查詢實例,Query實例由EntityManager通過指定查詢語句構建。該接口擁有眾多執行數據查詢的接口方法:
? Object getSingleResult():執行SELECT查詢語句,并返回一個結果;
? List getResultList() :執行SELECT查詢語句,并返回多個結果;
? Query setParameter(int position, Object value):通過參數位置號綁定查詢語句中的參數,如果查詢語句使用了命令參數,則可以使用Query setParameter(String name, Object value)方法綁定命名參數;
? Query setMaxResults(int maxResult):設置返回的最大結果數;
? int executeUpdate():如果查詢語句是新增、刪除或更改的語句,通過該方法執行更新操作;
6.JPA的查詢語言
JPA的查詢語言是面向對象而非面向數據庫的,它以面向對象的自然語法構造查詢語句,可以看成是Hibernate HQL的等價物。
簡單的查詢
你可以使用以下語句返回所有Topic對象的記錄:
SELECT t FROM Topic t
t表示Topic的別名,在Topic t是Topic AS t的縮寫。
如果需要按條件查詢Topic,我們可以使用:
SELECT DISTINCT t FROM Topic t WHERE t.topicTitle = ?1
通過WHERE指定查詢條件,?1表示用位置標識參數,爾后,我們可以通過Query的setParameter(1, "主題1")綁定參數。而DISTINCT表示過濾掉重復的數據。
如果需要以命名綁定綁定數據,可以改成以下的方式:
SELECT DISTINCT t FROM Topic t WHERE t.topicTitle = :title
這時,需要通過Query的setParameter("title", "主題1")綁定參數。
關聯查詢:從One的一方關聯到Many的一方
返回PollOptions對應的PollTopic對象,可以使用以下語句:
SELECT DISTINCT p FROM PollTopic p, IN(p.options) o WHERE o.optionItem LIKE ?1
這個語法和SQL以及HQL都有很大的區別,它直接實體屬性連接關聯的實體,這里我們通過PollTopic的options屬性關聯到PollOption實體上,對應的SQL語句為:
SELECT DISTINCT t0.TOPIC_ID, t0.TOPIC_TYPE, t0.TOPIC_TITLE, t0.TOPIC_TIME, t0.TOPIC_VIEWS, t0.MULTIPLE, t0.MAX_CHOICES FROM T_TOPIC t0, T_POLL_OPTION t1 WHERE (((t1.OPTION_ITEM LIKE ?) AND (t0.TOPIC_TYPE = ?)) AND (t1.TOPIC_ID = t0.TOPIC_ID))
該查詢語句的另外兩種等價的寫法分別是:
SELECT DISTINCT p FROM PollTopic p JOIN p.options o WHERE o.optionItem LIKE ?1
和
SELECT DISTINCT p FROM PollTopic p WHERE p.options.optionItem LIKE ?1
關聯查詢:從Many的一方關聯到One的一方
從Many一方關聯到One一方的查詢語句和前面所講的也很相似。如我們希望查詢某一個調查主題下的所示調查項,則可以編寫以下的查詢語句:
SELECT p FROM PollOption p JOIN p.pollTopic t WHERE t.topicId = :topicId
對應的SQL語句為:
SELECT t0.OPTION_ID, t0.OPTION_ITEM, t0.TOPIC_ID FROM T_POLL_OPTION t0, T_TOPIC t1 WHERE ((t1.TOPIC_ID = ?) AND ((t1.TOPIC_ID = t0.TOPIC_ID) AND (t1.TOPIC_TYPE = ?)))
使用其它的關系操作符
使用空值比較符,比如查詢附件不空的所有帖子對象:
SELECT p FROM Post p WHERE p.postAttach IS NOT NULL
范圍比較符包括BETWEEN..AND和>、>= 、<、<=、<>這些操作符。比如下面的語句查詢瀏覽次數在100到200之間的所有論壇主題:
SELECT t FROM Topic t WHERE t.topicViews BETWEEN 100 AND 200
集合關系操作符
和其它實體是One-to-Many或Many-to-Many關系的實體,通過集合引用關聯的實體,我們可以通過集合關系操作符進行數據查詢。下面的語句返回所有沒有選項的調查論壇的主題:
SELECT t FROM PollTopic t WHERE t.options IS EMPTY
我們還可以通過判斷元素是否在集合中進行查詢:
SELECT t FROM PollTopic t WHERE :option MEMBER OF t.options
這里參數必須綁定一個PollOption的對象,JPA會自動將其轉換為主鍵比較的SQL語句。
子查詢
JPA可以進行子查詢,并支持幾個常見的子查詢函數:EXISTS、ALL、ANY。如下面的語句查詢出擁有6個以上選項的調查主題:
SELECT t FROM PollTopic t WHERE (SELECT COUNT(o) FROM t.options o) > 6
可用函數
JPA查詢支持一些常見的函數,其中可用的字符串操作函數有:
? CONCAT(String, String):合并字段串;
? LENGTH(String):求字段串的長度;
? LOCATE(String, String [, start]):查詢字段串的函數,第一個參數為需要查詢的字段串,看它在出現在第二個參數字符串的哪個位置,start表示從哪個位置開始查找,返回查找到的位置,沒有找到返回0。如LOCATE ('b1','a1b1c1',1)返回為3;
? SUBSTRING(String, start, length):子字段串函數;
? TRIM([[LEADING|TRAILING|BOTH] char) FROM] (String):將字段串前后的特殊字符去除,可以通過選擇決定具體的去除位置和字符;
? LOWER(String):將字符串轉為小寫;
? UPPER(String):將字符串轉為大寫。
數字操作函數有:
? ABS(number):求絕對值函數;
? MOD(int, int):求模的函數;
? SQRT(double):求平方函數;
? SIZE(Collection):求集合大小函數。
更改語句
可以用EntityManager進行實體的更新操作,也可以通過查詢語言執行數據表的字段更新,記錄刪除的操作:
下面的語句將某一個調查選項更新為新的值:
UPDATE PollOption p SET p.optionItem = :value WHERE p.optionId = :optionId
我們使用Query接口的executeUpdate()方法執行更新。下面的語句刪除一條記錄:
DELETE FROM PollOption p WHERE p.optionId = :optionId
排序和分組
我們還可以對查詢進行排序和分組。如下面的語句查詢出主題的發表時間大于某個時間值,返回的結果按瀏覽量降序排列:
SELECT t FROM Topic t WHERE t.topicTime > :time ORDER BY t.topicViews DESC
下面的語句計算出每個調查主題對應的選項數目:
SELECT COUNT(p),p.pollTopic.topicId FROM PollOption p GROUP BY p.pollTopic.topicId
這里,我們使用了計算數量的聚集函數COUNT(),其它幾個可用的聚集函數分別為:
? AVG:計算平均值,返回類型為double;
? MAX:計算最大值;
? MIN:計算最小值;
? SUM:計算累加和;
我們還可以通過HAVING對聚集結果進行條件過濾:
SELECT COUNT(p),p.pollTopic.topicId FROM PollOption p GROUP BY p.pollTopic.topicId HAVING p.pollTopic.topicId IN(1,2,3)
7.小結
在不久的將來,Sun可能會將JPA作為一個單獨的JSR對待,同時JPA還可能作為Java SE的一部分。不過這些都不太重要,重要的是,我們現在已經可以在脫離容器的情況下、在Java SE應用中使用JPA了。
JPA已經作為一項對象持久化的標準,不但可以獲得Java EE應用服務器的支持,來可以直接在Java SE中使用。開發者將無需在現有多種ORM框架中艱難地選擇,按照Sun的預想,現有ORM框架頭頂的光環將漸漸暗淡,不再具有以往的吸引力。
posted on 2007-05-23 17:32 liaojiyong 閱讀(4147) 評論(0) 編輯 收藏 所屬分類: JPA