【JavaEE】经典JAVA EE企业应用实战-读书笔记14

来源:互联网 发布:国泰安金融数据库 编辑:程序博客网 时间:2024/06/07 15:41

JPA的核心API就是EntityManager,负责管理JPA持久化上下文中的所有实体,负责跟踪所有实体的保存、更新和修改情况,并根据指定的flush模式将这些修改保存到数据库中。

在应用程序中使用EntityManager,大致分为3种情况

1)EJB中使用EntityManager:直接使用依赖注入来管理EntityManager

2)ServleJSF的托管Bean中使用EntityManager:不能直接使用依赖注入。因为多请求线程可能共享同一个ServletJSF的托管Bean,而EntityManager并没有被设计成线程安全的。两种解决方法,一是使用JNDI查找来获得EntityManager对象。二是使用依赖注入管理EntityManagerFactory对象(他是线程安全的),在通过EntityManagerFactory来获取EntityManager对象。

3)JavaSE应用中使用EntityManager:需要通过应用程序显式地创建EntityManager

 

现在介绍第三种的使用步骤

1)通过javax.persistence.Persistence工厂提供的createEntityManagerFactory()静态方法创建EntityManagerFactory对象。调用该方法时需要传入persistence.xml文件中持久化单元的名称。

2)调用EntityManagerFactorycreateEntityManager()createEntityManager(Map map)方法来创建EntityManager对象。第二个createEntityManager方法可以传入一个Map参数,这个Map参数传入的属性会补充或覆盖persistence.xml文件中配置的属性

 

Hibernate

JPA

数据库、连接池配置信息

hibernate.cfg.xml

persistence.xml

持久化组件

持久化对象(Persistence Object

实体(Entity

持久化管理组件

Session(线程不安全)

EntityManager(线程不安全)

工厂类

SessionFactory(线程安全)

EntityManagerFactory(线程安全)


public class JpaQs {private static final EntityManagerFactory emf =Persistence.createEntityManagerFactory("qs");public static void main(String[] args) {final EntityManager em = emf.createEntityManager();News news = new News();news.setTitle("this is a title");news.setContent("this is a content");try {em.getTransaction().begin();em.persist(news);em.getTransaction().commit();} finally {em.close();}}}
 

EntityManager中常用方法如下:

方法

简介

void persistent(Object entity)

将指定实体保存到数据库中,转换为持久化状态

T merge(T entity)

将指定合并到底层数据库

void remove(Object entity)

删除指定实体,转换为瞬态

T find(Class<T> entityClass,Object pk)

根据实体的主键加载实体

void setFlushMode(FlushModeType flushMode)

设置持久化上下文的flush模式,可以是AUTO(默认值,即EntityManager自动管理实体和数据库同步)或COMMIT

void flush()

将持久化上下文中的实体状态同步到底层数据库

void refresh(Object entity)

刷新指定实体状态

Query createQuery(String jpql)

根据指定JPQL语句创建查询

Query createNamedQuery(String name)

根据配置中的查询名创建命名查询

Query createNativeQuery(String sqlString)

根据指定SQL语句创建原生SQL查询

void close()

关闭EntityManager

boolean isOpen()

判断EntityManager是否打开

 

JPA除了使用注解之外还可使用orm.xml文件来管理实体和数据表之间的映射关系,类似于*.hbm.xml文件。

News.orm.xml文件例子如下:

<entity-mappings><persistence-unit-metadata><access>PROPERTY</access></persistence-unit-metadata><!-- 指定实体默认所在的包 --><package>com.kingdz.model</package><entity class="News"><!-- 指定将实体类映射到表 --><table name="news_table"/><attributes><!-- 配置主键映射 --><id name="id"><!-- 指定主键生成策略 --><generated-value strategy="IDENTITY"/></id><basic name="title"><column name="news_title" length="50"/></basic><basic name="content"/></attributes></entity></entity-mappings>

这样在persistence.xml文件中需要增加对文件的引用,使用<mapping-file>来指定,片段如下

<persistence-unit>

<mapping-file>com/kingdz/model/News.orm.xml</mapping-file>

</persistence-unit>

 

JPA的主要思想就是让实体来映射底层数据表。

JPA规范中涉及两个常用的概念:持久化上下文(persistence context)和持久化单元(persistence unit)。

EntityManager负责跟踪持久化上下文中所有实体的状态,当应用程序改变了持久化上下文中的实体状态后,EntityManager将会根据指定的flush模式将实体的状态写入底层数据库。如果持久化上下文关闭,该上下文中所有实体都将会脱离EntityManager的管理,进入脱管状态,此时对实体所做的修改将不会自动同步到底层数据库。

持久化单元由persistence.xml文件定义,该文件必须位于META-INF路径下,关于该文件的存放位置说明如下:

1)对于一个JavaSE应用,如果程序没有将persistence.xml文件打包成JAR,则应该将该文件放在应用类加载路径的META-INF路径下

2)如果将persistence.xml文件打包到EJB JAR包中,则应该将该文件放在该JAR包的META-INF路径下

