JPA(四):EntityManager

Persistence

Persistence类使用于获取EntityManagerFactory实例,该类包含一个名为createEntityManagerFactory的静态方法。

        // 创建EntityManagerFactory
        String persistenceUnitName = "Jpa-helloword";
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);

Persistence提供了两个创建EntityManagerFactory的方法:

        Map<String, Object> map = new HashMap<>();
        map.put("hibernate.show_sql", false);
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName, map);

该方法中可以起到修改persistence.xml的作用。

上边的代码尽管persistence.xml配置内配置项“hibernate.format_sql”属性为true,但是我们在创建EntityManagerFactory的方法中传入了参数后,会覆盖persistence.xml中的配置项的值。

EntityManagerFactory

EntityManagerFactory接口主要用来创建EnittyManager实例,该接口约定了如下4个方法:

  • createEntityManager():用于创建实体管理器对象实例。
  • createEntityManager(Map map):用于创建实体管理器对象实例的重载方法,Map参数用于提供EntityManager的属性。
  • isOpen():检查EntityManagerFactory是否处于打开状态。实体管理器工厂创建后一直处于打开状态,除非调用close()方法将其关闭。
  • close():关闭EntityFactoryFactory。EntityManagerFactory关闭后将释放所有资源,isOpen()方法测试将返回false,其它方法将不能调用,否则将导致IllegalStateException异常。

EntityManager

准备工作:新建功能添加JPA,Spring依赖相关参考《JPA(二):HellWord工程

Person.java

package com.dxsoft.jpa.helloword;

