Jakarta EE 之 JPA入门精要

企业级的软件开发中,Java一直都是中流砥柱。在Java EE8之后,Oracle公司把企业级Java标准控制权转交Eclipse基金。最新或·和以后的企业级Java将冠名为Jakarta EE。最新的Jakarta EE是和Java EE 8相兼容的。企业级Java标准,包含有一系列的可选且可用规范(参见下图-1),可根据自己的业务需要酌情进行企业模块的选择。我们知道,任何企业应用如果不跟数据库打交道,那都是耍流氓的。在企业级Java的标准组件中,JPA的标准和实现,无疑是非常优秀而突出的。JPA是在积累了20多年的企业级应用的行业经验的基础诞生的,且依然在发展中。所以说,在企业级应用开发中,与数据库的通讯和交互,大力推荐用JPA。

JPA是一组标准和规范,是Java EE中的一块,其最终的实现厂商各有不同(我这里的几篇文章,都是基于eclipselink的),但必须遵循标准。当然,各个标准的最终厂商实现,也或多或少的加入了自己的扩展和特性,可酌情考虑采用。

对于很多开发者来说,或许觉得JPA“太重”了,直接拒绝之。其实不然,JPA的基本设计和构成足够简单和易用。相信通过我的几篇文章,能让你更快更好的掌握JPA的精要,以便在实际业务中采用,让你更多的关注业务逻辑。当然,本问首要目标是构建你的总体框架似的认知和体验,随着后续的示例和实战演示,相信你会更好的理解之。

行文中,尽力长话短说,一看就懂。下面就进入正文介绍。


Jakarta EE 之 JPA入门精要

图-1:企业级Java标准体系组成

1 JPA核心架构

1.1JPA核心组件

作为JAVA EE数据持久化操作的官方标准的JPA,其核心组件结构如下如所示:


Jakarta EE 之 JPA入门精要

图-2:JPA组件及关系

组件构图说明:

1)Persistence:此类javax.persistence.Persistence包含获取EntityManagerFactory实例的的静态帮助器方法,此方法是与供应商(JPA实现提供方)无关的,即供应商中立的方法。典型代码示例:

EntityManagerFactory factory = Persistence.createEntityManagerFactory(persistenceUnitName);

persistenceUnitName为我们的配置文件中的持久化单元名称。

使用Persistence类获取EntityManagerFactory不建议经常使用以获取工厂,因为工厂的构建是一个昂贵的操作,代价高昂。我们通常会缓存一个工厂,然后引用它重复使用,尤其在SE环境下,更应如此。此类间接需要引用persistence.xml配置文件。

2)Persistence Unit:为JPA的持久化单元配置文件,即persistence.xml。此文件中可以配置多个持久化单元,即persistence-unit元素可以根据需要配置多个。持久化单元可以认为就是persistence.xml配置文件,示例参考如下:

<code>
<persistence>

         <persistence-unit>   
                  <class>com.nd.jpa.demo.TxDepart/<class>
                  <class>com.nd.jpa.demo.TxEmployee/<class>
                  <properties>
                          <property>
                          <property>                           value="jdbc:mysql://localhost:33060/mytest?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true"/>
                          <property>
                          <property>
                  /<property>/<properties>
         /<persistence-unit>
/<persistence>/<code>

EntityManagerFactory: javax.persistence.EntityManagerFactory类是实体管理器EntityManager的工厂,用于创建实体管理器,典型典型代码如下:

<code>EntityManager em = factory.createEntityManager();//从工厂创建实体管理器/<code>

4)Persistence Contexts:作为JPA的核心术语,一个持久化单元就是一些实体类的命名配置(项)界定的范围。持久性或持久化上下文就是指的一组受管理(托管)的实体实例集合。每个持久性上下文都与一个持久性单元相关联,将托管实例的类限制在持久性单元定义的集合。若说一个实体实例是受管理(托管)的,意味着它包含在一个持久性上下文中,并且可以由实体管理器对其进行操作。就是因此,我们一个说实体管理器管理着一个持久性上下文,但一个持久化上下文可以对应多个实体管理器。我们可以借由实体管理器来操作持久化上下文中的实体实例(对象)。

理解持久性上下文是理解实体管理器的关键。实体在持久性上下文中的包含或排除将决定对其进行的任何持久性操作的结果。如果持久性上下文参与事务,则托管实体的内存状态将同步到数据库。然而,尽管持久性上下文起着重要的作用,但它实际上对应用程序是不可见的。它总是通过实体管理器间接访问,并假定在我们需要它时它就在那里。

