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異常。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM