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實例的方法:
EntityManagerFactorycreateContainerEntityManagerFactory(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接口方法發生遷移:
新建態: 新創建的實體對象,尚未擁有持久化主鍵,沒有和一個持久化上下文關聯起來。
受控態: 已經擁有持久化主鍵並和持久化上下文建立了聯繫;
遊離態: 擁有持久化主鍵,但尚未和持久化上下文建立聯繫;
刪除態: 擁有持久化主鍵,已經和持久化上下文建立聯繫,但已經被安排從數據庫中刪除。
EntityManager 的API
- 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 merge(T entity)
將一個遊離態的實體持久化到數據庫中,並轉換爲受控態的實體;
- T find(Class entityClass, Object primaryKey)
以主鍵查詢實體對象,entityClass是實體的類,primaryKey是主鍵值,如以下的代碼查詢Topic實體:
- Topic t = em.find(Topic.class,1);
- Query createQuery(String qlString)
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():如果查詢語句是新增、刪除或更改的語句,通過該方法執行更新操作;
JPA的查詢語言
JPA的查詢語言是面向對象而非面向數據庫的,它以面向對象的自然語法構造查詢語句 ,可以看成是Hibernate HQL的等價物。
- 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
小結
在不久的將來,Sun可能會將JPA作爲一個單獨的JSR對待,同時JPA還可能作爲Java SE的一部分。不過這些都不太重要,重要的是,我們現在已經可以在脫離容器的情況下、在Java SE應用中使用JPA了。
JPA已經作爲一項對象持久化的標準,不但可以獲得Java EE應用服務器的支持,還可以直接在Java SE中使用。開發者將無需在現有多種ORM框架中艱難地選擇, 這對開發人員來說是個福音。