编译时检查JPA查询
JPA提供了幾種查詢(xún)數(shù)據(jù)的方法。 可以根據(jù)各種標(biāo)準(zhǔn)(例如,所使用的語(yǔ)言(SQL與JPQL)或查詢(xún)是靜態(tài)的(編譯時(shí)間)還是動(dòng)態(tài)的(執(zhí)行時(shí)間))對(duì)此類(lèi)替代方案進(jìn)行分類(lèi)。
靜態(tài)查詢(xún)是使用@Entity類(lèi)定義本身中的注釋@NamedQuery ( javax.persistence.NamedQuery )和@NamedQueries ( javax.persistence.NamedQueries )定義的:
另一方面, EntityManager提供的方法createQuery(…)和createNativeQuery(…)分別采用JPQL或SQL查詢(xún)。
因此,可以在編譯或執(zhí)行時(shí)定義查詢(xún)。
( 注意 :建議始終使用Query中的 setParameter(…)方法使用參數(shù)化查詢(xún),以避免SQL注入漏洞。)
標(biāo)準(zhǔn)API
但是,JPA提供了另一種查詢(xún)對(duì)象的方法: Criteria API 。 確實(shí),切換到JPA的動(dòng)機(jī)之一是處理對(duì)象而不是SQL方言,不是嗎?
讓我們看一個(gè)示例代碼。
實(shí)體定義:
@Entity public class User {@Idprivate Integer userId;@Basic@Column(length=15, nullable=false)private String name;@Basic@Column(length=64, nullable=false)private String userDigestedPasswd;@Basic@Column(length=50, nullable=true)private String email;@Basic@Column(nullable=false)public Integer privilegeLevel;@Basic@Column(nullable=false)private Boolean active; }讓我們查詢(xún)數(shù)據(jù)庫(kù)并檢查結(jié)果(使用JUnit):
public class UserTest {@Testpublic void testUserCriteria(){ EntityManagerFactory emf = null; EntityManager em = null; try {emf = Persistence.createEntityManagerFactory("criteria");em = emf.createEntityManager();final CriteriaBuilder cb = em.getCriteriaBuilder();final CriteriaQuery<User> q = cb.createQuery(User.class);final Root<User> users = q.from(User.class);final Predicate condition = cb.equal(users.get("privilegeLevel"), 5);q.select(users).where(condition).orderBy(cb.asc(users.get("userId")));em.getTransaction().begin();List<User> result = em.createQuery(q).getResultList();em.getTransaction().commit();assertNotNull(result);assertEquals(2, result.size());assertEquals(1, (int)result.get(0).getUserId());assertEquals("Pepe", result.get(0).getName());assertEquals(3, (int)result.get(1).getUserId());assertEquals("Dolores", result.get(1).getName());} catch (Exception e) {fail("Unexpected Exception " + e.getMessage()); } finally {if (em != null)em.close();if (emf != null)emf.close(); } } }以下幾行顯示查詢(xún)的創(chuàng)建:
final CriteriaBuilder cb = em.getCriteriaBuilder();final CriteriaQuery<User> q = cb.createQuery(User.class);final Root<User> users = q.from(User.class);final Predicate condition = cb.equal(users.get("privilegeLevel);q.select(users).where(condition).orderBy(cb.asc(users.get("userId首先,從EntityManager獲得CriteriaBuilder 。 然后,獲取一個(gè)CriteriaQuery實(shí)例,將該類(lèi)設(shè)置為保存結(jié)果。 在我們的例子中, User.class :
final CriteriaBuilder cb = em.getCriteriaBuilder(); final CriteriaQuery<User> q = cb.createQuery(User.class);接下來(lái),必須設(shè)置要對(duì)其運(yùn)行查詢(xún)的實(shí)體:
final Root<User> users = q.from(User.class);現(xiàn)在是時(shí)候設(shè)置查詢(xún)匹配條件了。 在示例代碼中,條件只是屬性privilegeLevel等于5:
final Predicate condition = cb.equal(users.get("privilegeLevel"), 5);最后,構(gòu)建查詢(xún)以在Root上添加條件。 也可以設(shè)置分組和排序選項(xiàng)(即,對(duì)userId設(shè)置升序排序):
q.select(users).where(condition).orderBy(cb.asc(users.get(“userId”)));請(qǐng)查看CriteriaBuilder中的不同選項(xiàng)。 可以在CriteriaQuery中找到分組和排序選項(xiàng)。
使用元模型進(jìn)行編譯時(shí)檢查
請(qǐng)注意,我們剛剛構(gòu)建的查詢(xún)需要跟蹤對(duì)象屬性名稱(chēng)。 例如,要構(gòu)建查詢(xún),請(qǐng)使用屬性privilegeLevel的名稱(chēng)。 但是,如果稍后更改屬性名稱(chēng),則代碼將編譯并且僅在運(yùn)行時(shí)失敗:
final CriteriaQuery<User> q = cb.createQuery(User.class);final Root<User> users = q.from(User.class);final Predicate condition = cb.equal(users.get("privilegeLevel"), 5);q.select(users).where(condition).orderBy(cb.asc(users.get("userId")));那不好
幸運(yùn)的是,使用元模型,我們將能夠構(gòu)建編譯時(shí)檢查的查詢(xún)。 可以在The Java EE6 Tutorial中找到簡(jiǎn)短的介紹。
使用元模型,代碼將引用對(duì)象的SingularAttribute,而不是使用包含對(duì)象屬性名稱(chēng)的String。 因此,如果稍后更改對(duì)象屬性,則編譯器將為我們標(biāo)記該屬性。
首先,必須創(chuàng)建對(duì)應(yīng)的元模型類(lèi)( EntityType )。 盡管可以通過(guò)多種方法實(shí)現(xiàn),但對(duì)于openJPA實(shí)現(xiàn),最簡(jiǎn)單的方法可能是添加openJPA構(gòu)建標(biāo)志 : -Aopenjpa.metamodel = true 。
因此,我們創(chuàng)建了User_類(lèi),它是User的對(duì)應(yīng)元模型類(lèi):
* Generated by OpenJPA MetaModel Generator Tool.?**/ package com.wordpress.tododev.criteria.entities; import javax.persistence.metamodel.SingularAttribute; @javax.persistence.metamodel.StaticMetamodel (value=com.wordpress.tododev.criteria.entities.User.class) @javax.annotation.Generated (value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Mon Mar 04 16:47:46 CET 2013") public class User_ {public static volatile SingularAttribute<User,Boolean> active;public static volatile SingularAttribute<User,String> email;public static volatile SingularAttribute<User,String> name;public static volatile SingularAttribute<User,Integer> privilegeLevel;public static volatile SingularAttribute<User,String> userDigestedPasswd;public static volatile SingularAttribute<User,Integer> userId; }如果將此類(lèi)添加到代碼庫(kù)中,則以后對(duì)User類(lèi)的任何更改都不會(huì)引起注意。 而且,將自動(dòng)生成的項(xiàng)目添加到代碼版本控制系統(tǒng)中不是一個(gè)好主意。
使用ant , maven或類(lèi)似工具,可以添加目標(biāo)以創(chuàng)建元模型類(lèi)。 在更改JPA實(shí)體后,應(yīng)執(zhí)行該目標(biāo)。
也可以使用IDE。 例如,對(duì)于使用Eclipse的,只需要已經(jīng)提到編譯標(biāo)志添加屬性- > Java的反編譯>注解處理器和的lib(JAR)包含所選擇的JPA實(shí)現(xiàn)第廠(chǎng)路內(nèi)注釋處理器的注釋處理器(可能導(dǎo)致自動(dòng)模式下的編譯問(wèn)題,前提是必須在使用它的代碼之前編譯元模型類(lèi))。
讓我們向套件添加另一個(gè)測(cè)試。 這個(gè)不會(huì)提供包含屬性名稱(chēng)的String,而是使用metamodel類(lèi):
@Testpublic void testUserCriteriaMetaModel(){EntityManagerFactory emf = null;EntityManager em = null;try {emf = Persistence.createEntityManagerFactory("criteria");em = emf.createEntityManager();final CriteriaBuilder cb = em.getCriteriaBuilder();final CriteriaQuery<User> q = cb.createQuery(User.class);final Metamodel m = em.getMetamodel();final Root<User> user = q.from(m.entity(User.class));final Predicate condition = cb.equal(user.get(User_.privilegeLevel), 5);q.select(user).where(condition).orderBy(cb.asc(user.get(User_.userId)));em.getTransaction().begin();List<User> result = em.createQuery(q).getResultList();em.getTransaction().commit();assertNotNull(result);assertEquals(2, result.size());assertEquals(1, (int)result.get(0).getUserId());assertEquals("Pepe", result.get(0).getName());assertEquals(3, (int)result.get(1).getUserId());assertEquals("Dolores", result.get(1).getName()); } catch (Exception e) {fail("Unexpected Exception " + e.getMessage());} finally {if (em != null)em.close();if (emf != null)emf.close();}}更相關(guān)的更改是user.get(User_.privilegeLevel)而不是users.get(“ privilegeLevel”)和 ? user.get(User_.userId)而不是 ? users.get(“ userId”)。
- 從GitHub下載源代碼。
翻譯自: https://www.javacodegeeks.com/2014/08/compile-time-checking-jpa-queries.html
總結(jié)
以上是生活随笔為你收集整理的编译时检查JPA查询的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 俄罗斯用什么货币?
- 下一篇: 科技型企业备案证书(科技型企业备案)