您的位置:首页 > 编程语言 > Java开发

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

2017-03-09 17:46 253 查看
JPA的核心API就是EntityManager,负责管理JPA持久化上下文中的所有实体,负责跟踪所有实体的保存、更新和修改情况,并根据指定的flush模式将这些修改保存到数据库中。
在应用程序中使用EntityManager,大致分为3种情况
1)在EJB中使用EntityManager:直接使用依赖注入来管理EntityManager。
2)在Servle、JSF的托管Bean中使用EntityManager:不能直接使用依赖注入。因为多请求线程可能共享同一个Servlet或JSF的托管Bean,而EntityManager并没有被设计成线程安全的。两种解决方法,一是使用JNDI查找来获得EntityManager对象。二是使用依赖注入管理EntityManagerFactory对象(他是线程安全的),在通过EntityManagerFactory来获取EntityManager对象。
3)在JavaSE应用中使用EntityManager:需要通过应用程序显式地创建EntityManager。
 
现在介绍第三种的使用步骤
1)通过javax.persistence.Persistence工厂提供的createEntityManagerFactory()静态方法创建EntityManagerFactory对象。调用该方法时需要传入persistence.xml文件中持久化单元的名称。
2)调用EntityManagerFactory的createEntityManager()或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包中,则应该将该文件放在该WAR的WEB-INF/classes/META-INF路径下。即使对于不打包成WAR包的web应用,该文件也应该放在web应用的WEB-INF/classes/META-INF路径下。
JPA对实体类没有太多的要求,但是我们还是应该遵守几个基本原则
1)提供一个无参数的构造器,该构造器的访问控制符至少是包可见的,即大于或等于默认的访问控制符。
2)提供一个标识属性,通常映射数据库表的主键字段。如果使用联合主键,甚至可以用一个用户自定义的类,通常不推荐这么做。
注意:虽然JPA可以允许实体类没有标识属性,但这样做将导致JPA的许多功能无法使用。而且JPA建议使用允许接受null值的类型来作为标识属性的类型,因此应该尽量避免使用基本数据类型,可以考虑使用包装类。
3)为实体类的每个属性提供set和get方法。JPA持久化JavaBean风格的属性,认可如下方法名getFoo、isFoo和setFoo。如果需要也可以切换属性的访问策略。
4)使用非final类:许多JPA实现都需要在运行时生成动态代理。如果实体没有实现任何接口,那么JPA就需要为他动态地生成CGLIB代理类,该代理对象是实体类的子类的实例。
5)重写equals和hashCode方法:如果需要把实体类放入set中,则应该重写这两个方法。
 
JPA的实体状态演化图
 


 
持久化实体可以也可以使用EntityManager提供的persist(Object obj)方法。
与find方法查询相似的还有getReference方法,也可以根据实体主键类加载实体。区别是当调用find方法获取实体时如果不存在会返回null;getReference方法使用了代理模式,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方法将被忽略
 
如果怀疑当前实体的状态与底层数据库对应的记录不一致,可以调用EntityManager的refresh方法来刷新实体。
对于不同状态的实体,refresh方法行为如下
1)如果实体处于新建状态(或不是一个实体),将导致IllegalArgumentException异常
2)如果实体处于托管状态,refresh方法将会刷新该实体
3)如果实体处于脱管状态(或不是一个实体),将导致IllegalArgumentException异常
4)如果实体处于被删除状态,将导致IllegalArgumentException异常
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: