EntityManager經常使用API

JPA實體狀態分析java

實體狀態詳解docker

  • 瞬時狀態:
  1. 實際上就是new了一個普通的JavaBean對象。
  • 託管狀態:
  1. 1.當1.瞬時對象調用了管理器的persist()後,便可將通常的JavaBean作爲了持久Bean,該Bean的任何屬性改動都會牽涉到數據庫記錄的改動。
  2.  
  3. 2.一旦該記錄flush到數據庫以後,而且事務提交了,那麼此對象不在持久化上下文中,即:變爲了遊離(沒人管的孩子)狀態了。
  4. 在遊離狀態的時候調用更新、刷新方法後,遊離狀態對象就變爲了在持久化上下文的託管狀態了。
  5.  
  6. 3.經過管理器的find方法,將實體從數據庫查詢出來後,該實體也就變爲了託管形態。

    持久化狀態:數據庫

  1. 當處在託管狀態的實體Bean被管理器flush了,那麼就在極短暫的時間進入了持久化狀態,事務提交以後,馬上變爲了遊離狀態。
  2. 您能夠把持久化狀態當作實實在在的數據庫記錄。
  • 遊離狀態:
  1. 遊離狀態就是提交到數據庫後,事務commit後實體的狀態,由於事務已經提交了,此時實體的屬性任你如何改變,也不會同步到數據庫,
  2. 由於遊離是沒人管的孩子,不在持久化上下文中。
  • 銷燬對象:
  1. 通常要刪除一個持久化對象的時候都是先find出來,以後調用remove方法刪之,此時這個對象就是銷燬對象,實際上就是瞬時對象的另外一種形態罷了。

EntityManager一些經常使用的API(包含query, insert, update, delete操做)緩存

1)get entity —— find() or getReference()服務器

  1. Person person = em.find(Person.class,1);
  • 當在數據庫中沒有找到記錄時,getReference()和find()是有區別的,
    • find()方法會返回null,
    • 而getReference() 方法會拋出javax.persistence.EntityNotFoundException例外,
  • 調用getReference()方法,返回的其實並非實例對象,而是一個代理。當你要使用實體時,纔會真正的調用查詢語句來查詢實例對象
  • 另外getReference()方法不保證 entity Bean已被初始化。
  • 若是傳遞進getReference()或find()方法的參數不是實體Bean,都會引起 IllegalArgumentException

2)insert —— persist()網絡

  1. Person person = new Person();
  2. person.setName(name);
  3. //把數據保存進數據庫中
  4. em.persist(person);
  • persist方法的:使對象由臨時狀態變爲持久化狀態
  • 若是傳遞進persist()方法的參數不是實體Bean,會引起IllegalArgumentException
  • 和hibernate的save()方法有些不一樣:若是對象有id,則不能執行insert操做,會拋出異常

3)update —— 分2種狀況
    狀況1:當實體正在被容器管理時,你能夠調用實體的set方法對數據進行修改,在容器決定flush時(這個由Container自行判斷),更新的數據 纔會同步到數據庫,而不是在調用了set方法對數據進行修改後立刻同步到數據庫。若是你但願修改後的數據立刻同步到數據庫,你能夠調用EntityManager.flush()方法。session

  1. public void updatePerson() {
  2. try {
  3. Person person = em.find(Person.class, 1);
  4. person.setName("lihuoming"); //方法執行完後便可更新數據
  5. } catch (Exception e) {
  6. e.printStackTrace();
  7. }
  8. }


    狀況2:在實體Bean已經脫離了EntityManager的管理時,你調用實體的set方法對數據進行修改是沒法同步更改到數據庫的。你必須調用 EntityManager.merge()方法。調用以後,在容器決定flush時(這個由container自行判斷),更新的數據纔會同步到數據 庫。若是你但願修改後的數據立刻同步到數據庫,你能夠調用EntityManager.flush()方法性能

  1. public boolean updatePerson(Person person) {
  2. try {
  3. em.merge(person);
  4. } catch (Exception e) {
  5. e.printStackTrace();
  6. return false;
  7. }
  8. return true;
  9. }

    下面的代碼會調用上面的方法。由於下面的第二行代碼把實體Bean 返回到了客戶端,這時的實體Bean已經脫離了容器的管理,在客戶端對實體Bean進行修改,最後把他返回給EJB 容器進行更新操做:編碼

  1. PersonDAO persondao = (PersonDAO) ctx.lookup("PersonDAOBean/remote");
  2. Person person = persondao.getPersonByID(1); //此時的person 已經脫離容器的管理
  3. person.setName("張小豔");
  4. persondao.updatePerson(person);
  • merge 狀況1:傳入的對象沒有id
  1. 在這種狀況下,調用merge方法,將返回一個新的對象(有id),並對這個新的對象執行insert操做。
  • merge 狀況2:傳入的對象有id,entityManager的緩存中沒有該對象,數據庫中沒有該記錄:
  1. 在這種狀況下,調用merge方法,將返回一個新的對象,並對該對象執行insert操做。
  2. 新對象的id是數據庫中這條記錄的id(好比自增加的id,而不是咱們本身傳入的id。(其實和狀況1的結果是同樣的)
  • merge 狀況3:傳入的對象有id,entityManager的緩存沒有該對象,數據庫中有該記錄
  1. 在這種狀況下,調用merge方法,將會從數據庫中查詢對應的記錄,生成新的對象,而後將咱們傳入的對象複製到新的對象,最後執行update操做。 
  2. 簡單來講,就是更新操做
  • merge 狀況4:傳入的對象有id,entityManager的緩存有該對象
  1. 在這種狀況下,調用merge方法,JPA會把傳入的對象賦值到entityManager的緩存中的對象,而後對entityManager緩存中的對象執行update操做。
  2. (和狀況3的結果同樣)


4)Delete —— Remove().net

  1. Person person = em.find(Person.class, 2);
  2. //若是級聯關係cascade=CascadeType.ALL,在刪除person 時候,也會把級聯對象刪除。
  3. //把cascade屬性設爲cascade=CascadeType.REMOVE 有一樣的效果。
  4. em.remove (person);
  • 若是傳遞進remove ()方法的參數不是實體Bean,會引起一個IllegalArgumentException
  • remove()方法不能移除遊離對象,只能移除持久化對象
    1. Order order = new Order();
    2. order.setId(140);
    3. entityManager.remove(order);

    上面這段代碼會拋出異常,由於order是咱們本身建立的對象,也就是遊離對象。必須這樣寫:

     

    1. Order order = new Order();
    2. order = entityManager.find(Order.class, 140);
    3. entityManager.remove(order);

    這段代碼中的order是從數據庫中獲取的,也就是持久化對象

  • hibernate的delete()方法,只要對象有Id,就能夠刪除

5)HPQL query —— createQuery()
除了使用find()或getReference()方法來得到Entity Bean以外,你還能夠經過JPQL獲得實體Bean。

  • 要執行JPQL語句,你必須經過EntityManager的createQuery()或createNamedQuery()方法建立一個Query 對象
  • // 執行查詢語句
  1. Query query = em.createQuery("select p from Person p where p. name=’黎明’");
  2. List result = query.getResultList();
  3. Iterator iterator = result.iterator();
  4. while( iterator.hasNext() ){
  5. //處理Person
  6. }
  • // 執行更新語句
  1. Query query = em.createQuery("update Person as p set p.name =?1 where p. personid=?2");
  2. query.setParameter(1, 「黎明」);
  3. query.setParameter(2, new Integer(1) );
  4. int result = query.executeUpdate(); //影響的記錄數
  • // 執行刪除語句
  1. Query query = em.createQuery("delete from Person");
  2. int result = query.executeUpdate(); //影響的記錄數

6)SQL query —— createNaiveQuery()

  • 注意:該方法是針對SQL語句,而不是HPQL語句
  1. //咱們可讓EJB3 Persistence 運行環境將列值直接填充入一個Entity 的實例,
  2. //並將實例做爲結果返回.
  3. Query query = em.createNativeQuery("select * from person", Person.class);
  4. List result = query.getResultList();
  5. if (result!=null){
  6. Iterator iterator = result.iterator();
  7. while( iterator.hasNext() ){
  8. Person person= (Person)iterator.next();
  9. … ..
  10. }
  11. }
  • // 直接經過SQL 執行更新語句
  1. Query query = em.createNativeQuery("update person set age=age+2");
  2. query.executeUpdate();

7)Refresh entity —— refresh()

  1. Order order= entityManager.find(Order.class, 170);
  2. order= entityManager.find(Order.class, 170);
  • 運行以上代碼,發現調用了兩次find,可是隻執行了一次select語句,這是緩存致使的。
  1. Order order= entityManager.find(Order.class, 170);
  2. entityManager.refresh(order);
  • 只調用了一次find方法,卻執行了兩次select語句,這是由於refresh方法會去查看緩存中的數據狀態和數據庫中是否一致,所以又執行了一次select語句
  • 若是你懷疑當前被管理的實體已經不是數據庫中最新的數據,你能夠經過refresh()方法刷新實體,容器會把數據庫中的新值重寫進實體。
  • 這種狀況通常發 生在你獲取了實體以後,有人更新了數據庫中的記錄,這時你須要獲得最新的數據。
  • 固然你再次調用find()或getReference()方法也能夠獲得 最新數據,但這種作法並不優雅。
  1. Person person = em.find(Person.class, 2);
  2. //若是此時person 對應的記錄在數據庫中已經發生了改變,
  3. //能夠經過refresh()方法獲得最新數據。
  4. em.refresh (person);

8)Check entity是否在EntityManager管理當中 —— contains()

  • 判斷一個實例是否屬於當前持久上下文環境管理的實體
  • contains()方法使用一個實體做爲參數,若是這個實體對象當前正被持久化內容管理,返回值爲true,不然爲false。
  • 若是傳遞的參數不是實體 Bean,將會引起一個IllegalArgumentException.
  1.  
  2. Person person = em.find(Person.class, 2);
  3. 。。。
  4. if (em.contains(person)){
  5. //正在被持久化內容管理
  6. }else{
  7. //已經不受持久化內容管理
  8. }


9)分離全部當前正在被管理的實體 —— clear()

  • 清除持久上下文環境,斷開全部關聯的實體。若是這時還有未提交的更新則會被撤消。
  • 在處理大量實體的時候,若是你不把已經處理過的實體從EntityManager中分離出來,將會消耗你大量的內存。
  • 調用EntityManager 的clear()方法後,全部正在被管理的實體將會從持久化內容中分離出來。
  • 有一點須要說明下,在事務沒有提交前(事務默認在調用堆棧的最後提交,如:方 法的返回),若是調用clear()方法,以前對實體所做的任何改變將會掉失,
  • 因此建議你在調用clear()方法以前先調用flush()方法保存更 改

10)將實體的改變馬上刷新到數據庫中 —— flush()

  • 當EntityManager對象在一個session bean 中使用時,它是和服務器的事務上下文綁定的。
  • EntityManager在事務提交時而且同步它的內容。
  • 在一個session bean 中,服務器的事務默認地會在調用堆棧的最後提交(如:方法的返回)。
  1. 例子1:在方法返回時才提交事務
  2. public void updatePerson(Person person) {
  3. try {
  4. Person person = em.find(Person.class, 2);
  5. person.setName("lihuoming");
  6. em.merge(person);
  7. //後面還有衆多修改操做
  8. } catch (Exception e) {
  9. e.printStackTrace();
  10. }
  11. //更新將會在這個方法的末尾被提交和刷新到數據庫中
  12. }
  • 爲了只在當事務提交時纔將改變動新到數據庫中,容器將全部數據庫操做集中到一個批處理中,這樣就減小了代價昂貴的與數據庫的交互。
  • 當你調用 persist( ), merge( )或remove( )這些方法時,更新並不會馬上同步到數據庫中,直到容器決定刷新到數據庫中時纔會執行,
  • 默認狀況下,容器決定刷新是在「相關查詢」執行前或事務提交時發 生,
  • 固然「相關查詢」除find()和getreference()以外,這兩個方法是不會引發容器觸發刷新動做的,默認的刷新模式是能夠改變的。


若是你須要在事務提交以前將更新刷新到數據庫中,你能夠直接地調用EntityManager.flush()方法。

這種狀況下,你能夠手工地來刷新數據 庫以得到對數據庫操做的最大控制。

  1. public void updatePerson(Person person) {
  2. try {
  3. Person person = em.find(Person.class, 2);
  4. person.setName("lihuoming");
  5. em.merge(person);
  6. em.flush();//手動將更新馬上刷新進數據庫
  7.  
  8. //後面還有衆多修改操做
  9. } catch (Exception e) {
  10. e.printStackTrace();
  11. }
  12. }

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

  • setFlushMode()的Flush模式有2種類型:AUTO and COMMIT。AUTO爲缺省模式。你能夠改變他的值,以下:
  1. entityManager.setFlushMode(FlushModeType.COMMIT);
  • FlushModeType.AUTO:
  1. 刷新在查詢語句執行前(除了find()和getreference()查詢)或事務提交時才發生,
  2. 使用場合:在 大量更新數據的過程當中沒有任何查詢語句(除了find()和getreference()查詢)的執行。
  • FlushModeType.COMMIT:
  1. 刷新只有在事務提交時才發生,
  2. 使用場合:在大量更新數據的過程當中存在查詢語句(除了find()和 getreference()查詢)的執行。
  • 其實上面兩種模式最終反映的結果是:JDBC 驅動跟數據庫交互的次數。
  1. JDBC 性能最大的增進是減小JDBC 驅動與數據庫之間的網絡通信。
  2. FlushModeType.COMMIT模式使更新只在一次的網絡交互中完成,而FlushModeType.AUTO 模式可能須要屢次交互(觸發了多少次Flush 就產生了多少次網絡交互)

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

  • 用過getDelegate()方法,你能夠獲取EntityManager持久化實現者的引用,
  • 如Jboss EJB3的持久化產品採用Hibernate,能夠經過getDelegate()方法獲取對他的訪問,如:
  1. HibernateEntityManager manager = (HibernateEntityManager)em.getDelegate();
  • 得到對Hibernate的引用後,能夠直接面對Hibernate進行編碼,不過這種方法並不可取,強烈建議不要使用。

13)判斷當前的實體管理器是不是打開狀態--isOpen ()

14)返回資源層的事務對象。EntityTransaction實例能夠用於開始和提交多個事務--getTransaction ()

15)大量數據分批提交

  1. 有的時候咱們須要循環保存數據,當保存大量數據的時候,
  2. 若是到最後才提交全部數據,那麼數據庫的負載可能會比較大。咱們能夠這樣作,每30個記錄就提交(flush)一次。

代碼以下(每到30條記錄的時候就強制提交):

  1. public void updateBatch(List<Z> list) {
  2. for (int i = 0; i < list.size(); i++) {
  3. entityManager.merge(list.get(i)); //變成託管狀態
  4. if (i % 30 == 0) {
  5. entityManager.flush(); //變成持久化狀態
  6. entityManager.clear(); //變成遊離狀態
  7. }
  8. }
  9. }
  10.  
  11. public void saveBatch(List<Z> list) {
  12. for (int i = 0; i < list.size(); i++) {
  13. entityManager.persist(list.get(i)); //變成託管狀態
  14. if (i % 30 == 0) {
  15. entityManager.flush(); //變成持久化狀態
  16. entityManager.clear(); //變成遊離狀態
  17. }
  18. }
  19. }

    參考來源: http://blog.csdn.net/fobdddf/article/details/19479073

    參考來源: http://blog.csdn.net/dufufd/article/details/54408727

    參考來源:http://toknowme.iteye.com/blog/2235981

相關文章
相關標籤/搜索