import java.util.Date;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class Client {
    private String persistenceUnitName = "Jpa-helloword";
    private EntityManagerFactory entityManagerFactory = null;
    private EntityManager entityManager = null;
    private EntityTransaction entityTransaction = null;

    @Before
    public void init() {
        // 1.创建EntityManagerFactory
        entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
        // 2.创建EntityManager
        entityManager = entityManagerFactory.createEntityManager();
        // 3.开始事务
        entityTransaction = entityManager.getTransaction();
        entityTransaction.begin();
    }

    @After
    public void destory() {
        // 5.提交事务
        entityTransaction.commit();

        // 6.关闭EntityManager
        entityManager.close();
        // 7.关闭EnityManagerFactory
        entityManagerFactory.close();
    }
View Code

测试类:PersonTest.java

package com.dxsoft.jpa.helloword;

import java.util.Date;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class PersonTest {
    private String persistenceUnitName = "Jpa-helloword";
    private EntityManagerFactory entityManagerFactory = null;
    private EntityManager entityManager = null;
    private EntityTransaction entityTransaction = null;

    @Before
    public void init() {
        // 1.创建EntityManagerFactory
        entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
        // 2.创建EntityManager
        entityManager = entityManagerFactory.createEntityManager();
        // 3.开始事务
        entityTransaction = entityManager.getTransaction();
        entityTransaction.begin();
    }

    @After
    public void destory() {
        // 5.提交事务
        entityTransaction.commit();

        // 6.关闭EntityManager
        entityManager.close();
        // 7.关闭EnityManagerFactory
        entityManagerFactory.close();
    }
}

EntityManager#persistence

类似于Hibernate中的save方法,但是不同的是Hibernate允许添加实体对象指定了id,而JPA不允许。

    @Test
    public void testPersistence() {
        Person person = new Person();
        person.setFullName("AA");
        person.setAge(20);
        person.setBirth(new Date());
        person.setCreateTime(new Date());
        person.setId(1);

        entityManager.persist(person);
    }

JPA如果指定了id,则抛出异常:

javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.dxsoft.jpa.helloword.Person

但不指定id时,则能成功执行:

    @Test
    public void testPersistence() {
        Person person = new Person();
        person.setFullName("AA");
        person.setAge(20);
        person.setBirth(new Date());
        person.setCreateTime(new Date());

        entityManager.persist(person);
    }

操作日志:

Hibernate: 
    select
        next_val as id_val 
    from
        hibernate_sequence for update
            
Hibernate: 
    update
        hibernate_sequence 
    set
        next_val= ? 
    where
        next_val=?
Hibernate: 
    insert 
    into
        jpa_person
        (age, birth, createTime, full_name, id) 
    values
        (?, ?, ?, ?, ?)
View Code

EntityManager#find

类似于Hibernate中的get方法

    @Test
    public void testFind() {
        Person person = entityManager.find(Person.class, 1);
        System.out.println("-------------------------");
        System.out.println(person);
    }

打印日志:

Hibernate: 
    select
        person0_.id as id1_0_0_,
        person0_.age as age2_0_0_,
        person0_.birth as birth3_0_0_,
        person0_.createTime as createTi4_0_0_,
        person0_.full_name as full_nam5_0_0_ 
    from
        jpa_person person0_ 
    where
        person0_.id=?
-------------------------
Person [id=1, fullName=AA, age=20, birth=2018-06-19, createTime=2018-06-19 20:40:31.0]
View Code

备注:从打印信息中可以看出,调用了find方法后立即执行了查询,之后才打印“-------------------------”

EntityManager#getReference

类似于Hibernate中的load方法,懒加载。

    @Test
    public void testGetReference() {
        Person person = entityManager.getReference(Person.class, 1);
        // 从打印信息可以说明返回的对象person是一个代理对象。
        System.out.println(person.getClass().getName());
        
        System.out.println("-------------------------");
        
        System.out.println(person);
    }

打印信息:

com.dxsoft.jpa.helloword.Person$HibernateProxy$4AN8PTeG
-------------------------
Hibernate: 
    select
        person0_.id as id1_0_0_,
        person0_.age as age2_0_0_,
        person0_.birth as birth3_0_0_,
        person0_.createTime as createTi4_0_0_,
        person0_.full_name as full_nam5_0_0_ 
    from
        jpa_person person0_ 
    where
        person0_.id=?
Person [id=1, fullName=AA, age=20, birth=2018-06-19, createTime=2018-06-19 20:40:31.0]

备注:从打印信息中可以看出,调用了find方法后并未立即执行了查询,而是先执行了打印“-------------------------”,当调用person代理对象时才执行了查询。

需要注意:如果关闭了连接后,再去读取对象则会抛出异常。

    @Test
    public void testGetReference() {
        Person person = entityManager.getReference(Person.class, 1);
        // 从打印信息可以说明返回的对象person是一个代理对象。
        System.out.println(person.getClass().getName());

        System.out.println("-------------------------");
        
        // 5.提交事务
        entityTransaction.commit();
        // 6.关闭EntityManager
        entityManager.close();
        
        System.out.println(person);
    }

抛出异常:

org.hibernate.LazyInitializationException: could not initialize proxy [com.dxsoft.jpa.helloword.Person#1] - no Session

EntityManager#remove

类似Hibernate的delete方法,但是不同之处hibernate允许删除游离和持久化对象。但是JPA只允许删除持久化对象。

JPA删除游离对象会抛出异常:

    @Test
    public void testDelete() {
        Person person = new Person();
        person.setId(1);
        entityManager.remove(person);
    }

抛出异常:

java.lang.IllegalArgumentException: Removing a detached instance com.dxsoft.jpa.helloword.Person#1

允许删除持久化对象。

    @Test
    public void testDelete() {
        Person person = entityManager.find(Person.class, 1);        
        entityManager.remove(person);
    }

执行日志:

Hibernate: 
    select
        person0_.id as id1_0_0_,
        person0_.age as age2_0_0_,
        person0_.birth as birth3_0_0_,
        person0_.createTime as createTi4_0_0_,
        person0_.full_name as full_nam5_0_0_ 
    from
        jpa_person person0_ 
    where
        person0_.id=?
Hibernate: 
    delete 
    from
        jpa_person 
    where
        id=?
View Code

EntityManager#merge

merge方法类似于hibernate中的saveOrUpdate方法,但是比hibernate中的saveOrUpdate方法要复杂。

merge(T entity):merge()用于处理Enitty的同步。即数据库的插入和更新操作。

1)如果传入的是一个临时对象,会创建一个新的对象,把临时对象的属性复制到新的对象中,然后对新的对象进行持久化操作。所以新的对象中有id,但临时对象中没有id。

    @Test
    public void testMerge1() {
        Person person = new Person();
        person.setFullName("AA");
        person.setAge(20);
        person.setBirth(new Date());
        person.setCreateTime(new Date());

        Person person2 = entityManager.merge(person);

        System.out.println("person id:" + person.getId());
        System.out.println("person2 id:" + person2.getId());
    }

执行打印结果:

Hibernate: 
    select
        next_val as id_val 
    from
        hibernate_sequence for update
            
Hibernate: 
    update
        hibernate_sequence 
    set
        next_val= ? 
    where
        next_val=?
person id:null
person2 id:2
Hibernate: 
    insert 
    into
        jpa_person
        (age, birth, createTime, full_name, id) 
    values
        (?, ?, ?, ?, ?)
View Code

2)若传入的是一个游离对象(即传入的对象有id),

2.1)如果在EntityManager缓存中没有该对象,

2.2)且在数据中也没有对应的记录,

2.3)则JPA会创建一个新的对象,然后把当前游离对象的属性复制到新创建的对象中,

