廣州黃埔區(qū)網(wǎng)站建設(shè)北京seo網(wǎng)站優(yōu)化公司
Hibernate EntityManager 專題
參考:
- JPA – EntityManager常用API詳解
- EntityManager基本概念
基本概念及獲得 EntityManager 對象
基本概念
在使用持久化工具的時候,一般都有一個對象來操作數(shù)據(jù)庫,在原生的Hibernate中叫做Session,在 JPA 中叫做EntityManager,在MyBatis中叫做SqlSession,通過這個對象來操作數(shù)據(jù)庫。
EntityManager是 JPA 中用于增刪改查的接口,連接內(nèi)存中的 java 對象和數(shù)據(jù)庫的數(shù)據(jù)存儲。Hibernate EntityManager是圍繞提供JPA編程接口實現(xiàn)的Hibernate Core的一個包裝,支持JPA實體實例的生命周期,并允許用標(biāo)準(zhǔn)的Java Persistence查詢語言編寫查詢。
EntityManager稱為實體管理器,它由EntityManagerFactory所創(chuàng)建。EntityManagerFactory,作為EntityManager的工廠,包含有當(dāng)前O-R映射的元數(shù)據(jù)信息,每個EntityManagerFactory,可稱為一個持久化單元(PersistenceUnit),每個持久化單元可認(rèn)為是一個數(shù)據(jù)源的映射(所謂數(shù)據(jù)源,可理解為一個數(shù)據(jù)庫,可以在應(yīng)用服務(wù)器中配置多個數(shù)據(jù)源,同時使用不同的PersistenceUnit來映射這些數(shù)據(jù)源,從而能夠很方便的實現(xiàn)跨越多個數(shù)據(jù)庫之間的事務(wù)操作!)
PersistenceContext,稱為持久化上下文,它一般包含有當(dāng)前事務(wù)范圍內(nèi)的,被管理的實體對象(Entity)的數(shù)據(jù)。每個EntityManager,都會跟一個PersistenceContext相關(guān)聯(lián)。PersistenceContext中存儲的是實體對象的數(shù)據(jù),而關(guān)系數(shù)據(jù)庫中存儲的是記錄,EntityManager正是維護這種OR映射的中間者,它可以把數(shù)據(jù)從數(shù)據(jù)庫中加載到PersistenceContext中,也可以把數(shù)據(jù)從PersistenceContext中持久化到數(shù)據(jù)庫,EntityManager通過Persist、merge、remove、refresh、flush等操作來操縱PersistenceContext與數(shù)據(jù)庫數(shù)據(jù)之間的同步!
EntityManager是應(yīng)用程序操縱持久化數(shù)據(jù)的接口。它的作用與hibernate session類似。為了能夠在一個請求周期中使用同一個session對象,在hibernate的解決方案中,提出了currentSession的概念,hibernate中的current session,可以跟JTA事務(wù)綁定,也可以跟當(dāng)前線程綁定。在hibernate中,session管理著所有的持久化對象的數(shù)據(jù)。而在EJB3中,EntityManager管理著PersistenceContext,PersistenceContext正是被管理的持久化對象的集合。
在 Java EE 環(huán)境下,一個 JTA 事務(wù)通常會橫跨多個組件的調(diào)用(比如多個 EJB 組件的方法調(diào)用)。這些組件需要能夠在單個事務(wù)范圍內(nèi)訪問到同樣的PersistenceContext。為了滿足這種情況的需要,當(dāng)EntityManager被注入或通過 JNDI 被查詢的時候,它的 PersistenceContext 將會在當(dāng)前事務(wù)范圍內(nèi)自動傳播,引用到同一個 Persistence unit 的EntityManager將使用同樣的 PersistenceContext。這可以避免在不同的組件之間傳遞EntityManager引用。
通過容器來傳遞PersistenceContext,而不是應(yīng)用程序自己來傳遞EntityManager。這種方式(由容器管理著PersistenceContext,并負(fù)責(zé)傳遞到不同的EntityManager)稱為容器管理的實體管理器(Container-Managed EntityManager),它的生命周期由容器負(fù)責(zé)管理,編程人員不需要考慮EntityManger的連接,釋放以及復(fù)雜的事務(wù)問題等。
有一種不常見的情況是,應(yīng)用程序自身需要獨立訪問PersistenceContext。即每次創(chuàng)建一個EntityManager都會迫使創(chuàng)建一個新的PersistenceContext。這些PersistenceContext即使在同一個事務(wù)范圍內(nèi)也不會跟其它EntityManager共享!這個創(chuàng)建過程可以由EntityManagerFactory的createEntityManager方法來創(chuàng)建。這被稱為應(yīng)用管理的實體管理器(application-managed entity manager)。
獲得EntityManager對象
常用方式:SpringBoot容器托管對象方式:
依賴:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
對象注入:
@Autowired
private EntityManager entityManager;
實體狀態(tài)和轉(zhuǎn)換
實體狀態(tài)詳解:
-
臨時狀態(tài)
實際上就是new了一個普通的 JavaBean 對象。
-
托管狀態(tài)
臨時狀態(tài)在調(diào)用 persist() 后,即可將一般的 JavaBean 做為了托管狀態(tài)的Bean,該Bean的任何屬性改動都會牽涉到數(shù)據(jù)庫記錄的改動。
一旦該記錄flush到數(shù)據(jù)庫之后,并且事務(wù)提交了,那么此對象不在持久化上下文中,即:變?yōu)榱擞坞x(沒人管的孩子)狀態(tài)了。
在游離狀態(tài)的時候調(diào)用更新、刷新方法后,游離狀態(tài)對象就變?yōu)榱嗽诔志没舷挛牡耐泄軤顟B(tài)了。
通過管理器的find方法,將實體從數(shù)據(jù)庫查詢出來后,該實體也就變?yōu)榱送泄苄螒B(tài)。
-
持久化狀態(tài)
當(dāng)處在托管狀態(tài)的實體Bean被管理器flush了,那么就在極短暫的時間進入了持久化狀態(tài),事務(wù)提交之后,立刻變?yōu)榱擞坞x狀態(tài)。可以把持久化狀態(tài)當(dāng)做實實在在的數(shù)據(jù)庫記錄。
-
游離狀態(tài)
游離狀態(tài)就是提交到數(shù)據(jù)庫后,事務(wù)commit后實體的狀態(tài),因為事務(wù)已經(jīng)提交了,此時實體的屬性任你如何改變,也不會同步到數(shù)據(jù)庫,因為游離是沒人管的孩子,不在持久化上下文中。
-
銷毀對象
一般要刪除一個持久化對象的時候都是先find出來,之后調(diào)用remove方法刪之,此時這個對象就是銷毀對象,實際上就是瞬時對象的另一種形態(tài)罷了。
常用的 API
SELECT、DELETE
SELECT
? find() :返回指定的 OID 對應(yīng)的實體類對象,如果這個實體存在于當(dāng)前的持久化環(huán)境,則返回一個被緩存的對象;否則會創(chuàng)建一個新的 Entity, 并加載數(shù)據(jù)庫中相關(guān)信息;若 OID 不存在于數(shù)據(jù)庫中,則返回一個 null。
? getReference()
<T> T find(Class<T> entityClass, Object primaryKey);
<T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> var3);
<T> T find(Class<T> entityClass, Object primaryKey, LockModeType var3);
<T> T find(Class<T> entityClass, Object primaryKey, LockModeType var3, Map<String, Object> var4);<T> T getReference(Class<T> entityClass, Object primaryKey);// 參數(shù)說明:entityClass // 被查詢的實體類類型primaryKey // 待查找實體的主鍵值
異同:
-
當(dāng)在數(shù)據(jù)庫中沒有找到記錄時,find()方法會返回null,
而getReference() 方法會拋出javax.persistence.EntityNotFoundException異常,
-
調(diào)用getReference()方法,返回的其實并不是實例對象,而是一個代理。當(dāng)你要使用實體時,才會真正的調(diào)用查詢語句來查詢實例對象
-
另外getReference()方法不保證 entity Bean已被初始化。
-
如果傳遞進getReference()或find()方法的參數(shù)不是實體Bean,都會引發(fā) IllegalArgumentException
DELETE
? Remove()
void remove(Object entity);
-
如果級聯(lián)關(guān)系cascade=CascadeType.ALL,在刪除person 時候,也會把級聯(lián)對象刪除。把cascade屬性設(shè)為cascade=CascadeType.REMOVE 有同樣的效果。
Person person = em.find(Person.class, 2); em.remove (person);
-
如果傳遞進remove ()方法的參數(shù)不是實體Bean,會引發(fā)一個IllegalArgumentException
remove()方法不能移除游離對象,只能移除持久化對象
Order order = new Order(); order.setId(140); entityManager.remove(order);
上面這段代碼會拋出異常,因為order是自己創(chuàng)建的對象,也就是游離對象。必須這樣寫:
Order order = new Order(); order = entityManager.find(Order.class,140); entityManager.remove(order);
這段代碼中的order是從數(shù)據(jù)庫中獲取的,也就是持久化對象
hibernate的delete()方法,只要對象有Id,就可以刪除
INSERT、UPDATE
INSERT
? persist(): 將臨時狀態(tài)的實體持久化到數(shù)據(jù)庫
void persist(Object entity);
persist方法:使對象由臨時狀態(tài)變?yōu)橥泄軤顟B(tài)。進而變?yōu)槌志没癄顟B(tài),就是執(zhí)行INSERT操作。
- 如果傳遞進persist()方法的參數(shù)不是實體Bean,會引發(fā)IllegalArgumentException
- 和hibernate的save()方法有些不同:當(dāng)Entity實體類中設(shè)置了主鍵自動生成時,如果傳入對象有id值,則會拋出異常
特殊場景及處理方案:
-
特殊場景:Entity上使用自動生成ID值,但有些又需要插入的主鍵值是指定的ID值,而非自動生成的ID值
-
處理方案1:Bean主鍵為Null,persist()后自動生成ID值,然后再使用QueryDsl-Jpa的update()方法,根據(jù)自動生成的ID值為條件,更新新增的實體的ID值為指定值
@Test @Transactional @Rollback(false) public void addByEntityManager(){User bean = User.builder().addressee("孫六").build();entityManager.persist(u1);QUser entity = QUser.user;long execute = queryFactory.update(entity).set(entity.id, "11111").where(entity.id.eq(bean.getId())).execute();entityManager.flush();entityManager.clear(); }
-
處理方案2:使用Querydsl-SQL中,SqlQueryFactory.insert()方法。
UPDATE
? 當(dāng)實體正在被容器管理,即托管狀態(tài),你可以調(diào)用實體的set方法對數(shù)據(jù)進行修改,在容器決定flush時(這個由Container自行判斷),更新的數(shù)據(jù)才會同步到數(shù)據(jù)庫,而不是在調(diào)用了set方法對數(shù)據(jù)進行修改后馬上同步到數(shù)據(jù)庫。如果希望修改后的數(shù)據(jù)馬上同步到數(shù)據(jù)庫,可以調(diào)用 EntityManager.flush() 方法。
// 使用示例
@tranational
publicvoid updatePerson() {Person person = entityManager.find(Person.class, 1);person.setName("lihuoming"); //方法執(zhí)行完后即可更新數(shù)據(jù)entityManager.merge(person);
}
? Merge
<T> T merge(T entity);
-
傳入的對象沒有id
在這種情況下,調(diào)用merge方法,將返回一個新的對象(有id),并對這個新的對象執(zhí)行insert操作。
-
傳入的對象有id,entityManager的緩存中沒有該對象,數(shù)據(jù)庫中沒有該記錄:
在這種情況下,調(diào)用merge方法,將返回一個新的對象,并對該對象執(zhí)行insert操作。
注意:如果Entity的主鍵設(shè)置的是自動生成,則新對象的id并不是原傳入對象的id,而是自動生成的(比如自增長的id)。(其實和情況1的結(jié)果是一樣的)
-
傳入的對象有id,entityManager的緩存沒有該對象,數(shù)據(jù)庫中有該記錄
在這種情況下,調(diào)用merge方法,將會從數(shù)據(jù)庫中查詢對應(yīng)的記錄,生成新的對象,然后將傳入的對象復(fù)制到新的對象,最后執(zhí)行update操作。
簡單來說,就是更新操作。
-
傳入的對象有id,entityManager的緩存有該對象
在這種情況下,調(diào)用merge方法,JPA會把傳入的對象賦值到entityManager的緩存中的對象,然后對entityManager緩存中的對象執(zhí)行update操作。(和情況3的結(jié)果一樣)
總結(jié):執(zhí)行merge時,如果實體ID為空,則進行insert操作。 如果有ID則進行update操作。
flush()、clear()
flush()
將實體的改變立刻刷新到數(shù)據(jù)庫中
當(dāng)EntityManager對象在一個session bean 中使用時,它是和服務(wù)器的事務(wù)上下文綁定的。EntityManager 在服務(wù)器的事務(wù)提交時提交并且同步它的內(nèi)容。
在一個session bean 中,服務(wù)器的事務(wù)默認(rèn)地會在調(diào)用堆棧的最后提交(如:方法的返回)。
// 例子1:在方法返回時才提交事務(wù)
public void updatePerson(Person person) {try {Person person = em.find(Person.class, 2);person.setName("lihuoming");em.merge(person);//后面還有眾多修改操作} catch (Exception e) {e.printStackTrace();}//更新將會在這個方法的末尾被提交和刷新到數(shù)據(jù)庫中
}
默認(rèn)只在當(dāng)事務(wù)提交時才將改變更新到數(shù)據(jù)庫中,容器將所有數(shù)據(jù)庫操作集中到一個批處理中,這樣就減少了代價昂貴的與數(shù)據(jù)庫的交互。
當(dāng)調(diào)用 persist( ),merge( )或 remove( ) 這些方法時,更新并不會立刻同步到數(shù)據(jù)庫中,直到容器決定刷新到數(shù)據(jù)庫中時才會執(zhí)行,默認(rèn)情況下,容器決定刷新是在 “相關(guān)查詢” 執(zhí)行前或事務(wù)提交時發(fā)生。
當(dāng)然 “相關(guān)查詢” 除 find() 和 getreference() 之外,這兩個方法是不會引起容器觸發(fā)刷新動作的,默認(rèn)的刷新模式是可以改變的。
如果你需要在事務(wù)提交之前將更新刷新到數(shù)據(jù)庫中,你可以直接地調(diào)用EntityManager.flush()方法。
ORM框架執(zhí)行的一些更新數(shù)據(jù)庫的方法,其實質(zhì)是在更新緩存,只有調(diào)用了 flush() 后才會將緩存同步到數(shù)據(jù)庫,即真正執(zhí)行SQL語句,但是這時并沒有真正將數(shù)據(jù)保存進數(shù)據(jù)庫,需要事務(wù)commit后才能全部保存。一般 flush 后立刻就會進行事務(wù)的提交。
public void updatePerson(Person person) {try {Person person = em.find(Person.class, 2);person.setName("lihuoming");em.merge(person);em.flush();//手動將更新立刻刷新進數(shù)據(jù)庫//后面還有眾多修改操作} catch (Exception e) {e.printStackTrace();}
}
clear()
分離所有當(dāng)前正在被管理的實體
1.清除持久上下文環(huán)境,斷開所有關(guān)聯(lián)的實體。如果這時還有未提交的更新則會被撤消。
2.在處理大量實體的時候,如果你不把已經(jīng)處理過的實體從EntityManager中分離出來,將會消耗你大量的內(nèi)存。
3.調(diào)用EntityManager 的clear()方法后,所有正在被管理的實體將會從持久化內(nèi)容中分離出來。
4.有一點需要說明下,在事務(wù)沒有提交前(事務(wù)默認(rèn)在調(diào)用堆棧的最后提交,如:方法的返回),如果調(diào)用clear()方法,之前對實體所作的任何改變將會丟失,所以建議在調(diào)用clear()方法之前先調(diào)用flush()方法保存更改。
JcreateQuery() – PQL
創(chuàng)建查詢對象
除了使用 find() 或 getReference() 方法來獲得Entity Bean之外,你還可以通過 JPQL 得到實體Bean。要執(zhí)行 JPQL 語句,你必須通過 EntityManager 的 createQuery() 或 createNamedQuery() 方法創(chuàng)建一個Query 對象。
注:JPQL 沒有插入語句。即不能執(zhí)行insert語句。
? getResultList()
Query query = em.createQuery("select p from Person p where p.name=’黎明’");
List result = query.getResultList();Iterator iterator = result.iterator();
while( iterator.hasNext() ){
//處理Person
}
? getSingleResult()
返回查詢的第一條數(shù)據(jù),可以進行強轉(zhuǎn),如:
// 查詢數(shù)目:
Query query = em.createQuery("select count(1) from Person p");
Long num = (Long)query. getSingleResult ();// 強轉(zhuǎn)為實體:
Query query = em.createQuery("select p from Person p");
User user = (User)query. getSingleResult ();
? executeUpdate()
// 執(zhí)行更新和刪除操作,返回受影響的記錄數(shù)。
Query query = em.createQuery("delete from Person");
int result =query.executeUpdate(); //影響的記錄數(shù)
? 關(guān)于 **JPQL 和SQL **中參數(shù)的問題:
-
使用標(biāo)識符
Query query =em.createQuery("delete from Person p where p.name := name");
query.setParameter("name","張三");
int result =query.executeUpdate(); //影響的記錄數(shù)
-
使用索引下標(biāo)
Query query =em.createQuery("delete from Person p where p.id = ?1 ");
query.setParameter(1, "張三");
int result =query.executeUpdate(); //影響的記錄數(shù)
createNaiveQuery() – SQL
用法基本同createQuery(),只不過這里使用的不是 JPQL 而是SQL
? 將查詢到的數(shù)據(jù)映射成實體:
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();… }
}
refresh()
如果懷疑當(dāng)前被管理的實體已經(jīng)不是數(shù)據(jù)庫中最新的數(shù)據(jù),則可以通過 refresh() 方法刷新實體,容器會把數(shù)據(jù)庫中的新值重寫進實體。這種情況一般發(fā)生在獲取了實體之后,有人更新了數(shù)據(jù)庫中的記錄,這時需要得到最新的數(shù)據(jù)。當(dāng)然再次調(diào)用 find() 或 getReference() 方法也可以得到最新數(shù)據(jù),但這種做法并不優(yōu)雅。
User user = em.find(User.class, 1);//第二次同樣的查詢不會訪問數(shù)據(jù)庫
user = em.find(User.class, 1);// 運行以上代碼,發(fā)現(xiàn)調(diào)用了兩次find,但是只執(zhí)行了一次select語句,這是緩存導(dǎo)致的。// 執(zhí)行refresh()方法刷新緩存,容器會把數(shù)據(jù)庫中的新值重寫進實體。
User user = em.find(User.class, 1);
em.refresh(user);
其他方法
contains()
判斷實體是否還在EntityManage的管理下,或者說是否屬于當(dāng)前持久上下文環(huán)境。
contains() 方法使用一個實體作為參數(shù),如果這個實體對象當(dāng)前正被持久化內(nèi)容管理,返回值為true,否則為false。
如果傳遞的參數(shù)不是實體 Bean,將會引發(fā)一個IllegalArgumentException.
User user = em.find(User.class, 1);if (em.contains(user)){//正在被持久化內(nèi)容管理
}else{//已經(jīng)不受持久化內(nèi)容管理
}
getFlushMode ():
獲取持久上下文環(huán)境的Flush模式。返回FlushModeType類的枚舉值。
setFlushMode()
改變實體管理器的Flush模式
setFlushMode()的Flush模式有2種類型:AUTO 和 COMMIT。AUTO為默認(rèn)模式。
// 改變實體管理器的Flush模式
em.setFlushMode(FlushModeType.COMMIT);
-
FlushModeType.AUTO
默認(rèn)情況下除了在事務(wù)提交時 flush,在進行查詢時(除了 find() 和 getreference() 查詢)也會進行一次 flush ,比如使用JPQL查詢前會進行一次flush。
使用場合:在大量更新數(shù)據(jù)的過程中沒有任何查詢語句(除了 find() 和 getreference() 查詢)的執(zhí)行。
-
FlushModeType.COMMIT
刷新只有在事務(wù)提交時才發(fā)生,查詢不觸發(fā)。
使用場合:在大量更新數(shù)據(jù)的過程中存在查詢語句(除了find() 和 getreference() 查詢)的執(zhí)行。
其實上面兩種模式最終反映的結(jié)果是:JDBC 驅(qū)動跟數(shù)據(jù)庫交互的次數(shù)。
JDBC 性能最大的增進是減少JDBC 驅(qū)動與數(shù)據(jù)庫之間的網(wǎng)絡(luò)通訊。
FlushModeType.COMMIT 模式使更新只在一次的網(wǎng)絡(luò)交互中完成,而 FlushModeType.AUTO 模式可能需要多次交互(觸發(fā)了多少次Flush 就產(chǎn)生了多少次網(wǎng)絡(luò)交互)
isOpen()
判斷當(dāng)前的實體管理器是否是打開狀態(tài)
close()
關(guān)閉實體管理器。
之后若調(diào)用實體管理器實例的方法或其派生的查詢對象的方法都將拋出 IllegalstateException 異常,除了 getTransaction 和 isOpen方法(返回 false)。
不過,當(dāng)與實體管理器關(guān)聯(lián)的事務(wù)處于活動狀態(tài)時,調(diào)用 close() 方法后持久上下文將仍處于被管理狀態(tài),直到事務(wù)完成。
getTransaction()
返回資源層的事務(wù)對象。EntityTransaction實例可以用于開始和提交多個事務(wù)
EntityTransaction 接口用來管理資源層實體管理器的事務(wù)操作
通過調(diào)用實體管理器的getTransaction方法 獲得其實例。
① begin ()
用于啟動一個事務(wù),此后的多個數(shù)據(jù)庫操作將作為整體被提交或撤消。若這時事務(wù)已啟動則會拋出 IllegalStateException 異常。
② commit ()
用于提交當(dāng)前事務(wù)。即將事務(wù)啟動以后的所有數(shù)據(jù)庫更新操作持久化至數(shù)據(jù)庫中。
③ rollback ()
撤消(回滾)當(dāng)前事務(wù)。即撤消事務(wù)啟動后的所有數(shù)據(jù)庫更新操作,從而不對數(shù)據(jù)庫產(chǎn)生影響。
④ setRollbackOnly ()
使當(dāng)前事務(wù)只能被撤消。
⑤ getRollbackOnly ()
查看當(dāng)前事務(wù)是否設(shè)置了只能撤消標(biāo)志。
⑥ isActive ()
查看當(dāng)前事務(wù)是否是活動的。如果返回true則不能調(diào)用begin方法,否則將拋出 IllegalStateException 異常;如果返回 false 則不能調(diào)用 commit、rollback、setRollbackOnly 及 getRollbackOnly 方法,否則將拋出 IllegalStateException 異常。
需要注意的是:
// 在JPA里面,先需要 getTransaction,再 begin
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();// 在 hibernate 里面呢,直接 begin,然后進行 commit
EntityTransaction transaction = session.beginTransaction();
transaction.commit();
JPA 調(diào)用存儲過程
參考:https://www.cnblogs.com/zhuang229/p/12227938.html
定義存儲過程及調(diào)用方式
定義一個簡單的存儲過程
傳入一個int參數(shù),返回這個參數(shù)+1
CREATE DEFINER=`root`@`localhost` PROCEDURE `plus1inout`(IN ARG INT, OUT res INT)
BEGIN SET res = ARG + 1;
END
注意:
-
IN參數(shù)個數(shù)沒有限制。
-
如果out參數(shù)類型為sys_refcursor,那么最好只定義這 一個out參數(shù)(JPA API限制);
sys_refcursor 類型的 out 參數(shù),在 JPA 中統(tǒng)一注冊為 Void.class 類型,參數(shù)模式定義為 ParameterMode.REF_CURSOR;
使用
getResultList()
方法獲取游標(biāo)fetch到的多行數(shù)據(jù),返回結(jié)果為List<Object[]>,一個Object[]對應(yīng)一行數(shù)據(jù)。 -
如果使用Oracle 存儲包,只需在定義存儲過程名字時加個對應(yīng)的package名前綴即可(例如:包名.存儲過程名)。
JPA調(diào)用存儲過程的兩種方式
- 基于Entity實體類,在實體類上使用@NamedStoredProcedureQuery注解(需要數(shù)據(jù)庫中有對應(yīng)的表,可自動映射結(jié)果集)
- EntityManager創(chuàng)建createNamedStoredProcedureQuery,傳參調(diào)用
- SpringDataJpa中Repository自定義方法,傳參調(diào)用
- 直接使用EntityManager進行自定義(不需要數(shù)據(jù)庫中有對應(yīng)的表,需要自己處理結(jié)果集)
- EntityManager創(chuàng)建StoredProcedureQuery對象,注冊 IN/OUT 參數(shù)模式
實體類(使用注解聲明存儲過程)
@NamedStoredProcedureQuery注解(解析詳見注解目錄)
-
name為唯一名稱,調(diào)用時使用;procedureName 為存儲過程名;
-
@Entity注解必須有
-
@Entity要求必須有主鍵id屬性(存儲過程可返回id任意值即可)
-
@Entity要求必須對應(yīng)數(shù)據(jù)庫表必須存在(JPA表檢查用)(若用EntityManager調(diào)用存儲過程,此條存疑)
import lombok.Data;
import javax.persistence.*;@Data
@Entity
// 存儲過程使用了注解@NamedStoredProcedureQuery,并綁定到一個隨意一個JPA表
@NamedStoredProcedureQueries({@NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = {@StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class),@StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) }),@NamedStoredProcedureQuery(name = "User.mytest", procedureName = "mytest") })
class user{@Idprivate Integer id;private String name;private String age;
}
EntityManager直接調(diào)用存儲過程
@Autowired
private EntityManager em;@Test
public void test01(){StoredProcedureQuery query = em.createStoredProcedureQuery("plus1inout") // 創(chuàng)建StoredProcedureQuery對象,傳入被調(diào)用的存儲過程名稱.registerStoredProcedureParameter("ARG", Integer.class, ParameterMode.IN) // 注冊參數(shù).registerStoredProcedureParameter("res", Integer.class, ParameterMode.OUT).setParameter("ARG", 20); // 傳參query.execute(); // 執(zhí)行存儲過程調(diào)用String result = query.getOutputParameterValue("res").toString(); // 獲取存儲過程中的返回值System.out.println(result);
}
調(diào)用基于實體類注解的存儲過程
使用EntityManager調(diào)用基于Entity實體類注解的存儲過程。實體類詳見 JPA調(diào)用存儲過程
@Autowired
private EntityManager em;@Test
public void test02(){StoredProcedureQuery query = em// 創(chuàng)建NamedStoredProcedureQuery對象,傳入實體類上@NamedStoredProcedureQuery注解中name的值.createNamedStoredProcedureQuery("proKQAttendanceRecord")// IN模式的參數(shù)可以在實體類上注解,此處相應(yīng)注釋掉// .registerStoredProcedureParameter("PRM_ID", Integer.class, ParameterMode.IN)// 使用EntityManager調(diào)用基于Entity實體類注解的存儲過程時,OUT或INOUT模式的參數(shù),必須要在此處注冊,并刪掉原實體類上相應(yīng)的參數(shù)注解。不然運行報錯。.registerStoredProcedureParameter("PRM_APPCODE", Integer.class, ParameterMode.OUT).registerStoredProcedureParameter("PRM_ERRMSGE", Integer.class, ParameterMode.OUT).setParameter("PRM_ID", attId); // 傳參query.execute();List resultList = query.getResultList();Object code = query.getOutputParameterValue("PRM_APPCODE");Object msg = query.getOutputParameterValue("PRM_ERRMSGE");
}