jpa的查询api_为JPA的本机查询API键入安全查询
jpa的查詢api
當您使用JPA時-有時-JPQL不能解決問題,您將不得不使用本機SQL。 從一開始,像Hibernate這樣的ORM就為這些情況保留了開放的“后門”,并為Spring的JdbcTemplate , Apache DbUtils或jOOQ提供了類似的API,用于純SQL 。 這很有用,因為您可以繼續(xù)將ORM用作數(shù)據(jù)庫交互的單個入口點。
但是,使用字符串連接編寫復(fù)雜的動態(tài)SQL既繁瑣又容易出錯,并且是SQL注入漏洞的門戶。 使用像jOOQ這樣的類型安全的API會非常有用,但是您可能會發(fā)現(xiàn)僅在10-15個本機查詢中就很難在同一應(yīng)用程序中維護兩個不同的連接,事務(wù)和會話模型。
但事實是:
您可以將jOOQ用于JPA本機查詢!
確實如此! 有幾種方法可以實現(xiàn)此目的。
提取元組(即Object [])
最簡單的方法將不會利用JPA的任何高級功能,而只是為您獲取JPA的本機Object[]形式的元組。 假設(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
這就是您以最簡單的形式橋接這兩個API所需要的,以通過EntityManager運行“復(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é)果中沒有很多類型安全性,因為我們只得到一個Object[] 。 我們期待著將來支持Scala或Ceylon之類的元組(甚至記錄)類型的Java。
因此,更好的解決方案可能是以下方法:
獲取實體
假設(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è),我們將添加一個附加的實用程序方法,該方法還將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)在這相當靈活,只需將jOOQ查詢放入該API并從中獲取JPA實體-兩者兼有,因為您可以輕松地從獲取的實體中添加/刪除嵌套集合,就好像您是通過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!}); });獲取實體結(jié)果
如果您比較敢于冒險并且對注釋有一種奇怪的喜好 ,或者只想在休假前給同事開個玩笑,還可以使用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 )映射到實體及其對應(yīng)的屬性。 使用這項強大的技術(shù),您可以從任何類型SQL查詢結(jié)果中生成實體結(jié)果。
同樣,我們將創(chuàng)建一個小的實用工具方法:
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(); }請注意, 上面的API使用了anti-pattern ,在這種情況下可以使用,因為JPA首先不是類型安全的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[]并不表示具有單獨列的元組,而是表示由SqlResultSetMapping注釋聲明的實體。
這種方法很吸引人,當您需要從查詢中映射任意結(jié)果但仍需要托管實體時,可能會用到它。 如果您想了解更多信息,我們只能推薦Thorben Janssen關(guān)于這些高級JPA功能的有趣博客系列:
- 結(jié)果集映射:基礎(chǔ)
- 結(jié)果集映射:復(fù)雜映射
- 結(jié)果集映射:構(gòu)造函數(shù)結(jié)果映射
- 結(jié)果集映射:Hibernate特定功能
結(jié)論
在ORM和SQL之間(特別是在Hibernate和jOOQ之間)進行選擇并不總是那么容易。
- 當涉及到應(yīng)用對象圖持久性時,即當您有很多復(fù)雜的CRUD(涉及復(fù)雜的鎖定和事務(wù)策略)時,ORM會閃耀。
- 當運行批量SQL(用于讀取和寫入操作),運行分析,報告時,SQL大放異彩。
當您“幸運”時(例如,工作很簡單),您的應(yīng)用程序僅位于安全柵的一側(cè),您可以在ORM和SQL之間進行選擇。 當您“幸運”時(例如– ooooh,這是一個有趣的問題),您將不得不同時使用兩者。 ( 另請參閱Mike Hadlow關(guān)于該主題的有趣文章 )
這里的信息是:可以! 使用JPA的本機查詢API,您可以利用RDBMS的全部功能運行復(fù)雜的查詢,并且仍然可以將結(jié)果映射到JPA實體。 您不限于使用JPQL。
邊注
盡管過去我們一直在批評JPA的某些方面(有關(guān)詳細信息,請閱讀JPA 2.1如何成為新的EJB 2.0 ),但我們的批評主要集中在JPA對注釋的濫用上。 當使用jOOQ之類的類型安全API時,可以輕松地向編譯器提供所有必需的類型信息以構(gòu)造結(jié)果。 我們堅信,將來的JPA版本將更積極地使用Java的類型系統(tǒng),從而可以更流暢地集成SQ??L,JPQL和實體持久性。
翻譯自: 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发布日期是什么时候