2.4)对新创建的对象执行insert操作。

    @Test
    public void testMerge2() {
        Person person = new Person();
        person.setFullName("AA");
        person.setAge(20);
        person.setBirth(new Date());
        person.setCreateTime(new Date());
        
        // 此时数据中没有id为100的记录。
        person.setId(100);
        
        Person person2 = entityManager.merge(person);

        System.out.println("person id:" + person.getId());
        System.out.println("person2 id:" + person2.getId());
    }

打印记录:

Hibernate: 
    select
        person0_.id as id1_0_0_,
        person0_.age as age2_0_0_,
        person0_.birth as birth3_0_0_,
        person0_.createTime as createTi4_0_0_,
        person0_.full_name as full_nam5_0_0_ 
    from
        jpa_person person0_ 
    where
        person0_.id=?
Sun Jun 24 18:31:42 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Hibernate: 
    select
        next_val as id_val 
    from
        hibernate_sequence for update
            
Hibernate: 
    update
        hibernate_sequence 
    set
        next_val= ? 
    where
        next_val=?
person id:100
person2 id:3
Hibernate: 
    insert 
    into
        jpa_person
        (age, birth, createTime, full_name, id) 
    values
        (?, ?, ?, ?, ?)
View Code

3)若传入的是一个游离对象(即传入的对象有id),

3.1)如果在EntityManager缓存中没有该对象,

3.2)但在数据中有对应的记录,

3.3)则JPA会查询对应的记录,然后返回该记录对应的一个对象,然后把游离对象的属性复制到查询返回对象中。

3.4)对查询返回的对象执行update操作。

    @Test
    public void testMerge3() {
        Person person = new Person();
        person.setFullName("EE");
        person.setAge(20);
        person.setBirth(new Date());
        person.setCreateTime(new Date());

        // 此时数据中没有id为100的记录。
        person.setId(3);

        Person person2 = entityManager.merge(person);

        System.out.println(person2 == person);
    }

执行前数据库中数据记录:

执行后数据库中数据记录:

执行打印结果:

Hibernate: 
    select
        person0_.id as id1_0_0_,
        person0_.age as age2_0_0_,
        person0_.birth as birth3_0_0_,
        person0_.createTime as createTi4_0_0_,
        person0_.full_name as full_nam5_0_0_ 
    from
        jpa_person person0_ 
    where
        person0_.id=?
false
Hibernate: 
    update
        jpa_person 
    set
        age=?,
        birth=?,
        createTime=?,
        full_name=? 
    where
        id=?
View Code

4)若传入的是一个游离对象(即传入的对象有id),

4.1)如果在EntityManager缓存中有该对象,

4.2)则JPA会把游离对象的属性复制到EntityManager缓存中的对象中。

4.3)EntityManager缓存中的对象执行update操作。

注意:hibernate不允许Session在同一个时刻将两个id相同的对象进行关联,但是JPA允许。

hibernate将抛出异常:

    @Test
    public void testMerge4() {
        Person person = new Person();
        person.setFullName("FF");
        person.setAge(20);
        person.setBirth(new Date());
        person.setCreateTime(new Date());

        person.setId(3);
        
        Person person2 = entityManager.find(Person.class, 3);
        entityManager.merge(person);

        System.out.println(person2 == person);
    }

执行前数据中记录:

执行后数据库中记录:

执行打印记录:

Hibernate: 
    select
        person0_.id as id1_0_0_,
        person0_.age as age2_0_0_,
        person0_.birth as birth3_0_0_,
        person0_.createTime as createTi4_0_0_,
        person0_.full_name as full_nam5_0_0_ 
    from
        jpa_person person0_ 
    where
        person0_.id=?
false
Hibernate: 
    update
        jpa_person 
    set
        age=?,
        birth=?,
        createTime=?,
        full_name=? 
    where
        id=?
View Code

EntityManager其他方法

flush():同步持久化上下文环境,即将持久化上下文环境的所有未保存的状态信息保存到数据库中。

setFlushMode(FlushModeType flushMode):设置持久上下文环境的Flush模式。参数可以取两个枚举值:

--- FlushModeType.AUTO:为自动更新数据库实体;

--- FlushModeType.COMMIT:为直接提交事务时才更新数据库记录。

getFlushMode():获取持久上下文环境的Flush模式。返回FlushModeType类的枚举值。

    @Test
    public void testFlush() {
        Person person = entityManager.find(Person.class, 3);
        person.setFullName("Flush Method");

        //entityManager.flush();
        System.out.println(entityManager.getFlushMode());
        System.out.println("---------------------");
    }

此时从打印信息可以得知,知道transaction.commit();执行才执行update,

Hibernate: 
    select
        person0_.id as id1_0_0_,
        person0_.age as age2_0_0_,
        person0_.birth as birth3_0_0_,
        person0_.createTime as createTi4_0_0_,
        person0_.full_name as full_nam5_0_0_ 
    from
        jpa_person person0_ 
    where
        person0_.id=?