上面所述,结合上图都好理解,但是持久性上下文是如何创建的?何时创建持久性上下文?实体管理器在应用中是如何体现的?这就是它开始变得有趣的地方。随着我们JPA的深入应用,你将能更好的来理解——目前就认为它是一种逻辑性范围管理概念即可。

5)EntityManager:javax.persistence.EntityManager是应用程序使用的主要JPA接口。每个EntityManager管理一组持久对象,并具有用于插入新对象和删除现有对象的API。

注意,在容器之外使用时,EntityManager和EntityTransaction之间存在一对一的关系。EntityManagers还充当查询(Query)实例的工厂。

6)Entity:表示数据(库)存储记录的持久化对象。通常是用Entity注解来表示的一个POJO对象类,可对应数据库里的一张表。

7)EntityTransaction:前面说过,每个EntityManager与单个javax.persistence.EntityTransaction有一对一的关系。EntityTransactions允许将持久化数据上的操作分组到工作单元中,以便这些工作单元要么完全成功,要么完全失败,从而使数据存储保持原始状态。这些全成功或全失败操作对于维护数据完整性非常重要。通常我们在SE环境下(非Java EE环境下),要手动编程来进行实体管理器相关的事务性操作。

8)Query:javax.persistence.Query接口由每个JPA供应商实现,以查找满足特定条件的持久对象。JPA标准支持使用Java持久性查询语言(JPQL)和结构化查询语言(SQL)进行查询。编程时,我们从EntityManager获取查询(Query)实例。

当然,JPA中还有其它一些组件类,这里暂不介绍。

再次强调JPA中还有其它接口,但仅在兼容ejb3的应用程序服务器之外使用。在应用服务器中,EntityManager实例通常被注入式的,这使得手动操作EntityManagerFactory变得不必要,即不需要手动编程实现。此外,应用服务器中的事务是使用标准的应用服务器事务控件器处理的,因此,EntityTransaction也未被使用。简而言之,若在应用服务器外应用,需要编程式操作EntityManagerFactory和事务处理。

1.1JPA配置文件

persistence.xml文件为标准化JPA运行时提供了通用的支持,并用配置元素properties和子元素property为特定厂商提供了兼容支持。JPA的第一步就是在此xml文件中配置持久化单元。此配置文件在通常在源代码根目录下的META-INF目录下(若IDE会生成一个资源目录,则位置一般为:resources\\META-INF\\persistence.xml)。这里给出一个常规的且较详细的persistence.xml,并作简要解释:

<code>
<persistence>
   
    <persistence-unit>
        <jta-data-source>jdbc/ndsa/<jta-data-source>
<provider>org.eclipse.persistence.jpa.PersistenceProvider/<provider>
        <class>com.newdayedu.sa.entity.SmOrgans/<class>
       
        <exclude-unlisted-classes>
            false
        /<exclude-unlisted-classes>
        <properties>
           
            <property>
        /<properties>
/<persistence-unit>

   
/<persistence>/<code>

文件主要配置项说明:

1) persistence-unit:配置文件中,持久化单元可以根据需要配置多个,且可以是异构的数据库;

2) persistence-unit的属性name:为管理器工厂所有的唯一名称;

3) persistence-unit的属性transaction-type:有两种类型JTA和 RESOURCE_LOCAL ;通常在Java EE服务器下选JTA,在SE下使用RESOURCE_LOCAL 。前者的事务交给服务器管理,后者一般要手动控制事务,通过EntityTransaction来进行事务的处理。

4) 子元素provider:指定JPA特定厂商的实现类,即持久化提供器。若应用中只有一种特定的提供器(一般应用服务器会提供),如无特别的元数配置要求,可以不用显式声明,若有多个持久化单元,且有不同的提供商来实现持久化器,则需要显式声明。

5) 子元素jta-data-source/ non-jta-data-source:这里要知道,持久性单元元数据的一个基本功能属性,是描述提供者应该从何处获取数据库连接(属性)以读写实体数据。目标数据库是根据位于服务器JNDI空间中的JDBC数据源的名称指定的(服务器配置托管的数据源)。这个数据源必须是全局可访问的,以便提供器(JPA的实现)在部署持久性应用程序时要访问它。因此典型的情况是使用JTA事务,因此应该在jta-data-source元素中指定JTA数据源的名称。类似地,如果持久性单元的事务类型是本地化资源的,则应该使用non-jta-data-source元素。尽管JPA定义了用于指定数据源名称的标准元素,但它并没有规定格式。在过去,通过在服务器特定的配置文件或管理控制台中进行配置,数据源在JNDI中是可用的(不管JTA还是非JTA型数据源)。这个名称并非官方可移植的,但实际上它们通常是jdbc/SomeDataSource的形式。

有些JPA(的实现)提供器通过与当前JTA事务无关的数据库连接(非JTA连接)来提供高性能的读取操作。然后返回查询结果并使其与持久性上下文的内容一致。这样提高了应用程序的可伸缩性,因为数据库连接直到以后绝对需要时(通常是在提交时)才会参与到JTA事务中。所以为了支持这些可伸缩的读取,除了jta-data-source元素外,还提供non-jta-data-source元素值。比如如下示例:

<code><persistence-unit>
<jta-data-source>java:app/jdbc/EmployeeDS/<jta-data-source>
<non-jta-data-source>java:app/jdbc/NonTxEmployeeDS
/<non-jta-data-source>/<persistence-unit>/<code>

请注意,EmployeeDS是一个规则配置的数据源,用于访问employee数据库,而NonTxEmployeeDS是一个单独的数据源,用于访问相同的employee数据库,但不参与到JTA事务中。说白了,这两个数据源都是容器相关的JNDI服务,在SE环境下,根本不用考虑这两项配置。

6) 子元素mapping-file*:实体和可嵌入类的XML映射文件的资源名。也还可以在META-INF目录中的一个orm.xml文件中指定映射信息。如果存在,将自动读取orm.xml映射文件。可出现零到多个映射文件。

7) 子元素jar-file*:包含实体和可嵌入类的jar文件的名称。JPA实现将扫描这个jar以查找相关注释的类。可出现零到多个。

8) 子元素class*: 实体和可嵌入类的类名,可并列出现零到多个。

还有其它几个配置元素,很少用,或说一般不用,具体就不再赘述。感兴趣的可自行查看persistence元素定义。

1.1JPA应用案例

为了对上述JPA的核心构成有个更直观的感受,我们这里给了两个示例作为对比总结。若一时不能理解,没关系,继续看完就可以掌握JPA的基本应用了。

1.1.1 SE环境下应用

下面的示例说明了JPA接口如何交互来执行JPQL查询和更新持久化对象。该示例假设在容器外部执行(非Java EE环境)。主要示例代码如下:

<code>public class JPADemo {
 
         public static void main(String[] args) {
                  //获取实体管理器工厂
                  EntityManagerFactory factory = Persistence.createEntityManagerFactory("JPADemo");
                  // 从工厂获取实体管理器EntityManager
                  EntityManager em = factory.createEntityManager();
                  // 开始事务
                  em.getTransaction().begin();
                  // 查询所有在研究部门工作,且平均每周工作超过40小时的员工
                  Query query = em.createQuery("SELECT e " +
                  " FROM Employee e " +
                  " WHERE e.division.name = 'Research' " +

                  " AND e.avgHours > 40");
                  List results = query.getResultList();
                  // 给这些努力工作的员工加薪
                  for (Object res : results) {
                          Employee emp = (Employee) res;
                          emp.setSalary(emp.getSalary() * 1.1);
                  }
                  // 提交(commit)将检测所有更新了的实体,然后将它们保存在数据库中
                  em.getTransaction().commit();
                  // 关闭实体管理器,释放资源
                  em.close();
         }
}/<code>


1.1.2 容器环境下应用

在容器中,EntityManager将被注入,事务将以声明的方式处理。因此,容器内的示例本版,将完全由业务逻辑组成,实例代码如下:

<code>    @PersistenceContext(unitName = "JPADemo")
    private EntityManager em;
         ……
         // 查询所有在研究部门工作,且平均每周工作超过40小时的员工
         // -注意,这里的实体管理器(EntityManager) em使用@PersistenceContext
         // 或@Resource注解注入的,其创建由应用服务器完成,这里直接引用
         Query query = em.createQuery("select e from Employee e where "
         + "e.division.name = 'Research' and e.avgHours > 40");
         List results = query.getResultList();
         // 给努力工作的员工加薪,事务有容器管理

         for (Object res : results) {
                  emp = (Employee) res;
                  emp.setSalary(emp.getSalary() * 1.1);
         }
         ……/<code>

Jakarta EE 之 JPA入门精要

在前两个应用示例中,对于应用程序管理的实体管理器,在Java SE和Java EE环境之间的区别,不在于如何创建实体管理器,而在于如何获得工厂。

本篇内容到此介绍,请一定仔细看看,理解JPA的总体性机制,后续在慢慢深入,若一下就深入细节,结果就是让你很想放弃。

下一篇我会介绍JPA的核心接口以及相关API。在此系列介绍后,我会在给出一个完整的JPA工程,以供你进一步实战操练。

敬请期待吧 ^_^



欢迎点赞、收藏、转发;若能打赏一二,那一准会点燃我更多的创作激情的 ^_^


分享到:


相關文章: