jpa的查询api_为JPA的本机查询API键入安全查询
jpa的查詢api
當(dāng)您使用JPA時(shí)-有時(shí)-JPQL不能解決問題,您將不得不使用本機(jī)SQL。 從一開始,像Hibernate這樣的ORM就為這些情況保留了開放的“后門”,并為Spring的JdbcTemplate , Apache DbUtils或jOOQ提供了類似的API,用于純SQL 。 這很有用,因?yàn)槟梢岳^續(xù)將ORM用作數(shù)據(jù)庫交互的單個(gè)入口點(diǎn)。
但是,使用字符串連接編寫復(fù)雜的動(dòng)態(tài)SQL既繁瑣又容易出錯(cuò),并且是SQL注入漏洞的門戶。 使用像jOOQ這樣的類型安全的API會(huì)非常有用,但是您可能會(huì)發(fā)現(xiàn)僅在10-15個(gè)本機(jī)查詢中就很難在同一應(yīng)用程序中維護(hù)兩個(gè)不同的連接,事務(wù)和會(huì)話模型。
但事實(shí)是:
您可以將jOOQ用于JPA本機(jī)查詢!
確實(shí)如此! 有幾種方法可以實(shí)現(xiàn)此目的。
提取元組(即Object [])
最簡單的方法將不會(huì)利用JPA的任何高級(jí)功能,而只是為您獲取JPA的本機(jī)Object[]形式的元組。 假設(shè)這個(gè)簡單的實(shí)用方法:
public static List<Object[]> nativeQuery(EntityManager em, org.jooq.Query query ) {// Extract the SQL statement from the jOOQ query:Query result = em.createNativeQuery(query.getSQL());// Extract the bind values from the jOOQ query:List<Object> values = query.getBindValues();for (int i = 0; i < values.size(); i++) {result.setParameter(i + 1, values.get(i));}return result.getResultList(); }使用API
這就是您以最簡單的形式橋接這兩個(gè)API所需要的,以通過EntityManager運(yùn)行“復(fù)雜”查詢:
List<Object[]> books = nativeQuery(em, DSL.using(configuration).select(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME, BOOK.TITLE).from(AUTHOR).join(BOOK).on(AUTHOR.ID.eq(BOOK.AUTHOR_ID)).orderBy(BOOK.ID));books.forEach((Object[] book) -> System.out.println(book[0] + " " + book[1] + " wrote " + book[2]));同意的結(jié)果中沒有很多類型安全性,因?yàn)槲覀冎坏玫揭粋€(gè)Object[] 。 我們期待著將來支持Scala或Ceylon之類的元組(甚至記錄)類型的Java。
因此,更好的解決方案可能是以下方法:
獲取實(shí)體
假設(shè)您具有以下非常簡單的實(shí)體:
@Entity @Table(name = "book") public class Book {@Idpublic int id;@Column(name = "title")public String title;@ManyToOnepublic Author author; }@Entity @Table(name = "author") public class Author {@Idpublic int id;@Column(name = "first_name")public String firstName;@Column(name = "last_name")public String lastName;@OneToMany(mappedBy = "author")public Set<Book> books; }并假設(shè),我們將添加一個(gè)附加的實(shí)用程序方法,該方法還將Class引用傳遞給EntityManager :
public static <E> List<E> nativeQuery(EntityManager em, org.jooq.Query query,Class<E> type ) {// Extract the SQL statement from the jOOQ query:Query result = em.createNativeQuery(query.getSQL(), type);// Extract the bind values from the jOOQ query:List<Object> values = query.getBindValues();for (int i = 0; i < values.size(); i++) {result.setParameter(i + 1, values.get(i));}// There's an unsafe cast here, but we can be sure// that we'll get the right type from JPAreturn result.getResultList(); }使用API
現(xiàn)在這相當(dāng)靈活,只需將jOOQ查詢放入該API并從中獲取JPA實(shí)體-兩者兼有,因?yàn)槟梢暂p松地從獲取的實(shí)體中添加/刪除嵌套集合,就好像您是通過JPQL來獲取它們一樣:
List<Author> authors = nativeQuery(em,DSL.using(configuration).select().from(AUTHOR).orderBy(AUTHOR.ID) , Author.class); // This is our entity class hereauthors.forEach(author -> {System.out.println(author.firstName + " " + author.lastName + " wrote");books.forEach(book -> {System.out.println(" " + book.title);// Manipulate the entities here. Your// changes will be persistent!}); });獲取實(shí)體結(jié)果
如果您比較敢于冒險(xiǎn)并且對(duì)注釋有一種奇怪的喜好 ,或者只想在休假前給同事開個(gè)玩笑,還可以使用JPA的javax.persistence.SqlResultSetMapping 。 想象以下映射聲明:
@SqlResultSetMapping(name = "bookmapping",entities = {@EntityResult(entityClass = Book.class,fields = {@FieldResult(name = "id", column = "b_id"),@FieldResult(name = "title", column = "b_title"),@FieldResult(name = "author", column = "b_author_id")}),@EntityResult(entityClass = Author.class,fields = {@FieldResult(name = "id", column = "a_id"),@FieldResult(name = "firstName", column = "a_first_name"),@FieldResult(name = "lastName", column = "a_last_name")})} )本質(zhì)上,以上聲明將數(shù)據(jù)庫列( @SqlResultSetMapping -> entities -> @EntityResult -> fields -> @FieldResult -> column )映射到實(shí)體及其對(duì)應(yīng)的屬性。 使用這項(xiàng)強(qiáng)大的技術(shù),您可以從任何類型SQL查詢結(jié)果中生成實(shí)體結(jié)果。
同樣,我們將創(chuàng)建一個(gè)小的實(shí)用工具方法:
public static <E> List<E> nativeQuery(EntityManager em, org.jooq.Query query,String resultSetMapping ) {// Extract the SQL statement from the jOOQ query:Query result = em.createNativeQuery(query.getSQL(), resultSetMapping);// Extract the bind values from the jOOQ query:List<Object> values = query.getBindValues();for (int i = 0; i < values.size(); i++) {result.setParameter(i + 1, values.get(i));}// This implicit cast is a lie, but let's risk itreturn result.getResultList(); }請(qǐng)注意, 上面的API使用了anti-pattern ,在這種情況下可以使用,因?yàn)镴PA首先不是類型安全的API。
使用API
現(xiàn)在,再次,您可以通過上述API將類型安全的jOOQ查詢傳遞給EntityManager ,并傳遞SqlResultSetMapping的名稱,如下SqlResultSetMapping :
List<Object[]> result = nativeQuery(em,DSL.using(configuration.select(AUTHOR.ID.as("a_id"),AUTHOR.FIRST_NAME.as("a_first_name"),AUTHOR.LAST_NAME.as("a_last_name"),BOOK.ID.as("b_id"),BOOK.AUTHOR_ID.as("b_author_id"),BOOK.TITLE.as("b_title")).from(AUTHOR).join(BOOK).on(BOOK.AUTHOR_ID.eq(AUTHOR.ID)).orderBy(BOOK.ID)), "bookmapping" // The name of the SqlResultSetMapping );result.forEach((Object[] entities) -> {JPAAuthor author = (JPAAuthor) entities[1];JPABook book = (JPABook) entities[0];System.out.println(author.firstName + " " + author.lastName + " wrote " + book.title); });在這種情況下,結(jié)果仍然是Object[] ,但是這一次, Object[]并不表示具有單獨(dú)列的元組,而是表示由SqlResultSetMapping注釋聲明的實(shí)體。
這種方法很吸引人,當(dāng)您需要從查詢中映射任意結(jié)果但仍需要托管實(shí)體時(shí),可能會(huì)用到它。 如果您想了解更多信息,我們只能推薦Thorben Janssen關(guān)于這些高級(jí)JPA功能的有趣博客系列:
- 結(jié)果集映射:基礎(chǔ)
- 結(jié)果集映射:復(fù)雜映射
- 結(jié)果集映射:構(gòu)造函數(shù)結(jié)果映射
- 結(jié)果集映射:Hibernate特定功能
結(jié)論
在ORM和SQL之間(特別是在Hibernate和jOOQ之間)進(jìn)行選擇并不總是那么容易。
- 當(dāng)涉及到應(yīng)用對(duì)象圖持久性時(shí),即當(dāng)您有很多復(fù)雜的CRUD(涉及復(fù)雜的鎖定和事務(wù)策略)時(shí),ORM會(huì)閃耀。
- 當(dāng)運(yùn)行批量SQL(用于讀取和寫入操作),運(yùn)行分析,報(bào)告時(shí),SQL大放異彩。
當(dāng)您“幸運(yùn)”時(shí)(例如,工作很簡單),您的應(yīng)用程序僅位于安全柵的一側(cè),您可以在ORM和SQL之間進(jìn)行選擇。 當(dāng)您“幸運(yùn)”時(shí)(例如– ooooh,這是一個(gè)有趣的問題),您將不得不同時(shí)使用兩者。 ( 另請(qǐng)參閱Mike Hadlow關(guān)于該主題的有趣文章 )
這里的信息是:可以! 使用JPA的本機(jī)查詢API,您可以利用RDBMS的全部功能運(yùn)行復(fù)雜的查詢,并且仍然可以將結(jié)果映射到JPA實(shí)體。 您不限于使用JPQL。
邊注
盡管過去我們一直在批評(píng)JPA的某些方面(有關(guān)詳細(xì)信息,請(qǐng)閱讀JPA 2.1如何成為新的EJB 2.0 ),但我們的批評(píng)主要集中在JPA對(duì)注釋的濫用上。 當(dāng)使用jOOQ之類的類型安全API時(shí),可以輕松地向編譯器提供所有必需的類型信息以構(gòu)造結(jié)果。 我們堅(jiān)信,將來的JPA版本將更積極地使用Java的類型系統(tǒng),從而可以更流暢地集成SQ??L,JPQL和實(shí)體持久性。
翻譯自: https://www.javacodegeeks.com/2015/05/type-safe-queries-for-jpas-native-query-api.html
jpa的查詢api
總結(jié)
以上是生活随笔為你收集整理的jpa的查询api_为JPA的本机查询API键入安全查询的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 项目定位是什么意思 项目定位的含义是什么
- 下一篇: 摩托罗拉razr发布日期是什么时候