AUTO
---------------------
Hibernate: 
    update
        jpa_person 
    set
        age=?,
        birth=?,
        createTime=?,
        full_name=? 
    where
        id=?
View Code

开启刷新同步持久化上下文环境:

    @Test
    public void testFlush() {
        Person person = entityManager.find(Person.class, 3);
        person.setFullName("Flush_Method");

        entityManager.flush();
        System.out.println(entityManager.getFlushMode());
        System.out.println("---------------------");
    }

此时执行信息如下:

Hibernate: 
    select
        person0_.id as id1_0_0_,
        person0_.age as age2_0_0_,
        person0_.birth as birth3_0_0_,
        person0_.createTime as createTi4_0_0_,
        person0_.full_name as full_nam5_0_0_ 
    from
        jpa_person person0_ 
    where
        person0_.id=?
Hibernate: 
    update
        jpa_person 
    set
        age=?,
        birth=?,
        createTime=?,
        full_name=? 
    where
        id=?
AUTO
---------------------
View Code

refresh(Object Entity):用数据库实体记录的值更新实体对象的状态,即更新实例的属性值。

clear():清除持久上下文环境,断开所有关联的实体。如果这时还有未提交的更新则会被撤销。

contains(Object entity):判断一个实体是否属于当前持久上下文环境管理的实体。

isOpen():判断当前的实体管理器是否是打开状态。

getTransaction():返回资源层的事务对象。EntityTransaction实例可以用于开始和提交多个事务。

close():关闭实体管理器。之后若调用实体管理器实例的方法或其派生的查询对象的方法都将抛出IlleglastateException异常,除了getTransaction和isOpen方法(返回false)。不过,当与实体管理器关闭的事务处于活动状态时,调用close方法后持久上下文将仍处于被管理状态,直到事务完成。

    @Test
    public void testRefresh() {
        Person person = entityManager.find(Person.class, 3);
        person = entityManager.find(Person.class, 3);
    }

此时如果没有开启refresh,则会由于JPA存在与Hibernate相似的一级缓存存在的原因,导致值查询数据库一次,从以下打印信息可以看出:

Hibernate: 
    select
        person0_.id as id1_0_0_,
        person0_.age as age2_0_0_,
        person0_.birth as birth3_0_0_,
        person0_.createTime as createTi4_0_0_,
        person0_.full_name as full_nam5_0_0_ 
    from
        jpa_person person0_ 
    where
        person0_.id=?
View Code

打开refresh,测试执行:

    @Test
    public void testRefresh() {
        Person person = entityManager.find(Person.class, 3);
        person = entityManager.find(Person.class, 3);

        entityManager.refresh(person);
    }

此时,打印日志中会两次查询:

Hibernate: 
    select
        person0_.id as id1_0_0_,
        person0_.age as age2_0_0_,
        person0_.birth as birth3_0_0_,
        person0_.createTime as createTi4_0_0_,
        person0_.full_name as full_nam5_0_0_ 
    from
        jpa_person person0_ 
    where
        person0_.id=?
Hibernate: 
    select
        person0_.id as id1_0_0_,
        person0_.age as age2_0_0_,
        person0_.birth as birth3_0_0_,
        person0_.createTime as createTi4_0_0_,
        person0_.full_name as full_nam5_0_0_ 
    from
        jpa_person person0_ 
    where
        person0_.id=?
View Code

createQuery(String qlString):创建一个查询对象。

createNamedQuery(String name):根据命名的查询语句块创建查询对象。参数为命名的查询语句。

createNativeQuery(String sqlString):使用标准sql语句创建查询对象。参数为标准sql语句字符串。

createNativeQuery(String sqls,String resultSetMapping):使用标准sql语句创建查询对象,并指定返回结果集Map的名称。

EntityTransaction

EntityTransaction接口用来管理资源层实体管理器的事务操作。通过调用实体管理器的getTransaction方法获得其实力。

begin():用于启动一个事务,此后的多个数据库操作将作为整体被提交或撤销。若这时事务已经开启则会抛出IllegalStateException异常。

commit():用于提交当前事务。即将事务启动以后的所有数据库更新操作持久化值数据中。

rollback():撤销(回滚)当前事务,即撤销事务启动后的所有数据库更新操作,从而不对数据库产生影响。

setRollbackOnly():使当前事务只能被撤销。

getRollbackOnly():查看当前事务是否设置了只能撤销标志。

isActive():查看当前事务是否是活动的。如果返回true则不能调用begin方法,否则将抛出IllegalStateException异常;如果返回false则不能调用commit、rollback、setRollbackOnly及getRollbackOnly方法,否则将抛出IllegalStateException异常。

原文地址:https://www.cnblogs.com/yy3b2007com/p/9195903.html