1.JPA概述 JPA(Java Persistence API)作為Java EE 5.0平臺標(biāo)準(zhǔn)的ORM規(guī)范,將得到所有Java EE服務(wù)器的支持。Sun這次吸取了之前EJB規(guī)范慘痛失敗的經(jīng)歷,在充分吸收現(xiàn)有ORM框架的基礎(chǔ)上,得到了一個(gè)易于使用、伸縮性強(qiáng)的ORM規(guī)范。從目前的開發(fā)社區(qū)的反應(yīng)上看,JPA受到了極大的支持和贊揚(yáng),JPA作為ORM領(lǐng)域標(biāo)準(zhǔn)化整合者的目標(biāo)應(yīng)該不難實(shí)現(xiàn)。
JPA通過JDK 5.0注解或XML描述對象-關(guān)系表的映射關(guān)系,并將運(yùn)行期的實(shí)體對象持久化到數(shù)據(jù)庫中,圖 1很好地描述了JPA的結(jié)構(gòu):

Sun引入新的JPA ORM規(guī)范出于兩個(gè)原因:其一,簡化現(xiàn)有Java EE和Java SE應(yīng)用的對象持久化的開發(fā)工作;其二,Sun希望整合對ORM技術(shù),實(shí)現(xiàn)天下歸一。
JPA由EJB 3.0軟件專家組開發(fā),作為JSR-220實(shí)現(xiàn)的一部分。但它不囿于EJB 3.0,你可以在Web應(yīng)用、甚至桌面應(yīng)用中使用。JPA的宗旨是為POJO提供持久化標(biāo)準(zhǔn)規(guī)范,由此可見,經(jīng)過這幾年的實(shí)踐探索,能夠脫離容器獨(dú)立運(yùn)行,方便開發(fā)和測試的理念已經(jīng)深入人心了。目前Hibernate 3.2、TopLink 10.1.3以及OpenJpa都提供了JPA的實(shí)現(xiàn)。
JPA的總體思想和現(xiàn)有Hibernate、TopLink,JDO等ORM框架大體一致??偟膩碚f,JPA包括以下3方面的技術(shù):
? ORM映射元數(shù)據(jù),JPA支持XML和JDK 5.0注解兩種元數(shù)據(jù)的形式,元數(shù)據(jù)描述對象和表之間的映射關(guān)系,框架據(jù)此將實(shí)體對象持久化到數(shù)據(jù)庫表中;
? JPA 的API,用來操作實(shí)體對象,執(zhí)行CRUD操作,框架在后臺替我們完成所有的事情,開發(fā)者從繁瑣的JDBC和SQL代碼中解脫出來。
? 查詢語言,這是持久化操作中很重要的一個(gè)方面,通過面向?qū)ο蠖敲嫦驍?shù)據(jù)庫的查詢語言查詢數(shù)據(jù),避免程序的SQL語句緊密耦合。
2.實(shí)體對象
訪問數(shù)據(jù)庫前,我們總是要設(shè)計(jì)在應(yīng)用層承載數(shù)據(jù)的領(lǐng)域?qū)ο螅―omain Object),ORM框架將它們持久化到數(shù)據(jù)庫表中。為了方便后面的講解,我們用論壇應(yīng)用為例,建立以下的領(lǐng)域?qū)ο螅?

Topic是論壇的主題,而PollTopic是調(diào)查性質(zhì)的論壇主題,它擴(kuò)展于Topic,一個(gè)調(diào)查主題擁有多個(gè)選項(xiàng)PollOption。這三個(gè)領(lǐng)域?qū)ο蠛芎玫卣宫F(xiàn)了領(lǐng)域?qū)ο笾g繼承和關(guān)聯(lián)這兩大核心的關(guān)系。這3個(gè)領(lǐng)域?qū)ο髮⒈挥成涞綌?shù)據(jù)庫的兩張表中:

