EntityManager使用方法

ldear發表於2017-08-31
原文地址:EntityManager使用方法作者:風神

EntityManager


Session bean or MD bean對Entitybean的操作(包括所有的query, insert, update,delete操作)都是通過EntityManager例項來完成的。EntityManager是由EJB容器自動地管理和配置的,不需要使用者自己建立。

那麼Session bean or MD bean如何獲得EntityManager例項呢??
非常簡單,就是通過下列程式碼進行依賴注入:
Public class sessionbean1{
@PersistenceContext
EntityManager em;
。。。
}

注意:如果persistence.xml檔案中配置了多個<persistence-unit>。那麼在注入EntityManager物件時必須指定持久化名稱,通過@PersistenceContext註釋的unitName屬性進行指定,例:

@PersistenceContext(unitName="foshanshop")
EntityManager em;

如果只有一個<persistence-unit>,不需要明確指定。


請注意:EntityBean被EntityManager管理時,EntityManager會跟蹤他的狀態改變,在任何決定更新實體Bean的時候便會把發生改變的值同步到資料庫中(跟hibernate一樣)。但是如果entityBean從EntityManager分離後,他是不受管理的,EntityManager無法跟蹤他的任何狀態改變。


EntityManager一些常用的API(包含query, insert, update, delete操作)

1)get entity —— find() or getReference()
Person person = em.find(Person.class,1);

當在資料庫中沒有找到記錄時,getReference()和find()是有區別的,find()方法會返回null,而getReference()方法會丟擲javax.persistence.EntityNotFoundException例外,另外getReference()方法不保證entity Bean已被初始化。如果傳遞進getReference()或find()方法的引數不是實體Bean,都會引發IllegalArgumentException例外

2)insert —— persist()
Person person = new Person();
person.setName(name);
//把資料儲存進資料庫中
em.persist(person);

如果傳遞進persist()方法的引數不是實體Bean,會引發IllegalArgumentException

3)update —— 分2種情況
情況1:當實體正在被容器管理時,你可以呼叫實體的set方法對資料進行修改,在容器決定flush時(這個由Container自行判斷),更新的資料才會同步到資料庫,而不是在呼叫了set方法對資料進行修改後馬上同步到資料庫。如果你希望修改後的資料馬上同步到資料庫,你可以呼叫EntityManager.flush()方法。
public void updatePerson() {
try {
Person person = em.find(Person.class, 1);
person.setName("lihuoming"); //方法執行完後即可更新資料
} catch (Exception e) {
e.printStackTrace();
}
}


   情況2:在實體Bean已經脫離了EntityManager的管理時,你呼叫實體的set方法對資料進行修改是無法同步更改到資料庫的。你必須呼叫EntityManager.merge()方法。呼叫之後,在容器決定flush時(這個由container自行判斷),更新的資料才會同步到資料庫。如果你希望修改後的資料馬上同步到資料庫,你可以呼叫EntityManager.flush()方法。
   
public boolean updatePerson(Person person) {
try {
em.merge(person);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}

下面的程式碼會呼叫上面的方法。因為下面的第二行程式碼把實體Bean返回到了客戶端,這時的實體Bean已經脫離了容器的管理,在客戶端對實體Bean進行修改,最後把他返回給EJB容器進行更新操作:

PersonDAO persondao = (PersonDAO)ctx.lookup("PersonDAOBean/remote");
Person person = persondao.getPersonByID(1); //此時的person已經脫離容器的管理
person.setName("張小豔");
persondao.updatePerson(person);

執行em.merge(person)方法時,容器的工作規則:
1>    如果此時容器中已經存在一個受容器管理的具有相同ID的person例項,容器將會把引數person的內容拷貝進這個受管理的例項,merge()方法返回受管理的例項,但引數person仍然是分離的不受管理的。容器在決定Flush時把例項同步到資料庫中。

2>容器中不存在具有相同ID的person例項。容器根據傳進的person引數Copy出一個受容器管理的person例項,同時merge()方法會返回出這個受管理的例項,但引數person仍然是分離的不受管理的。容器在決定Flush時把例項同步到資料庫中。

如果傳遞進merge()方法的引數不是實體Bean,會引發一個IllegalArgumentException。


4)Delete —— Remove()
Person person = em.find(Person.class, 2);
//如果級聯關係cascade=CascadeType.ALL,在刪除person 時候,也會把級聯物件刪除。
//把cascade屬性設為cascade=CascadeType.REMOVE 有同樣的效果。
em.remove (person);

如果傳遞進remove()方法的引數不是實體Bean,會引發一個IllegalArgumentException


5)HPQL query —— createQuery()

除了使用find()或getReference()方法來獲得EntityBean之外,你還可以通過JPQL得到實體Bean。

要執行JPQL語句,你必須通過EntityManager的createQuery()或createNamedQuery()方法建立一個Query物件

Query query = em.createQuery("select p from Person p where p.name=’黎明’");
List result = query.getResultList();
Iterator iterator = result.iterator();
while( iterator.hasNext() ){
//處理Person
}

// 執行更新語句
Query query = em.createQuery("update Person as p set p.name =?1where p. personid=?2");
query.setParameter(1, “黎明”);
query.setParameter(2, new Integer(1) );
int result = query.executeUpdate(); //影響的記錄數

// 執行更新語句
Query query = em.createQuery("delete from Person");
int result = query.executeUpdate(); //影響的記錄數


6)SQL query —— createNaiveQuery()
注意:該方法是針對SQL語句,而不是HPQL語句

