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

    xiaoxiao2021-03-25  109

    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异常

    转载请注明原文地址: https://ju.6miu.com/read-14210.html

    最新回复(0)