其中,Topic及其子類PollTopic將映射到同一張t_topic表中,并用topic_type字段區(qū)分兩者。而PollOption映射到t_polloption中。
具有ORM元數(shù)據(jù)的領(lǐng)域?qū)ο蠓Q為實(shí)體(Entity),按JPA的規(guī)范,實(shí)體具備以下的條件:
? 必須使用javax.persistence.Entity注解或者在XML映射文件中有對應(yīng)的<entity>元素;
? 必須具有一個(gè)不帶參的構(gòu)造函數(shù),類不能聲明為final,方法和需要持久化的屬性也不能聲明為final;
? 如果游離狀的實(shí)體對象需要以值的方式進(jìn)行傳遞,如通Session bean的遠(yuǎn)程業(yè)務(wù)接口傳遞,則必須實(shí)現(xiàn)Serializable接口;
? 需要持久化的屬性,其訪問修飾符不能是public,它們必須通過實(shí)體類方法進(jìn)行訪問。
3.使用注解元數(shù)據(jù) 基本注解 首先,我們對Topic領(lǐng)域?qū)ο筮M(jìn)行注解,使其成為一個(gè)合格的實(shí)體類:
代碼清單 1 Topic實(shí)體類的注解
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方法
}
? @Entity:將領(lǐng)域?qū)ο髽?biāo)注為一個(gè)實(shí)體,表示需要保存到數(shù)據(jù)庫中,默認(rèn)情況下類名即為表名,通過name屬性顯式指定表名,如①處的name = "T_TOPIC",表示Topic保存到T_TOPIC表中;
? @Id :對應(yīng)的屬性是表的主鍵,如②-1所示;
? @GeneratedValue:主鍵的產(chǎn)生策略,通過strategy屬性指定。默認(rèn)情況下,JPA自動選擇一個(gè)最適合底層數(shù)據(jù)庫的主鍵生成策略,如SqlServer對應(yīng)identity,MySql對應(yīng)auto increment。在javax.persistence.GenerationType中定義了以下幾種可供選擇的策略:
? 1) IDENTITY:表自增鍵字段,Oracle不支持這種方式;
? 2) AUTO: JPA自動選擇合適的策略,是默認(rèn)選項(xiàng);
? 3) SEQUENCE:通過序列產(chǎn)生主鍵,通過@SequenceGenerator注解指定序列名,MySql不支持這種方式;
? 4) TABLE:通過表產(chǎn)生主鍵,框架借由表模擬序列產(chǎn)生主鍵,使用該策略可以使應(yīng)用更易于數(shù)據(jù)庫移植。不同的JPA實(shí)現(xiàn)商生成的表名是不同的,如OpenJPA生成openjpa_sequence_table表Hibernate生成一個(gè)hibernate_sequences表,而TopLink則生成sequence表。這些表都具有一個(gè)序列名和對應(yīng)值兩個(gè)字段,如SEQ_NAME和SEQ_COUNT。
? @Column(name = "TOPIC_ID"):屬性對應(yīng)的表字段。我們并不需要指定表字段的類型,因?yàn)镴PA會根據(jù)反射從實(shí)體屬性中獲取類型;如果是字符串類型,我們可以指定字段長度,以便可以自動生成DDL語句,如③處所示;
? @Temporal(TemporalType.DATE):如果屬性是時(shí)間類型,因?yàn)閿?shù)據(jù)表對時(shí)間類型有更嚴(yán)格的劃分,所以必須指定具體時(shí)間類型,如④所示。在javax.persistence.TemporalType枚舉中定義了3種時(shí)間類型:
? 1) DATE :等于java.sql.Date
? 2) TIME :等于java.sql.Time
? 3) TIMESTAMP :等于java.sql.Timestamp
?
?
繼承關(guān)系 ? Topic和PollTopic是父子類,JPA 采用多種方法來支持實(shí)體繼承。在父類中必須聲明繼承實(shí)體的映射策略,如代碼清單 2所示:
? 代碼清單 2:繼承實(shí)體的映射策略
…
@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 ...{
…
}
? 對于繼承的實(shí)體,在javax.persistence.InheritanceType定義了3種映射策略,:
? SINGLE_TABLE:父子類都保存到同一個(gè)表中,通過字段值進(jìn)行區(qū)分。這是我們Topic實(shí)體所采用的策略,Topic和PollTopic都保存到同一張表中,通過TOPIC_TYPE字段進(jìn)行區(qū)分,Topic在T_TOPIC表中對應(yīng)TOPIC_TYPE=1的記錄,而PollTopic對應(yīng)TOPIC_TYPE=2的記錄(稍后在PollTopic實(shí)體中指定);區(qū)別的字段通過@DiscriminatorColumn說明,如②所示,區(qū)分字段對應(yīng)該實(shí)體的值通過@DiscriminatorValue指定,如③所示;
? JOINED:父子類相同的部分保存在同一個(gè)表中,不同的部分分開存放,通過表連接獲取完整數(shù)據(jù);
? TABLE_PER_CLASS:每一個(gè)類對應(yīng)自己的表,一般不推薦采用這種方式。
?
?
關(guān)聯(lián)關(guān)系 ? 我們再繼續(xù)對PollTopic進(jìn)行注解,進(jìn)一步了解實(shí)體繼承的JPA映射定義:
? 代碼清單 3 PollTopic映射描述
package com.baobaotao.domain;
…
@Entity
@DiscriminatorValue(value="2") ①

