`

JPA EntityManager 详解

阅读更多
持久化上下文(Persistence Contexts)的相关知识,内容包括如何从Java EE容器中创建EntityManager对象、如何从Java SE中创建EntityManager对象、持久化上下文与事务(Transction)的关系,以及实体管理器工厂(Entity Manager Factory)的相关内容。
通过本章的学习,读者将深入掌握JPA中有关持久化上下文、事务处理的相关知识,从而能够更加深入地应用JPA。


11.1 获得EntityManager对象

那么如何获得EntityManager对象呢?这又是JPA中另外一个很重要的问题。


11.1.1  Java EE环境与J2SE环境

在详细讲述EntityManager对象之前,读者首先要分清楚两个概念,即Java EE环境与J2SE环境。因为在本章后面的学习中要经常提到这两个概念,所以读者一定要先理解它们,为以后的学习打好基础。


— Java EE环境,包括EJB容器和Web容器。

(1)Web容器:只运行Web应用的容器,例如Tomcat就是开源的Web容器,它可以运行JSP、Servlet等。

(2)EJB容器:运行在EJB组件的容器,提供EJB组件的状态管理、事务管理、线程管理、远程数据资源访问、连接管理和安全性管理等系统级服务。例如JBoss为EJB容器和Web容器(Web容器是集成了Tomcat)结合。

部署在EJB容器中的JAR包都可以认为是运行在EJB容器中。但JBoss中的Web应用,比如war包中的类就不是运行在EJB容器中,而是运行在Web容器中。

— J2SE环境

最普通Java运行环境,例如一个HelloWorld的Java程序就是运行在J2SE的环境中,通常使用main入口方法作为程序启动的触发。

如图11-1所示,它说明了Java EE与J2SE环境的关系。

11.1.2  两种类型的EntityManager对象

根据EntityManager对象的管理方式,可以有以下两种类型。

— 容器托管的(container-managed)EntityManager对象

容器托管的EntityManager对象最简单,程序员不需要考虑EntityManager连接的释放,以及事务等复杂的问题,所有这些都交给容器去管理。容器托管的EntityManager对象必须在EJB容器中运行,而不能在Web容器和J2SE的环境中运行。本书前面讲述的EntityManager对象都是通过注入 @PersistenceContext注释来获得的,其实,这种获得EntityManager对象的方式就是容器托管的。

— 应用托管的(application-managed)EntityManager对象

应用托管的EntityManager对象,程序员需要手动地控制它的释放和连接、手动地控制事务等。但这种获得应用托管的EntityManager对象的方式,不仅可以在EJB容器中应用,也可以使 JPA脱离EJB容器,而与任何的Java环境集成,比如说Web容器、J2SE环境等。所以从某种角度上来说,这种方式是JPA能够独立于EJB环境运行的基础。

理想状态下,最好是选用容器托管的EntityManager对象的方式,但在特殊的环境下,还是需要使用应用托管的EntityManager对象这种方式。

正是因为应用托管的EntityManager对象的连接释放、事务控制比较复杂,所以在使用时涉及的相关内容比较多,这些内容将在本章后面部分详细讲述,这里读者应对两种方式有一个大致的了解,两种EntityManager对象类型的比较如表11-1所示。

表11-1  容器托管与应用托管的EntityManager对象对比

比较内容

容器托管的(container-managed)EntityManager对象

应用托管的(application-managed)EntityManager对象

获得方式

两种方式:1 @PersistenceContex注入 2 JNDI获得

EntityManagerFactory创建

支持事务

JTA

JTA、RESOURCE_LOCAL

运行环境

EJB容器

EJB容器、Web容器、J2SE环境

11.1.3  容器托管的(container-managed)EntityManager对象

容器托管的EntityManager对象只能运行在EJB容器中。所以可以这样理解,只有在EJB-JAR包中,才可以获得容器托管的EntityManager对象,否则只能获得应用托管的EntityManager对象。

在EJB容器中获得EntityManager对象主要有两种方式,即@PersistenceContext注释注入和JNDI方式获得。

11.1.3.1  通过@PersistenceContext注释注入

这种方式获得EntityManager对象最为常用,例如下面代码所示。

@Stateless

public class CustomerService implements ICustomerService {

    @PersistenceContext(unitName = "jpaUnit")

    private EntityManager entityManager;

    public List<CustomerEO> findAllCustomers() {

        Query query = entityManager.createQuery("SELECT c FROM CustomerEO c");

        List<CustomerEO> result = query.getResultList();

        for (CustomerEO c : result) {

            System.out.println(c.getId()+","+c.getName());

        }

        return result;

    }

}


在使用此种方式创建EntityManager对象时,需要注意以下几个问题。

— @PersistenceContext注释中,其中unitName为persistence.xml文件中<persistence-unit>元素中的属性“name”的值,表示要初始化哪个持久化单元,如下所示。

<persistence>

    <persistence-unit name="jpaUnit" transaction-type="JTA">

    </persistence-unit>

</persistence>


— @PersistenceContext注释中还可以配置其他的设置,它的定义如下所示。

@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)

public @interface PersistenceContext{

    String name() default "";

    String unitName() default "";

    PersistenceContextType type default TRANSACTION;

    PersistenceProperty[] properties() default {};

}


— 其中PersistenceContextType可以设置创建EntityManager对象时,持久化上下文的作用范围,它的意义在于对有状态的Bean(Stateless Bean)可以跨事务操作实体。它主要有两种方式,定义如下所示。
public enum PersistenceContextType {

    TRANSACTION,

    EXTENDED

}


默认情况下使用TRANSACTION,有关TRANSACTION方式和EXTENDED方式创建EntityManager对象的异同,将在下文中详细讲述,这里读者简单了解一下即可。

11.1.3.2  通过JNDI的方式获得

如果指定了@PersistenceContext注释中的name值,则设置了持久化上下文的JNDI名称。通过SessionContext可以创建EntityManager对象。

例如,下面代码为通过JNDI方式获得EntityManager对象。

@Stateless

@PersistenceContext(name="jpa")

public class CustomerService implements ICustomerService {

    @Resource

    SessionContext ctx;

    public List<CustomerEO> findAllCustomers() {

        EntityManager entityManager = (EntityManager) ctx.lookup("jpa");

        Query query = entityManager.createQuery("SELECT c FROM CustomerEO c");

        List<CustomerEO> result = query.getResultList();

        for (CustomerEO c : result) {

            System.out.println(c.getId()+","+c.getName());

        }

        return result;

    }

}


11.1.4  应用托管的(application-managed)EntityManager对象

应用托管的EntityManager对象,不仅可以在Java EE环境中获得,也可以应用在J2SE的环境中。但无论是在什么情况下获得的EntityManager对象,都是通过实体管理器工厂(EntityManagerFactory)对象创建的。所以如何获得应用托管的EntityManager对象关键是 EntityManagerFactory对象如何获得。

下面就分别讲述在EJB容器、Web容器和J2SE环境中如何获得EntityManagerFactory对象。

11.1.4.1  EJB容器中获得

在EJB容器中,EntityManagerFactory对象可以通过使用注入@PersistenceUnit注释获得,例如下面代码为在EJB容器中,获得应用托管的EntityManager对象的方法。

@Stateless

public class CustomerService implements ICustomerService {

    @PersistenceUnit(unitName="jpaUnit")

    private EntityManagerFactory emf;

    public List<CustomerEO> findAllCustomers() {

        /**创建EntityManager对象*/

        EntityManager em = emf.createEntityManager();

        Query query = em.createQuery("SELECT c FROM CustomerEO c");

        List<CustomerEO> result = query.getResultList();

        for (CustomerEO c : result) {

            System.out.println(c.getId()+","+c.getName());

        }

        /**关闭EntityManager */

        em.close();

        return result;

    }

}

通过以上的EntityManager对象代码,可以总结出以下几个问题。

— 应用托管的EntityManager对象,要在代码中手动地创建和关闭,例如下面代码所示。

EntityManager em = emf.createEntityManager();

/**其他的业务逻辑*/

em.close();

这点正是与容器托管的EntityManager对象的最大不同之处。事实上,容器托管的EntityManager对象,它的创建和关闭是由容器负责管理的,所以不需要编写代码来控制。

— 应用托管的EntityManager对象,都是通EntityManagerFactory对象来创建的。在容器中可以通过使用注入@PersistenceUnit注释的方法实现,它的定义如下所示。

@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)

public @interface PersistenceUnit{

    String name() default "";

    String unitName() default "";

}


其中,属性unitName为persistence.xml文件中<persistence-unit>元素中的属性“name”的值,表示要初始化哪个持久化单元,与@PersistenceContext注释中unitName属性相同。

11.1.4.2  Web容器中获得

在Web容器中,EntityManagerFactory对象也可以通过使用注入@PersistenceUnit注释获得。例如,下面代码为在Servlet中,获得应用托管的EntityManager对象的方法。

public class TestServlet extends HttpServlet {

    @PersistenceUnit(unitName = "jpaUnit")

    private EntityManagerFactory emf;

    public TestServlet() {

        super();

    }

    public void doGet(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        doPost(request, response);

    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        response.setContentType("text/html");

        PrintWriter out = response.getWriter();

        out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional //EN\">");

        out.println("<HTML>");

        out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");

        out.println("  <BODY>");

        if (emf != null) {

            /**创建EntityManager 对象*/

            EntityManager entityManager = emf.createEntityManager();

            try {

                Query query = entityManager

                        .createQuery("SELECT c FROM CustomerEO c");

                List<CustomerEO> result = query.getResultList();

                for (CustomerEO c : result) {

                    System.out.println(c.getId() + "," + c.getName());

                }

            } finally {

                /**关闭EntityManager*/

                entityManager.close();

            }

        }

        out.println("  </BODY>");

        out.println("</HTML>");

        out.flush();

        out.close();

    }

}


由于是通过注入方法获得的EntityManagerFactory对象,所以EntityManagerFactory对象的创建不需要手动创建和关闭,这里与在EJB容器中的获得方法相同。

★ 提示 ★

目前JBoss 4.2集成了的Tomcat版本为5.5,但Tomcat 6.0以后的版本中才支持使用注释,所以如果将本例中Servlet运行在JBoss 4.2中,并不能获得EntityManagerFactory对象;但在符合J2EE 5.0的服务器中,这样运行是可以的。

虽然在目前JBoss 4.2版本中不支持使用注释,但可以通过另一种方式来获得应用托管的EntityManager对象。代码如下所示。

public class TestServlet extends HttpServlet {

    private EntityManagerFactory emf;

    public TestServlet() {

        super();

    }

    public void doGet(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        doPost(request, response);

    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        response.setContentType("text/html");

        PrintWriter out = response.getWriter();

        out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional //EN\">");

        out.println("<HTML>");

        out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");

        out.println("  <BODY>");

        if (emf != null) {

            /**创建EntityManager 对象*/

            EntityManager entityManager = emf.createEntityManager();

            try {

                Query query = entityManager

                        .createQuery("SELECT c FROM CustomerEO c");

                List<CustomerEO> result = query.getResultList();

                for (CustomerEO c : result) {

                    System.out.println(c.getId() + "," + c.getName());

                }

            } finally {

                /**关闭EntityManager*/

                entityManager.close();

            }

        }

        out.println("  </BODY>");

        out.println("</HTML>");

        out.flush();

        out.close();

    }

    /**Servlet初始化时,创建EntityManagerFactory 对象*/

    public void init() throws ServletException {

        if (emf == null) {

            emf = Persistence.createEntityManagerFactory("jpaUnit");

        }

    }

    /**Servlet销毁时,关闭EntityManagerFactory对象*/

    public void destroy() {

        if (emf.isOpen())

            emf.close();

    }

}


使用这种方式创建EntityManagerFactory对象需要注意以下几个问题。

— 可以看到,这里的EntityManagerFactory对象不是通过注入获得的,而是通过Persistence类中的静态方法createEntityManagerFactory来创建的。

— 正因为EntityManagerFactory对象是手动创建的,所以在不再使用时,一定要调用close()方法手动关闭。

11.1.4.3  J2SE环境中获得

在J2SE环境中,获得应用托管的EntityManager对象只能通过手动创建的方式,而不能使用注释的方式,与Web容器中不使用注释的方法相同,都是通过Persistence类中createEntityManagerFactory来创建的。

例如,下面代码为J2SE环境中获得应用托管EntityManager对象的方法。

public class CustomerClient {

    public static void main(String[] args) {

        /** 创建EntityManagerFactory对象 */

        EntityManagerFactory emf = Persistence

                .createEntityManagerFactory("jpaUnit");

        /** 创建entityManager对象 */

        EntityManager entityManager = emf.createEntityManager();

        Query query = entityManager.createQuery("SELECT c FROM CustomerEO c");

        List<CustomerEO> result = query.getResultList();

        for (CustomerEO c : result) {

            System.out.println(c.getId() + "," + c.getName());

        }

        /** 关闭entityManager对象 */

        entityManager.close();

        /** 关闭EntityManagerFactory对象 */

        emf.close();

    }

}

但是,在J2SE环境中使用JPA需要将实现的JPA的第三方类包和数据库的驱动包,设置到当前的运行环境下。

例如,在Eclipse中创建一个Java项目,需要将JPA实现者的类库(这里以Hibernate为例)和MySQL的数据库连接包添加到构建路径中,如图11-2所示。

11.1.5  ThreadLocal的使用

对于在Web容器中使用EntityManager对象,这里需要做一些改进,才能更安全。读者应该了解,Servlet是非线程安全的,所以需要改变获得EntityManager对象的方式,这里笔者建议使用ThreadLocal类。

ThreadLocal就是为每一个使用某变量的线程都提供一个该变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有一个该变量,这就解决了Servlet非线程安全的问题。

首先编写一个EntityManagerHelper类,代码如下所示。

public class EntityManagerHelper {

   

    private static final EntityManagerFactory emf;

    private static final ThreadLocal<EntityManager> threadLocal;

   

    /**初始化*/

    static {

        emf = Persistence.createEntityManagerFactory("jpaUnit");       

        threadLocal = new ThreadLocal<EntityManager>();

    }

   

    /**通过threadLocal 获得EntityManager 对象*/

    public static EntityManager getEntityManager() {

        EntityManager manager = threadLocal.get();     

        if (manager == null || !manager.isOpen()) {

            manager = emf.createEntityManager();

            threadLocal.set(manager);

        }

        return manager;

    }

   

    /**关闭EntityManager 对象*/

    public static void closeEntityManager() {

        EntityManager em = threadLocal.get();

        threadLocal.set(null);

        if (em != null) em.close();

    }

   

    public static void beginTransaction() {

    getEntityManager().getTransaction().begin();

    }

   

    public static void commit() {

    getEntityManager().getTransaction().commit();

    } 

   

    public static Query createQuery(String query) {

        return getEntityManager().createQuery(query);

    }

   

}


这样经过改进后,在Servlet中创建EntityManager对象的方法修改为如下所示。

public class TestServlet extends HttpServlet {

    public TestServlet() {

        super();

    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

            EntityManager entityManager = EntityManagerHelper.getEntityManager();

            try {

                Query query = entityManager

                        .createQuery("SELECT c FROM CustomerEO c");

                List<CustomerEO> result = query.getResultList();

                for (CustomerEO c : result) {

                    System.out.println(c.getId() + "," + c.getName());

                }

            } finally {

                /**关闭EntityManager*/

                EntityManagerHelper.closeEntityManager();

            }

    }

}



http://ajava.org/readbook/J2EE/ejbjpa/12472.html
分享到:
评论
2 楼 treblesoftware 2010-05-07  
hahalzb 写道
这是哪本书里的啊?


有注明转载:http://ajava.org/readbook/J2EE/ejbjpa/12472.html
1 楼 hahalzb 2010-05-07  
这是哪本书里的啊?

相关推荐

    jpa查询详解

    jpa查询详解 JPA(Java Persistence API)是一种 Java 持久层 API,用于处理 Java 对象和关系数据库之间的 Mapping。JPA 查询是 JPA 中的一种重要机制,用于从数据库中检索数据。在本文中,我们将详细介绍 JPA 查询...

    04_JPA详解_第一个JPA实例与JPA主键生成策略.zip

    本压缩包文件"04_JPA详解_第一个JPA实例与JPA主键生成策略.zip"包含了关于如何在实际项目中运用JPA,以及理解JPA主键生成策略的详细教程。 ### JPA基础 1. **JPA简介**: JPA是Java EE的一部分,它提供了一套规范,...

    最好的JPA详解大全

    在“最好的JPA详解大全”这本书中,你将深入学习到JPA的核心概念和使用技巧,涵盖以下几个关键知识点: 1. **基本概念**:了解什么是ORM,为什么需要JPA,以及它与Hibernate等ORM框架的关系。理解实体(Entity)、...

    01_传智播客JPA详解_全面阐释和精彩总结JPA

    Java Persistence API(JPA)是Java平台上的一个标准,用于管理关系数据库中的数据。...在“01_传智播客JPA详解_全面阐释和精彩总结JPA”这个资源中,你将找到关于JPA的深入讲解和实用技巧,帮助你全面掌握这一技术。

    08_JPA详解_分析JPA与持久化实现产品对接的源代码.zip

    2. **实体管理器(EntityManager)**: 这是JPA的核心接口,负责处理实体的生命周期,如创建、读取、更新和删除(CRUD)操作。开发者可以通过`EntityManager`实例来执行查询,管理实体的状态。 3. **实体管理工厂...

    07_JPA详解_使用JPA加载_更新_删除对象.zip

    `EntityManager`是JPA的核心接口,负责处理实体的生命周期,如加载、保存、更新和删除实体。 ### 2. 加载对象 #### 2.1 find()方法 使用`EntityManager`的`find()`方法可以按主键加载实体。例如: ```java ...

    04_传智播客JPA详解_第一个JPA实例与JPA主键生成策略

    【标题】"04_传智播客JPA详解_第一个JPA实例与JPA主键生成策略"涉及的关键知识点主要包括Java Persistence API (JPA)的基础应用、首个JPA实例的创建以及JPA主键生成策略的理解和配置。JPA是Java平台上的一个标准,...

    02_JPA详解_JPA开发环境和思想介绍.zip

    **JPA(Java Persistence API)详解 - 开发环境与核心思想** JPA,全称Java Persistence API,是Java平台上的一个标准,用于管理和持久化Java对象到关系数据库中。它是ORM(Object-Relational Mapping)的一种实现...

    03_JPA详解_搭建JPA开发环境和全局事务介绍.zip

    &lt;artifactId&gt;hibernate-entitymanager ``` 这些依赖提供了JPA的API以及Hibernate作为默认的JPA实现。 3. **配置数据库连接**: 在Spring Boot项目中,可以通过application.properties或application.yml文件配置...

    01_JPA详解_全面阐释和精彩总结JPA.zip

    这个资料包《01_JPA详解_全面阐释和精彩总结JPA》应该会涵盖以上所有知识点,并可能包含实例代码、示例数据库和详细的解释,帮助读者深入理解并掌握JPA在实际项目中的应用。通过学习和实践,开发者可以更加高效地...

    黑马程序员 jpa详解视频教程配套源代码 笔记 jar包 很全的

    这个“黑马程序员 JPA详解视频教程配套源代码 笔记 jar包 很全的”资源提供了全面的学习材料,包括源代码、笔记和必要的库文件,非常适合初学者或者想要深入理解JPA的开发者。 首先,JPA的核心概念是实体(Entity)...

    03_传智播客JPA详解_搭建JPA开发环境和全局事务介绍

    了解以上基础后,你可以观看"03_传智播客JPA详解_搭建JPA开发环境和全局事务介绍"的自学视频,视频将更深入地展示如何操作和实践。学习过程中,你将看到如何配置JPA环境,创建并管理实体,以及如何在实际项目中运用...

    07_传智播客JPA详解_使用JPA加载_更新_删除对象

    JPA的实体管理器(EntityManager)负责执行数据库相关的操作,如持久化、查询和事务管理。 在JPA中,加载对象主要通过实体管理器的`find()`方法或`createQuery()`方法来实现。`find()`方法根据实体的主键直接获取...

    09_JPA详解_使用JPQL语句进行查询.zip

    在"09_传智播客JPA详解_使用JPQL语句进行查询"的课程中,你将深入学习如何利用JPQL进行复杂的查询,包括联合查询、子查询、分页查询以及使用JOIN操作来处理关联数据。通过这个课程,你可以掌握JPA的核心概念和实践...

    02_传智播客JPA详解_JPA开发环境和思想介绍

    本套“02_传智播客JPA详解_JPA开发环境和思想介绍”自学视频主要涵盖了以下几个关键知识点: 1. **JPA基础概念**:首先,视频会解释什么是JPA,它与Hibernate等ORM框架的关系,以及为什么在Java开发中需要使用JPA。...

    springboot整合jpa

    - **EntityManager**:JPA的主要接口,用于管理持久化实体的生命周期,包括保存、更新、删除和查询实体。 - **EntityTransaction**:用于管理事务,提供了开始、提交、回滚事务的方法。 - **Query**:提供动态...

    EJB+JPA数据库持久层开发实践详解PDF

    例如,实体bean可以利用JPA的EntityManager接口来执行CRUD(Create, Read, Update, Delete)操作。此外,EJB还提供了事务管理、安全性、生命周期管理等功能,使得整个应用更加健壮。 在"更多电子资料.docx"文档和...

    JPA大全之经典全集

    JPA详解通常是对JPA的深入解析,包括实体(Entity)、持久化上下文(Persistence Context)、托管状态(Managed State)、懒加载(Lazy Loading)、级联操作(Cascading)、关系映射(Relationship Mapping)、查询...

Global site tag (gtag.js) - Google Analytics