1. 程式人生 > >持久化API(JPA)系列(四)管理器EntityManager--執行資料庫更新

持久化API(JPA)系列(四)管理器EntityManager--執行資料庫更新


        EntityManager是應用訪問持久化上下文中的實體的介面,用來對實體Bean進行操作。我們可以使用它來建立、刪除、修改持久化的實體,以體現到資料庫中;也可以從資料庫中查詢得到實體或實體列表。所有的這些操作都是通過實體管理器進行的。
本文將首先講解EntityManager物件的引用方式,然後講解使用EntityManager的操作函式實現資料庫的各種操作,包括以下內容。

持久化實體persist():往資料表中插入資料。

刪除實體remove():從資料表中刪除記錄。

更新實體merge():更新資料表記錄。

重新整理實體到資料庫flush()。

設定Flush重新整理模式setFlushMode()。

重新整理實體refresh():從資料表中更新。

按主鍵查詢實體find():從資料表中查詢記錄。

檢測實體是否被管理contains()。

分離管理的實體clear()。

=============================================================================
1、 EntityManager物件的引用方式
根據實體管理器應用的場景不同,可以將它分為兩種型別。
1)容器託管的EntityManager
容器託管的EntityManager物件必須在EJB容器中執行,而不能在Web容器和Java SE環境中執行。前一節的例項中的EntityManager物件就是通過注入@PersistenceContext註釋從容器中獲得的,這種獲得EntityManager物件的方式就是容器託管。這種方式使用起來最簡單,開發人員不需要考慮EntityManager物件的建立、事務等複雜問題,所有這些都交給容器去管理。取得該物件的方式如下:
@PersistenceContext(unitName="demo")  
EntityManager em; 
2)應用託管的EntityManager
應用託管的EntityManager物件可以在EJB容器中執行,也可以脫離EJB容器,而與任何的Java環境整合,比如說Web容器、Java SE環境等。它需要開發人員手動地控制它的建立、釋放、事務等,這些操作都比較複雜。取得該物件的方式如下:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("demo");  
EntityManager em = emf.getEntityManager(); 


因為我們公司的專案是執行在JBoss這個EJB容器中的,因此採用第一種方式來使用它。


==特點:更新並不會立刻同步到資料庫=====================================================

1、持久化實體persist()--往資料表中插入資料
DAO層主要的工作就是將EntityManager管理的實體持久化到資料庫中儲存起來,即將記憶體中的實體物件寫入到資料表中,在表中反應的是新增了一行記錄。
持久化的方法是:
em.persist(obj); 
類似於執行了以下SQL語句:
insert into student(name, sex, age, birthday, telephone, address) values('劉中兵', 1, 25, '1981-05-04', '12345678', '北京'); 

2、刪除實體remove()--從資料表中刪除記錄
當需要刪除一個已經被持久化到資料庫中的實體物件時
Object obj = em.find(clazz, id);  
em.remove(obj); 
例如:
    Student student = em.find(Student.class, 1);  
    em.remove(student);  
類似於執行了以下SQL語句:
delete from student where id=1; 

3、更新實體merge()--更新資料表記錄


更新一個已經被持久化的實體物件
Object updatedObj = em.merge(obj); 
例如: 
    Student student = em.find(Student.class, 1);  
    student.setName("liuzhongbing");  
    em.merge(student);  
類似於執行了以下SQL語句:
update student set name="liuzhongbing" where id=1; 

當執行em.merge(student)方法時,有如下兩個執行規則:
    如果此時容器中已經存在一個受容器管理的具有相同id的student例項,則容器將會把引數student的內容複製進這個受管理的例項,merge()方法返回受管理的例項,但引數student仍然是分離的、不受管理的。容器在決定Flush時把例項同步到資料庫中。
    容器中不存在具有相同id的student例項。容器根據傳進的student引數複製出一個受容器管理的student例項,同時merge()方法會返回出這個受管理的例項,但引數student仍然是分離的、不受管理的。容器在決定Flush時把例項同步到資料庫中。


==特點:重新整理實體同步到資料庫=========================================================
1、重新整理實體到資料庫flush()
當呼叫persist()、merge()和remove()這些方法時,更新並不會立刻同步到資料庫中,直到容器決定重新整理到資料庫中時才會執行。在預設情況下,容器決定重新整理是在"相關查詢"執行前或事務提交時發生的,當然"相關查詢"除find()和getReference()之外,這兩個方法是不會引起容器觸發重新整理動作的,如果你需要在事務提交之前將更新重新整理到資料庫中,你可以直接呼叫EntityManager.flush()方法。在這種情況下,你可以手動來重新整理資料庫以獲得對資料庫操作的最大控制。


當實體正在被容器管理時,你可以呼叫實體的setXXX()方法對資料進行修改,在容器決定Flush時,更新的資料才會同步到資料庫中。如果你希望修改後的資料即時同步到資料庫中,則可以執行flush()方法:
em. flush(obj); 
該函式將會做兩件事:
提交更改的欄位到資料庫。
重新整理資料庫中的欄位到該實體中。
例如:
    Student student = em.find(Student.class, 1);  
    student.setName("liuzhongbing");  
    em.flush(student);  

這與上面執行merge()的區別如下。
    提交的物件不同:flush()是將容器管理的物件提交到資料庫,而merge()不僅可以提交容器管理的物件,而且可以提交沒有被容器管理的物件。
    提交的時機不同:flush()會立即同步到資料庫,而merge()只會在容器需要Flush時執行同步。

2、設定Flush重新整理模式setFlushMode()
上面的flush()函式是手動呼叫的,如果不手動呼叫,則只能依賴於容器的自動重新整理。在預設情況下容器是自動重新整理的,這是因為它對應了重新整理了的AUTO值:
public enum FlushModeType {  
    AUTO,  
    COMMIT  

我們可以呼叫下面的方法改變重新整理模式:
em.setFlushMode(FlushModeType.COMMIT); 
這兩種模式的區別如下。
AUTO:重新整理在查詢語句執行前(除了find()和getreference()查詢)或事務提交時才發生,適用於在大量更新資料的過程中沒有任何查詢語句(除了find()和getreference()查詢)時執行。
COMMIT:重新整理只有在事務提交時才發生,適用於在大量更新資料的過程中存在查詢語句(除了find()和getreference()查詢)時執行。
這兩種模式的區別體現在資料庫底層SQL的執行上,即JDBC驅動跟資料庫互動的次數。COMMIT模式使更新只在一次網路互動中完成,而AUTO模式可能需要多次互動,它觸發了多少次Flush就產生了多少次網路互動。

==特點:獲取資料庫最新資料,並同步到實體=================================================
1、重新整理實體refresh()--從資料表中更新
如果當前被管理的實體已經不是資料庫中最新的資料,則可以通過refresh()方法重新整理實體,容器會把資料庫中的新值重寫進實體:
em. refresh(obj); 
例如: 
    Student student = em.find(Student.class, 1);  
    //如果此時student對應的記錄在資料庫中已經發生了改變,則可以通過refresh()方法得到最新資料:
    em. refresh(student);  


==特點:從實體中查詢資料=========================================================
1、按主鍵查詢實體find()--從資料表中查詢記錄
一旦將物件持久化到資料庫中,它就會產生一個主鍵,我們可以通過這個主鍵來查詢它。查詢的方法很簡單,只需要指定要查詢的id和查詢後物件的實體類,就可以取得該記錄的例項。
Object obj = em.find(clazz, id); 
其中clazz為實體Bean的類名。
還有一個方法getReference(),用法與find()方法相同,不同的是當資料庫中沒有找到記錄時,該方法將會丟擲異常不同。
例如:
Student student = em.find(Student.class, 1);  
類似於執行了以下SQL語句:
select * from student where id=1; 

==特點:檢視、分離被容器管理的實體====================================================
1、檢測實體是否被管理contains()
前文中的flush()和merge()分別針對實體是否被容器所管理進行了區分,所謂的被管理,就是實體管理器管理中的實體,沒有被管理,就是不在實體管理器容器範圍內,即沒有經過實體管理器的各種操作函式進行持久化。
實體管理器提供了一個方法contains(),用來檢查實體是否被管理。形式如下:
boolean b = em.contains(obj); 
contains()方法使用一個實體作為引數,如果這個實體物件當前正被持久化內容管理,則返回值為true,否則為false。
例如:
    Student student = em.find(Student.class, 1);  
    if (em.contains(person)){  
           //正在被持久化內容管理  
    }else{  
        //已經不受持久化內容管理  
    }  

2、分離管理的實體clear()
當處理了大量的實體後,這些實體都會存在於實體管理器中,這將會消耗大量的記憶體,使程式執行變慢。如果要減少消耗,則可以使用clear()方法,將正在被管理的實體從持久化內容中分離出來。如下所示:
    em.clear();  
如果呼叫clear()方法,則之前對實體所做的任何改變都將會被丟失,所以在呼叫clear()方法之前先呼叫flush()方法儲存更改。