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

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值