JPA學習筆記(5)——EntityManager相關
Persistence
在之前的JPA學習筆記(2)——建立JPA專案,有使用到Persistence來建立EntityManagerFactory例項
String persistenceUnitName = "jpa"; EntityManagerFactory factory = Persistence.createEntityManagerFactory(persistenceUnitName);
- 1
- 2
其實它還有一個過載方法可用
Map<String, Object> properties = new HashMap<String, Object>();properties.put("hibernate.format_sql" , false);EntityManagerFactory factory= Persistence.createEntityManagerFactory(persistenceUnitName,properties);
- 1
- 2
- 3
這裡的properties,和配置檔案persistence.xml中的屬性是一樣的。
EntityManagerFactory
EntityManagerFactory 介面主要用來建立 EntityManager 例項。該介面約定了如下4個方法:
createEntityManager():用於建立實體管理器物件例項。
createEntityManager(Map map):用於建立實體管理器物件例項的過載方法,Map 引數用於提供 EntityManager 的屬性。
isOpen():檢查 EntityManagerFactory 是否處於開啟狀態。實體管理器工廠建立後一直處於開啟狀態,除非呼叫close()方法將其關閉。
close():關閉 EntityManagerFactory 。 EntityManagerFactory 關閉後將釋放所有資源,isOpen()方法測試將返回 false,其它方法將不能呼叫,否則將導致IllegalStateException異常。
EntityManager
以下程式碼省略了entityManager,entityManagerFactory,transaction的建立,關閉等過程
find方法
//查詢ID為1的Order Order order = entityManager.find(Order.class, 1);
- 1
- 2
getReference方法
Order order = entityManager.getReference(Order.class, 1);System.out.println("-------------------------------------");System.out.println(order);
- 1
- 2
- 3
這個方法的使用和find()一樣,但是有一些區別,看下方執行結果截圖:
在程式碼中,我先呼叫getReference,然後列印“- - - - - - ”,最後列印order物件
但是從截圖看,是先列印“- - - - -”,然後才查詢order,接著列印order。
原因:呼叫getReference()方法,返回的其實並不是order物件,而是一個代理。當你要使用order時,才會真正的呼叫查詢語句來查詢order物件
persist方法
Order order = new Order();order.setOrderName("hehe");entityManager.persist(order);System.out.println("id="+order.getId());
- 1
- 2
- 3
- 4
執行insert操作,相當於hibernate的save()方法
從上面程式碼中可以看到,我們並沒有給order一個id,但是在執行persist方法之後,沒有從資料庫查詢,order.getId()也可以獲取到值。這也是persist方法的一個功能:使物件由臨時狀態變為持久化狀態。
結果截圖:
但是和hibernate的save()方法有些不同:如果物件有id,則不能執行insert操作,會丟擲異常
remove方法
相當於hibernate的delete()方法,但是有些不同:
remove()方法不能移除遊離物件,只能移除持久化物件,什麼意思呢?對比下面兩段程式碼:
Order order = new Order();order.setId(140);entityManager.remove(order);
- 1
- 2
- 3
上面這段程式碼會丟擲異常,因為order是我們自己建立的物件,也就是遊離物件。
必須這樣寫:
Order order = new Order();order = entityManager.find(Order.class, 140);entityManager.remove(order);
- 1
- 2
- 3
這段程式碼中的order是從資料庫中獲取的,也就是持久化物件
hibernate的delete()方法,只要物件有Id,就可以刪除
merge方法
情況1:傳入的物件沒有id
Order order = new Order();order.setOrderName("hehe");Order order2 = entityManager.merge(order);System.out.println("order2#id:"+order2.getId());
- 1
- 2
- 3
- 4
結論:在這種情況下,呼叫merge方法,將返回一個新的物件(有id),並對這個新的物件執行insert操作。
情況2:傳入的物件有id,entityManager的快取中沒有該物件,資料庫中沒有該記錄
Order order = new Order();order.setId(1000);order.setOrderName("hehe");Order order2 = entityManager.merge(order);System.out.println("order#id:"+order.getId());System.out.println("order2#id:"+order2.getId());
- 1
- 2
- 3
- 4
- 5
- 6
結論:在這種情況下,呼叫merge方法,將返回一個新的物件,並對該物件執行insert操作。新物件的id是資料庫中這條記錄的id(比如自增長的id),而不是我們自己傳入的id。(其實和情況1的結果是一樣的)
情況3:傳入的物件有id,entityManager的快取沒有該物件,資料庫中有該記錄
Order order = new Order();order.setId(170);order.setOrderName("wahahahahah");Order order2 = entityManager.merge(order);System.out.println("order:"+order);System.out.println("order2:"+order2);
- 1
- 2
- 3
- 4
- 5
- 6
呼叫前資料庫表:
呼叫後結果:
結論:在這種情況下,呼叫merge方法,將會從資料庫中查詢對應的記錄,生成新的物件,然後將我們傳入的物件複製到新的物件,最後執行update操作。 簡單來說,就是更新操作
情況4:傳入的物件有id,entityManager的快取有該物件
Order order = new Order();order.setId(170);order.setOrderName("xixixixi");//通過find來將物件讀入entityManager快取Order order3 = entityManager.find(Order.class, 170);Order order2 = entityManager.merge(order);System.out.println("order:"+order);System.out.println("order2:"+order2);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
結果和情況3一樣,就不截圖了。
結論:在這種情況下,呼叫merge方法,JPA會把傳入的物件賦值到entityManager的快取中的物件,然後對entityManager快取中的物件執行update操作。(和情況3的結果一樣)
flush方法
先檢視資料庫,有一條記錄如下:
然後執行以下程式碼
Order order= entityManager.find(Order.class, 170);System.out.println("order:"+order);order.setOrderName("bbb");//5. 提交事務transaction.commit();
- 1
- 2
- 3
- 4
- 5
檢視結果:
結果發現,JPA居然執行了update操作,把aaa改成了bbb,但是在程式碼中我們並沒有執行update操作啊。
原因:order是通過find方法查詢出來的,因此它是持久化物件,也就是說它快取在entityManager中,當事務提交時,快取將會被更新到資料庫。
而flush方法,可以強制將快取更新到資料庫
entityManager.flush();
- 1
與之相關:
setFlushMode (FlushModeType flushMode):設定持久上下文環境的Flush模式。引數可以取2個列舉
- FlushModeType.AUTO 為自動更新資料庫實體,
- FlushModeType.COMMIT 為直到提交事務時才更新資料庫記錄。
getFlushMode ():獲取持久上下文環境的Flush模式。返回FlushModeType類的列舉值。
refresh方法
Order order= entityManager.find(Order.class, 170);order= entityManager.find(Order.class, 170);
- 1
- 2
執行以上程式碼,發現呼叫了兩次find,但是隻執行了一次select語句,這是快取導致的。
再看下面的程式碼:
Order order= entityManager.find(Order.class, 170);entityManager.refresh(order);
- 1
- 2
只調用了一次find方法,卻執行了兩次select語句,這是因為refresh方法會去檢視快取中的資料狀態和資料庫中是否一致,因此又執行了一次select語句
clear ()
清除持久上下文環境,斷開所有關聯的實體。如果這時還有未提交的更新則會被撤消。
contains (Object entity):
判斷一個例項是否屬於當前持久上下文環境管理的實體。
isOpen ()
判斷當前的實體管理器是否是開啟狀態。
getTransaction ()
返回資源層的事務物件。EntityTransaction例項可以用於開始和提交多個事務。
close ()
關閉實體管理器。之後若呼叫實體管理器例項的方法或其派生的查詢物件的方法都將丟擲 IllegalstateException 異常,除了getTransaction 和 isOpen方法(返回 false)。不過,當與實體管理器關聯的事務處於活動狀態時,呼叫 close 方法後持久上下文將仍處於被管理狀態,直到事務完成。
createQuery (String qlString)
建立一個查詢物件。
createNamedQuery (String name)
根據命名的查詢語句塊建立查詢物件。引數為命名的查詢語句。
createNativeQuery (String sqlString)
使用標準 SQL語句建立查詢物件。引數為標準SQL語句字串。
createNativeQuery (String sqls, String resultSetMapping)
使用標準SQL語句建立查詢物件,並指定返回結果集 Map的 名稱。