5.5.2 創建一個實體管理器工廠
簡而言之,基於JPA的程序使用EntityManagerFactory的一個實現來獲取EntityManager的實例。JPA規範定義了兩種實體管理器:
程序管理型:實體管理器是在程序直接向實體管理器工廠請求一個實體管理器時創建的。在這種情況下,程序負責打開或關閉實體管理器,並且在事務中控制管理器。這種類型最適合不運行於Java EE容器的獨立程序。
容器管理型:實體管理器由Java EE容器創建和管理。這種情況下,程序根本不與實體管理器工廠進行交互,實體管理器是通過注入或利用JNDI直接獲得的,容器負責配置實體管理器工廠。這種類型最適合希望不考慮persistence.xml中的特殊性而在JPA配置之上維持某種控制的Java EE容器。
這兩種實體管理器都實現同一個EntityManager接口,其關鍵區別並不在於EntityManager本身,而是EntityManager被創建和管理的方式。程序管理型的EntityManager是由EntityManagerFactory創建的,後者是通過調用PersistenceProvider的createEntityManagerFactory()方法獲得的。與之相比,容器管理型的EntityManagerFactory是通過PersistenceProvider的createContainerEntityManagerFactory()方法獲得的。
這些對於想使用JPA的Spring程序員意味着什麼呢?實際上沒有什麼。無論想使用什麼樣的EntityManagerFactory,Spring都會負責管理EntityManager。如果使用程序管理型實體管理器,Spring就會扮演程序的角色,以透明方式處理EntityManager。在使用容器管理型的情況下,Spring就會扮演容器。
這兩種實體管理器工廠分別由Spring相應的工廠Bean創建:
LocalEntityManagerFactoryBean生成程序管理型EntityManagerFactory。
LocalContainerEntityManagerFactoryBean生成容器管理型EntityManagerFactory。
需要說明的是,這兩種形式的EntityManagerFactory的選擇對於基於Spring的程序來說是完全透明的。Spring的JpaTemplate隱藏了處理EntityManagerFactory的複雜細節,讓我們的數據訪問代碼能夠着重完成其根本任務:數據訪問。
對Spring程序員來說,程序管理型和容器管理型實體管理器之間的惟一區別在於如何在Spring程序上下文裏進行配置。下面我們首先來介紹如何在Spring裏配置程序管理型的LocalEntityManager FactoryBean,然後再介紹如何配置容器管理型的LocalContainerEntityManagerFactoryBean。
配置程序管理型的JPA
程序管理型實體管理器工廠的絕大部分配置信息來自於一個名爲persistence.xml的配置 文件,這個文件必須位於類路徑下的META-INF目錄。
persistence.xml的作用在於定義一個或多個存留單元。存留單元是一個或多個存留類組成的組,它們對應於一個數據源。簡單來說,persistence.xml列出了一個或多個存留類以及其他額外配置,比如數據源和基於XML的映射文件。下面是針對RoadRantz程序的一個典型persistence.xml文件:
正是由於這個文件裏包含了大量配置信息,所以Spring裏僅需很少的配置。程序清單5.9所示的在Spring裏聲明瞭一個LocalEntityManagerFactoryBean。
程序清單5.9 配置一個程序管理型EntityManagerFactory工廠Bean
persistenceUnitName屬性的值就是persistence.xml裏存留單元的名稱。
創建程序管理型EntityManagerFactory的大部分工作是在persistence.xml裏完成的,這正是因爲它是要由程序進行管理的。在程序管理的情況下(不考慮Spring),完全由程序負責通過JPA實現的PersistenceProvider獲得EntityManagerFactory。如果在每次請求一個EntityManagerFactory時都要定義一個存留單元,那麼代碼會迅速膨脹。而通過在persistence.xml進行設置,JPA就可以在其中尋找存留單元定義了。
但由於Spring對JPA的支持,與PersistenceProvider打交道的是JpaTemplate而不是我們程序的代碼,這樣一來,把配置信息提取到persistence.xml就顯得有些多餘了。實際上,這樣做防止了我們在Spring裏配置EntityManagerFactory(好處之一是讓我們可以提供一個Spring配置的數據源)。
出於這個原因,我們把注意力轉向容器管理的JPA。
配置容器管理的JPA
容器管理的JPA採用了稍微不同的方法。當運行於容器裏時,EntityManagerFactory可以由容器提供的信息生成。這種形式的JPA本意是用於JEE程序服務器上的(比如WebLogic或JBoss),數據源信息是通過程序服務器的配置進行設置的。
儘管如此,容器管理型JPA也可以用於Spring。這時我們不是在persistence.xml裏配置數據源,而是在Spring程序上下文裏設置這些信息。舉例來說,程序清單5.10展示如何在Spring裏配置使用LocalContainerEntityManagerFactoryBean的容器管理型JPA。
程序清單5.10 配置一個容器管理型EntityManagerFactory工廠Bean
這裏的dataSource屬性被設置爲由Spring配置的數據源,javax.sql.DataSource的任何實現都可以,比如5.2小節裏配置的那些。雖然數據源仍然可以在persistence.xml進行配置,但這個屬性指定的數據源具有優先性。
jpaVendorAdapter屬性用於設置特定JPA實現的細節。本例使用的是TopLink Essentials,所以使用的TopLinkJpaVendorAdapter對其進行配置。這個代理適配器具有多個屬性,但最重要的是database屬性,在此指定了Hypersonic作爲我們所使用的數據庫。這個屬性能夠設置的其他值如表5.5所示。
表5.5 TopLink代理適配器支持多個數據庫,利用database屬性可以進行設置。
數據庫平臺 |
database屬性值 |
IBM DB2 |
DB2 |
續表
數據庫平臺 |
database屬性值 |
Hypersonic |
HSQL |
Informix |
INFORMIX |
MySQL |
MYSQL |
Oracle |
ORACLE |
PostgresQL |
POSTGRESQL |
Microsoft SQL Server |
SQLSERVER |
Sybase |
SYBASE |
一些動態存留特性要求存留對象的類進行修改,使用支持這個特性的指令。屬性被遲緩加載(也就是隻在其被實際訪問時才從數據庫獲取)的對象的類裏必須具有知道獲取未加載數據的代碼。有些框架使用動態代理來實現遲緩加載,有些(比如JDO)則在編譯時執行類指令。
JPA允許存留類在加載時執行指令,所以類可以在加載時以動態存留特性進行修改。LocalContainerEntityManagerFactoryBean的loadTimeWeaver屬性可以讓我們指定動態存留特性如何織入到存留類,這一次我們使用Spring的SimpleLoadTimeWeaver。
對實體管理器工廠的選擇主要取決於將如何使用它。對於簡單程序來說,LocalEntityManagerFactoryBean就足夠了。但LocalContainerEntityManagerFactoryBean讓我們在Spring裏不僅能夠配置JPA,所以在編寫產品程序時,它也是個很有吸引力的選擇。