3)如果将persistence.xml文件打包到某个web应该的WAR包中,则应该将该文件放在该WARWEB-INF/classes/META-INF路径下。即使对于不打包成WAR包的web应用,该文件也应该放在web应用的WEB-INF/classes/META-INF路径下。

JPA对实体类没有太多的要求,但是我们还是应该遵守几个基本原则

1)提供一个无参数的构造器,该构造器的访问控制符至少是包可见的,即大于或等于默认的访问控制符。

2)提供一个标识属性,通常映射数据库表的主键字段。如果使用联合主键,甚至可以用一个用户自定义的类,通常不推荐这么做。

注意:虽然JPA可以允许实体类没有标识属性,但这样做将导致JPA的许多功能无法使用。而且JPA建议使用允许接受null值的类型来作为标识属性的类型,因此应该尽量避免使用基本数据类型,可以考虑使用包装类。

3)为实体类的每个属性提供setget方法。JPA持久化JavaBean风格的属性,认可如下方法名getFooisFoosetFoo。如果需要也可以切换属性的访问策略。

4)使用非final类:许多JPA实现都需要在运行时生成动态代理。如果实体没有实现任何接口,那么JPA就需要为他动态地生成CGLIB代理类,该代理对象是实体类的子类的实例。

5)重写equalshashCode方法:如果需要把实体类放入set中,则应该重写这两个方法。

 

JPA的实体状态演化图

 

 

持久化实体可以也可以使用EntityManager提供的persist(Object obj)方法。

find方法查询相似的还有getReference方法,也可以根据实体主键类加载实体。区别是当调用find方法获取实体时如果不存在会返回nullgetReference方法使用了代理模式,JPA会延迟加载该实体的状态,会抛出EntityNotFoundException

当程序修改托管实体的属性后,程序应该使用新的EntityManager来保存这些修改。EntityManager提供了merge方法来保存这些修改。典型的应用场景就是:服务器端程序使用EntityManager从底层数据库加载指定实体,然后将该实体送到远程客户端,客户端对该实体进行修改,修改完后在送给服务器端,服务器端就需要将该实体的状态合并到底层数据库。

News n=firstEm.load(News.class,1);

//第一个EntityManager已经关闭了

firstEm.close();

//修改脱管状态下的实体

n.setTitle(“新标题”);

//打开第二个EntityManager

EntityManager secondEm=...

//保存脱管对象所做的修改

n=secondEm.merge(n);

//接下来实体n将处于托管状态

对于不同状态的实体,merge方法行为如下

1)如果实体处于新建状态,merge方法将会把该实体状态保存到底层数据库。并创建该实体的副本,将该副本纳入EntityManager管理之下,并返回该副本

2)如果实体处于托管状态,该操作会忽略

3)如果实体处于被删除状态(或不是一个实体),将导致IllegalArgumentException异常

4)如果该实体处于脱管状态,merge方法将会把该实体状态合并到底层数据库。并且创建改实体的副本,将该副本纳入EntityManager管理之下,并返回该副本。

 

删除实体使用remove方法

底层使用delete from table where id=?的语句处理

对于不同状态的实体,remove方法行为如下

1)如果实体处于新建状态。remove方法将被忽略

2)如果实体处于托管状态,remove方法将会把实体转换到被删除状态。

3)如果实体处于脱管状态(或不是一个实体),将导致IllegalArgumentException异常

4)如果实体处于被删除状态,remove方法将被忽略

 

如果怀疑当前实体的状态与底层数据库对应的记录不一致,可以调用EntityManagerrefresh方法来刷新实体。

对于不同状态的实体,refresh方法行为如下

1)如果实体处于新建状态(或不是一个实体),将导致IllegalArgumentException异常

2)如果实体处于托管状态,refresh方法将会刷新该实体

3)如果实体处于脱管状态(或不是一个实体),将导致IllegalArgumentException异常

4)如果实体处于被删除状态,将导致IllegalArgumentException异常

0 0