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,所以在编写产品程序时,它也是个很有吸引力的选择。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