//我們可以讓EJB3 Persistence 執行環境將列值直接填充入一個Entity 的例項,
//並將例項作為結果返回.
Query query = em.createNativeQuery("select * from person",Person.class);
List result = query.getResultList();
if (result!=null){
Iterator iterator = result.iterator();
while( iterator.hasNext() ){
Person person= (Person)iterator.next();
… ..
}
}

// 直接通過SQL 執行更新語句
Query query = em.createNativeQuery("update person setage=age+2");
query.executeUpdate();


7)Refresh entity —— refresh()
如果你懷疑當前被管理的實體已經不是資料庫中最新的資料,你可以通過refresh()方法重新整理實體,容器會把資料庫中的新值重寫進實體。這種情況一般發生在你獲取了實體之後,有人更新了資料庫中的記錄,這時你需要得到最新的資料。當然你再次呼叫find()或getReference()方法也可以得到最新資料,但這種做法並不優雅。

Person person = em.find(Person.class, 2);
//如果此時person 對應的記錄在資料庫中已經發生了改變,
//可以通過refresh()方法得到最新資料。
em.refresh (person);


8)Check entity是否在EntityManager管理當中 —— contains()
contains()方法使用一個實體作為引數,如果這個實體物件當前正被持久化內容管理,返回值為true,否則為false。如果傳遞的引數不是實體Bean,將會引發一個IllegalArgumentException.

Person person = em.find(Person.class, 2);
。。。
if (em.contains(person)){
//正在被持久化內容管理
}else{
//已經不受持久化內容管理
}


9)分離所有當前正在被管理的實體 —— clear()
在處理大量實體的時候,如果你不把已經處理過的實體從EntityManager中分離出來,將會消耗你大量的記憶體。呼叫EntityManager的clear()方法後,所有正在被管理的實體將會從持久化內容中分離出來。有一點需要說明下,在事務沒有提交前(事務預設在呼叫堆疊的最後提交,如:方法的返回),如果呼叫clear()方法,之前對實體所作的任何改變將會掉失,所以建議你在呼叫clear()方法之前先呼叫flush()方法儲存更改。


10)            將實體的改變立刻重新整理到資料庫中 —— flush()
當EntityManager物件在一個session bean中使用時,它是和伺服器的事務上下文繫結的。EntityManager在伺服器的事務提交時提交併且同步它的內容。在一個sessionbean 中,伺服器的事務預設地會在呼叫堆疊的最後提交(如:方法的返回)。

例子1:在方法返回時才提交事務
public void updatePerson(Person person) {
try {
Person person = em.find(Person.class, 2);
person.setName("lihuoming");
em.merge(person);
//後面還有眾多修改操作
} catch (Exception e) {
e.printStackTrace();
}
//更新將會在這個方法的末尾被提交和重新整理到資料庫中
}

為了只在當事務提交時才將改變更新到資料庫中,容器將所有資料庫操作集中到一個批處理中,這樣就減少了代價昂貴的與資料庫的互動。當你呼叫persist( ), merge( )或remove()這些方法時,更新並不會立刻同步到資料庫中,直到容器決定重新整理到資料庫中時才會執行,預設情況下,容器決定重新整理是在“相關查詢”執行前或事務提交時發生,當然“相關查詢”除find()和getreference()之外,這兩個方法是不會引起容器觸發重新整理動作的,預設的重新整理模式是可以改變的,具體請
考參下節。

如果你需要在事務提交之前將更新重新整理到資料庫中,你可以直接地呼叫EntityManager.flush()方法。這種情況下,你可以手工地來重新整理資料庫以獲得對資料庫操作的最大控制。
public void updatePerson(Person person) {
try {
Person person = em.find(Person.class, 2);
person.setName("lihuoming");
em.merge(person);
em.flush();//手動將更新立刻重新整理進資料庫

//後面還有眾多修改操作
} catch (Exception e) {
e.printStackTrace();
}
}


11)            改變實體管理器的Flush模式 —— setFlushMode()

的Flush模式有2種型別:AUTO and COMMIT。AUTO為預設模式。你可以改變他的值,如下:
entityManager.setFlushMode(FlushModeType.COMMIT);

FlushModeType.AUTO:重新整理在查詢語句執行前(除了find()和getreference()查詢)或事務提交時才發生,使用場合:在大量更新資料的過程中沒有任何查詢語句(除了find()和getreference()查詢)的執行。

FlushModeType.COMMIT:重新整理只有在事務提交時才發生,使用場合:在大量更新資料的過程中存在查詢語句(除了find()和getreference()查詢)的執行。

其實上面兩種模式最終反映的結果是:JDBC 驅動跟資料庫互動的次數。JDBC 效能最大的增進是減少JDBC驅動與資料庫之間的網路通訊。FlushModeType.COMMIT模式使更新只在一次的網路互動中完成,而FlushModeType.AUTO模式可能需要多次互動(觸發了多少次Flush 就產生了多少次網路互動)

12)            獲取持久化實現者的引用 —— getDelegate()

用過getDelegate()方法,你可以獲取EntityManager持久化實現者的引用,如JbossEJB3的持久化產品採用Hibernate,可以通過getDelegate()方法獲取對他的訪問,如:
HibernateEntityManager manager =(HibernateEntityManager)em.getDelegate();

獲得對Hibernate的引用後,可以直接面對Hibernate進行編碼,不過這種方法並不可取,強烈建議不要使用。在Weblogic中,你也可以通過此方法獲取對Kodo 的訪問。

相關文章