public class PollTopic extends Topic ...{②繼承于Topic實(shí)體
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方法
}
? 在①處,通過@DiscriminatorValue將區(qū)分字段TOPIC_TYPE的值為2。由于PollTopic實(shí)體繼承于Topic實(shí)體,其它的元數(shù)據(jù)信息直接從Topic獲得。
? JPA規(guī)范規(guī)定任何屬性都默認(rèn)映射到表中,所以雖然我們沒有給③處的multiple屬性提供注解信息,但JPA將按照默認(rèn)的規(guī)則對該字段進(jìn)行映射:字段名和屬性名相同,類型相同。如果我們不希望將某個(gè)屬性持久化到數(shù)據(jù)表中,則可以通過@Transient注解顯式指定:
? ? @Transient
? ? private boolean tempProp1;
? 在④處,我們通過@OneToMany指定了一個(gè)一對多的關(guān)聯(lián)關(guān)系,一個(gè)PollTopic包括多個(gè)PollOption對象(我們將在稍后的PollOption中通過ManyToOne描述PollOption和PollTopic的關(guān)系,以建立PollTopic和PollOption的雙向關(guān)聯(lián)關(guān)系)。
? @OneToMany中通過mappedBy屬性指定“Many”方類引用“One”方類的屬性名,這里mappedBy="pollTopic"表示PollOption實(shí)體擁有一個(gè)指定PollTopic的pollTopic屬性。
? 下面,我們來看一下Many方PollOption實(shí)體類的映射描述:
? 代碼清單 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;
}
? 在①處通過@ManyToOne描述了PollOption和PollTopic的多對一關(guān)聯(lián)關(guān)系,并通過@JoinColumn指定關(guān)聯(lián)PollTopic實(shí)體所對應(yīng)表的“外鍵”,如②所示。
? 當(dāng)然也可以通過@OneToOne和@ManyToMany指定一對一和多以多的關(guān)系,方法差不多,不再贅述。
Lob字段 在JPA中Lob類型類型的持久化很簡單,僅需要通過特殊的Lob注解就可以達(dá)到目的。下面,我們對Post中的Lob屬性類型進(jìn)行標(biāo)注:
代碼清單 5 Post:標(biāo)注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;
…
}
postText屬性對應(yīng)T_POST表的POST_TEXT字段,該字段的類型是LONTTEXT,并且非空。JPA通過@Lob將屬性標(biāo)注為Lob類型,如①-1和②-1所示。通過@Basic指定Lob類型數(shù)據(jù)的獲取策略,F(xiàn)etchType.EAGER表示非延遲加載,而FetchType. LAZY表示延遲加載,如①-2和②-2所示。通過@Column的columnDefinition屬性指定數(shù)據(jù)表對應(yīng)的Lob字段類型,如①-3和②-3所示。
關(guān)于JPA注解的更多信息,你可以通過這篇文章進(jìn)行更加深入的學(xué)習(xí): http://www.oracle.com/technology/products/ias/toplink/jpa/resources/toplink-jpa-annotations.html
4.使用XML元數(shù)據(jù) 除了使用注解提供元數(shù)據(jù)信息外,JPA也允許我們通過XML提供元數(shù)據(jù)信息。條條道路通羅馬,路路都是安康道,開發(fā)者安全可以根據(jù)自己的習(xí)慣喜好擇一而從。按照J(rèn)PA的規(guī)范,如果你提供了XML元數(shù)據(jù)描述信息,它將覆蓋實(shí)體類中的注解元數(shù)據(jù)信息。XML元數(shù)據(jù)信息以orm.xml命名,放置在類路徑的META-INF目錄下。
JPA盡量讓XML和注解的元數(shù)據(jù)在描述的結(jié)構(gòu)上相近,降低學(xué)習(xí)曲線和轉(zhuǎn)換難度,所以我們在學(xué)習(xí)注解元數(shù)據(jù)后,學(xué)習(xí)XML元數(shù)據(jù)變得非常簡單。下面,我們給出以上實(shí)體的XML描述版本,你可以對照注解的描述進(jìn)行比較學(xué)習(xí):
代碼清單 6 XML元數(shù)據(jù)配置: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">
①實(shí)體對象所在的包
<package>com.baobaotao.domain</package>
<entity class="Topic"> ②Topic實(shí)體配置
<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實(shí)體配置
<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實(shí)體配置
<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實(shí)體配置
<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>
從代碼清單 6中,我們可以看出PollTopic并不需要通過特殊配置指定和Topic的繼承關(guān)系,這些信息將從實(shí)體類反射信息獲取。所以從嚴(yán)格意義上來說,元數(shù)據(jù)信息或XML和實(shí)體類結(jié)構(gòu)信息共同構(gòu)成的。
5.JPA的編程結(jié)構(gòu)及重要的API JavaEE 5.0中所定義的JPA接口個(gè)數(shù)并不多,它們位于javax.persistence和javax.persistence.spi兩個(gè)包中。javax.persistence包中大部分API都是注解類,除此之外還包括EntityManager、Query等持久化操作接口。而javax.persistence.spi包中的4個(gè)API,是JPA的服務(wù)層接口。下面,我們就來認(rèn)識一下這些重要的接口。
EntityManager的類型
實(shí)體對象由實(shí)體管理器進(jìn)行管理,JPA使用javax.persistence.EntityManager代表實(shí)體管理器。實(shí)體管理器和持久化上下文關(guān)聯(lián),持久化上下文是一系列實(shí)體的管理環(huán)境,我們通過EntityManager和持久化上下文進(jìn)行交互。
有兩種類型的實(shí)體管理器: ? 容器型:容器型的實(shí)體管理器由容器負(fù)責(zé)實(shí)體管理器之間的協(xié)作,在一個(gè)JTA事務(wù)中,一個(gè)實(shí)體管理器的持久化上下文的狀態(tài)會自動廣播到所有使用EntityManager的應(yīng)用程序組件中。Java EE應(yīng)用服務(wù)器提供的就是管理型的實(shí)體管理器;
? 應(yīng)用程序型:實(shí)體管理器的生命周期由應(yīng)用程序控制,應(yīng)用程序通過javax.persistence.EntityManagerFactory的createEntityManager創(chuàng)建EntityManager實(shí)例。
EntityManager的創(chuàng)建過程 javax.persistence.spi.PersistenceProvider接口由JPA的實(shí)現(xiàn)者提供,該接口由啟動者調(diào)用,以便創(chuàng)建一個(gè)EntityManagerFactory實(shí)例。它定義了創(chuàng)建一個(gè)EntityManagerFactory實(shí)例的方法:
EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map)
javax.persistence.spi.PersistenceUnitInfo入?yún)⑻峁┝藙?chuàng)建實(shí)體管理器所需要的所有信息,這些信息根據(jù)JPA的規(guī)范,必須放置在META-INF/persistence.xml文件中。
PersistenceUnitInfo接口擁有了一個(gè)void addTransformer(ClassTransformer transformer)方法,通過該方式可以添加一個(gè)javax.persistence.spi.ClassTransformer,并通過PersistenceProvider開放給容器,以便容器在實(shí)體類文件加載到JVM之前進(jìn)行代碼的增強(qiáng),使元數(shù)據(jù)生效。JPA廠商負(fù)責(zé)提供ClassTransformer接口的實(shí)現(xiàn)。
圖 4描述了創(chuàng)建EntityManager的過程:

實(shí)體的狀態(tài) 實(shí)體對象擁有以下4個(gè)狀態(tài),這些狀態(tài)通過調(diào)用EntityManager接口方法發(fā)生遷移:
? 新建態(tài):新創(chuàng)建的實(shí)體對象,尚未擁有持久化主鍵,沒有和一個(gè)持久化上下文關(guān)聯(lián)起來。
? 受控態(tài):已經(jīng)擁有持久化主鍵并和持久化上下文建立了聯(lián)系;
? 游離態(tài):擁有持久化主鍵,但尚未和持久化上下文建立聯(lián)系;
? 刪除態(tài):擁有持久化主鍵,已經(jīng)和持久化上下文建立聯(lián)系,但已經(jīng)被安排從數(shù)據(jù)庫中刪除。
EntityManager 的API
下面是EntityManager的一些主要的接口方法:
? void persist(Object entity)
通過調(diào)用EntityManager的persist()方法,新實(shí)體實(shí)例將轉(zhuǎn)換為受控狀態(tài)。這意謂著當(dāng)persist()方法所在的事務(wù)提交時(shí),實(shí)體的數(shù)據(jù)將保存到數(shù)據(jù)庫中。如果實(shí)體已經(jīng)被持久化,那么調(diào)用persist()操作不會發(fā)生任何事情。如果對一個(gè)已經(jīng)刪除的實(shí)體調(diào)用persist()操作,刪除態(tài)的實(shí)體又轉(zhuǎn)變?yōu)槭芸貞B(tài)。如果對游離狀的實(shí)體執(zhí)行persist()操作,將拋出IllegalArgumentException。
在一個(gè)實(shí)體 上調(diào)用persist()操作,將廣播到和實(shí)體關(guān)聯(lián)的實(shí)體上,執(zhí)行相應(yīng)的級聯(lián)持久化操作;
? void remove(Object entity)
通過調(diào)用remove()方法刪除一個(gè)受控的實(shí)體。如果實(shí)體聲明為級聯(lián)刪除(cascade=REMOVE 或者cascade=ALL ),被關(guān)聯(lián)的實(shí)體也會被刪除。在一個(gè)新建狀態(tài)的實(shí)體上調(diào)用remove()操作,將被忽略。如果在游離實(shí)體上調(diào)用remove()操作,將拋出IllegalArgumentException,相關(guān)的事務(wù)將回滾。如果在已經(jīng)刪除的實(shí)體上執(zhí)行remove()操作,也會被忽略;
? void flush()
將受控態(tài)的實(shí)體數(shù)據(jù)同步到數(shù)據(jù)庫中;
? <T> T merge(T entity)
將一個(gè)游離態(tài)的實(shí)體持久化到數(shù)據(jù)庫中,并轉(zhuǎn)換為受控態(tài)的實(shí)體;
? <T> T find(Class<T> entityClass, Object primaryKey)
以主鍵查詢實(shí)體對象,entityClass是實(shí)體的類,primaryKey是主鍵值,如以下的代碼查詢Topic實(shí)體:
Topic t = em.find(Topic.class,1);
? Query createQuery(String qlString)
根據(jù)JPA的查詢語句創(chuàng)建一個(gè)查詢對象Query,如下面的代碼:
Query q= em.createQuery(""SELECT t FROM Topic t WHERE t.topicTitle LIKE :topicTitle")");
? Query createNativeQuery(String sqlString)
使用本地?cái)?shù)據(jù)庫的SQL語句創(chuàng)建一個(gè)Query對象,Query通過getResultList()方法執(zhí)行查詢后,返回一個(gè)List結(jié)果集,每一行數(shù)據(jù)對應(yīng)一個(gè)Vector。
Query JPA使用javax.persistence.Query接口代表一個(gè)查詢實(shí)例,Query實(shí)例由EntityManager通過指定查詢語句構(gòu)建。該接口擁有眾多執(zhí)行數(shù)據(jù)查詢的接口方法:
? Object getSingleResult():執(zhí)行SELECT查詢語句,并返回一個(gè)結(jié)果;
? List getResultList() :執(zhí)行SELECT查詢語句,并返回多個(gè)結(jié)果;
? Query setParameter(int position, Object value):通過參數(shù)位置號綁定查詢語句中的參數(shù),如果查詢語句使用了命令參數(shù),則可以使用Query setParameter(String name, Object value)方法綁定命名參數(shù);
? Query setMaxResults(int maxResult):設(shè)置返回的最大結(jié)果數(shù);
? int executeUpdate():如果查詢語句是新增、刪除或更改的語句,通過該方法執(zhí)行更新操作;
6.JPA的查詢語言
JPA的查詢語言是面向?qū)ο蠖敲嫦驍?shù)據(jù)庫的,它以面向?qū)ο蟮淖匀徽Z法構(gòu)造查詢語句,可以看成是Hibernate HQL的等價(jià)物。
簡單的查詢
你可以使用以下語句返回所有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表示用位置標(biāo)識參數(shù),爾后,我們可以通過Query的setParameter(1, "主題1")綁定參數(shù)。而DISTINCT表示過濾掉重復(fù)的數(shù)據(jù)。
如果需要以命名綁定綁定數(shù)據(jù),可以改成以下的方式:
SELECT DISTINCT t FROM Topic t WHERE t.topicTitle = :title
這時(shí),需要通過Query的setParameter("title", "主題1")綁定參數(shù)。
關(guān)聯(lián)查詢:從One的一方關(guān)聯(lián)到Many的一方 返回PollOptions對應(yīng)的PollTopic對象,可以使用以下語句:
SELECT DISTINCT p FROM PollTopic p, IN(p.options) o WHERE o.optionItem LIKE ?1
這個(gè)語法和SQL以及HQL都有很大的區(qū)別,它直接實(shí)體屬性連接關(guān)聯(lián)的實(shí)體,這里我們通過PollTopic的options屬性關(guān)聯(lián)到PollOption實(shí)體上,對應(yīng)的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))
該查詢語句的另外兩種等價(jià)的寫法分別是:
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
關(guān)聯(lián)查詢:從Many的一方關(guān)聯(lián)到One的一方 從Many一方關(guān)聯(lián)到One一方的查詢語句和前面所講的也很相似。如我們希望查詢某一個(gè)調(diào)查主題下的所示調(diào)查項(xiàng),則可以編寫以下的查詢語句:
SELECT p FROM PollOption p JOIN p.pollTopic t WHERE t.topicId = :topicId
對應(yīng)的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 = ?)))
使用其它的關(guān)系操作符
使用空值比較符,比如查詢附件不空的所有帖子對象:
SELECT p FROM Post p WHERE p.postAttach IS NOT NULL
范圍比較符包括BETWEEN..AND和>、>= 、<、<=、<>這些操作符。比如下面的語句查詢?yōu)g覽次數(shù)在100到200之間的所有論壇主題:
SELECT t FROM Topic t WHERE t.topicViews BETWEEN 100 AND 200
集合關(guān)系操作符 和其它實(shí)體是One-to-Many或Many-to-Many關(guān)系的實(shí)體,通過集合引用關(guān)聯(lián)的實(shí)體,我們可以通過集合關(guān)系操作符進(jìn)行數(shù)據(jù)查詢。下面的語句返回所有沒有選項(xiàng)的調(diào)查論壇的主題:
SELECT t FROM PollTopic t WHERE t.options IS EMPTY
我們還可以通過判斷元素是否在集合中進(jìn)行查詢:
SELECT t FROM PollTopic t WHERE :option MEMBER OF t.options
這里參數(shù)必須綁定一個(gè)PollOption的對象,JPA會自動將其轉(zhuǎn)換為主鍵比較的SQL語句。
子查詢 JPA可以進(jìn)行子查詢,并支持幾個(gè)常見的子查詢函數(shù):EXISTS、ALL、ANY。如下面的語句查詢出擁有6個(gè)以上選項(xiàng)的調(diào)查主題:
SELECT t FROM PollTopic t WHERE (SELECT COUNT(o) FROM t.options o) > 6
可用函數(shù) JPA查詢支持一些常見的函數(shù),其中可用的字符串操作函數(shù)有:
? CONCAT(String, String):合并字段串;
? LENGTH(String):求字段串的長度;
? LOCATE(String, String [, start]):查詢字段串的函數(shù),第一個(gè)參數(shù)為需要查詢的字段串,看它在出現(xiàn)在第二個(gè)參數(shù)字符串的哪個(gè)位置,start表示從哪個(gè)位置開始查找,返回查找到的位置,沒有找到返回0。如LOCATE ('b1','a1b1c1',1)返回為3;
? SUBSTRING(String, start, length):子字段串函數(shù);
? TRIM([[LEADING|TRAILING|BOTH] char) FROM] (String):將字段串前后的特殊字符去除,可以通過選擇決定具體的去除位置和字符;
? LOWER(String):將字符串轉(zhuǎn)為小寫;
? UPPER(String):將字符串轉(zhuǎn)為大寫。
數(shù)字操作函數(shù)有:
? ABS(number):求絕對值函數(shù);
? MOD(int, int):求模的函數(shù);
? SQRT(double):求平方函數(shù);
? SIZE(Collection):求集合大小函數(shù)。
更改語句
可以用EntityManager進(jìn)行實(shí)體的更新操作,也可以通過查詢語言執(zhí)行數(shù)據(jù)表的字段更新,記錄刪除的操作:
下面的語句將某一個(gè)調(diào)查選項(xiàng)更新為新的值:
UPDATE PollOption p SET p.optionItem = :value WHERE p.optionId = :optionId
我們使用Query接口的executeUpdate()方法執(zhí)行更新。下面的語句刪除一條記錄:
DELETE FROM PollOption p WHERE p.optionId = :optionId
排序和分組
我們還可以對查詢進(jìn)行排序和分組。如下面的語句查詢出主題的發(fā)表時(shí)間大于某個(gè)時(shí)間值,返回的結(jié)果按瀏覽量降序排列:
SELECT t FROM Topic t WHERE t.topicTime > :time ORDER BY t.topicViews DESC
下面的語句計(jì)算出每個(gè)調(diào)查主題對應(yīng)的選項(xiàng)數(shù)目:
SELECT COUNT(p),p.pollTopic.topicId FROM PollOption p GROUP BY p.pollTopic.topicId
這里,我們使用了計(jì)算數(shù)量的聚集函數(shù)COUNT(),其它幾個(gè)可用的聚集函數(shù)分別為:
? AVG:計(jì)算平均值,返回類型為double;
? MAX:計(jì)算最大值;
? MIN:計(jì)算最小值;
? SUM:計(jì)算累加和;
我們還可以通過HAVING對聚集結(jié)果進(jìn)行條件過濾:
SELECT COUNT(p),p.pollTopic.topicId FROM PollOption p GROUP BY p.pollTopic.topicId HAVING p.pollTopic.topicId IN(1,2,3)
7.小結(jié) 在不久的將來,Sun可能會將JPA作為一個(gè)單獨(dú)的JSR對待,同時(shí)JPA還可能作為Java SE的一部分。不過這些都不太重要,重要的是,我們現(xiàn)在已經(jīng)可以在脫離容器的情況下、在Java SE應(yīng)用中使用JPA了。
JPA已經(jīng)作為一項(xiàng)對象持久化的標(biāo)準(zhǔn),不但可以獲得Java EE應(yīng)用服務(wù)器的支持,來可以直接在Java SE中使用。開發(fā)者將無需在現(xiàn)有多種ORM框架中艱難地選擇,按照Sun的預(yù)想,現(xiàn)有ORM框架頭頂?shù)墓猸h(huán)將漸漸暗淡,不再具有以往的吸引力。