当您使用JPA时-有时-JPQL无法解决问题,您将不得不使用本机SQL。 从一开始,像Hibernate这样的ORM就为这些情况保留了一个开放的“后门”,并为Spring的JdbcTemplate , Apache DbUtils或jOOQ提供了类似的API,用于纯SQL 。 这很有用,因为您可以继续将ORM用作数据库交互的单个入口点。

但是,使用字符串连接编写复杂的动态SQL既繁琐又容易出错,并且是SQL注入漏洞的门户。 使用像jOOQ这样的类型安全的API会非常有用,但是您可能会发现仅在10-15个本机查询中就很难在同一应用程序中维护两个不同的连接,事务和会话模型。

但事实是:

您可以将jOOQ用于JPA本机查询!

确实如此! 有几种方法可以实现此目的。

提取元组(即Object [])

最简单的方法将不会利用JPA的任何高级功能,而只是为您获取JPA的本机Object[]形式的元组。 假设这个简单的实用方法:

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运行“复杂”查询:

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]));

同意的结果中没有很多类型安全性,因为我们只得到一个Object[] 。 我们期待着将来支持Scala或Ceylon之类的元组(甚至记录)类型的Java。

因此,更好的解决方案可能是:

获取实体

假设您具有以下非常简单的实体:

@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;
}

并假设,我们将添加一个附加的实用程序方法,该方法还将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

现在这相当灵活,只需将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!});
});

获取实体结果

如果您比较敢于冒险并且对注释有一种奇怪的喜好 ,或者只是想在休假前给同事开个玩笑,还可以使用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")})}
)

本质上,以上声明将数据库列( @SqlResultSetMapping -> entities -> @EntityResult -> fields -> @FieldResult -> column )映射到实体及其相应属性。 使用这种强大的技术,您可以从任何类型的SQL查询结果中生成实体结果。

同样,我们将创建一个小的实用工具方法:

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

现在,再次,您可以通过上述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);
});

在这种情况下,结果仍然是Object[] ,但是这一次, Object[]并不表示具有单独列的元组,而是表示由SqlResultSetMapping注释声明的实体。

这种方法很吸引人,当您需要映射查询的任意结果但仍然需要托管实体时,可能会使用它。 如果您想了解更多信息,我们只能推荐Thorben Janssen关于这些高级JPA功能的有趣博客系列:

  • 结果集映射:基础
  • 结果集映射:复杂映射
  • 结果集映射:构造函数结果映射
  • 结果集映射:休眠特定功能

结论

在ORM和SQL之间(特别是在Hibernate和jOOQ之间)进行选择并不总是那么容易。

  • 当涉及到应用对象图持久性时,即当您有很多复杂的CRUD(涉及复杂的锁定和事务策略)时,ORM会大放异彩。
  • 当运行批处理SQL(用于读取和写入操作),运行分析和报告时,SQL表现出色。

当您“很幸运”时(例如,工作很简单),您的应用程序仅位于安全栅的一侧,您可以在ORM和SQL之间进行选择。 当您“幸运”时(例如– ooooh,这是一个有趣的问题),您将不得不同时使用两者。 ( 另请参阅Mike Hadlow关于该主题的有趣文章 )

这里的信息是:可以! 使用JPA的本机查询API,您可以利用RDBMS的全部功能运行复杂的查询,并且仍然可以将结果映射到JPA实体。 您不限于使用JPQL。

边注

尽管过去我们一直在批评JPA的某些方面(有关详细信息,请参阅JPA 2.1如何成为新的EJB 2.0 ),但我们的批评主要集中在JPA对注释的滥用上。 当使用诸如jOOQ之类的类型安全API时,可以轻松地向编译器提供所有必需的类型信息以构造结果。 我们坚信,将来的JPA版本将更积极地使用Java的类型系统,从而可以更流畅地集成SQ​​L,JPQL和实体持久性。

翻译自: https://www.javacodegeeks.com/2015/05/type-safe-queries-for-jpas-native-query-api.html

为JPA的本机查询API键入安全查询相关推荐

  1. jpa的查询api_为JPA的本机查询API键入安全查询

    jpa的查询api 当您使用JPA时-有时-JPQL不能解决问题,您将不得不使用本机SQL. 从一开始,像Hibernate这样的ORM就为这些情况保留了开放的"后门",并为Spr ...

  2. HTML + CSS + JS 利用邮编查询 API 实现邮编查询工具

    引言 邮政编码是地址信息的重要组成部分,可以帮助快递公司.物流公司等对地址进行快速.准确的识别和派送.因此,邮编查询工具应用在许多业务场景中都有广泛的应用,例如:电商平台.物流公司.金融机构等.通过使 ...

  3. 苏州公交实时查询api 根据线路编码查询详细信息

    苏州公交实时查询api-苏州公交状态实时跟踪,根据线路编码查询详细信息. 接口名称:苏州公交实时查询api 接口平台:api接口 接口地址:http://apis.juhe.cn/szbusline/ ...

  4. Android利用高德天气查询API实现天气查询功能

      主要功能: 登录.注册(需要有Web端):这个很好写,我使用SpringBoot搭建的Web端,配置好Mybatis,编写Dao层.Service层和Controller层就基本完成了. 首页显示 ...

  5. java跨域权重_爱站权重查询 API 接口请求调用

    原标题:爱站权重查询 API 接口请求调用 爱站权重查询 API 接口在网上已经很多且大都封装成了 API 供别人调用.支持前台跨域请求,以GET/POST方式提交即可.爱站权重查询 API 接口可以 ...

  6. delphi 调用php接口_爱站权重查询 API 接口请求调用

    爱站权重查询 API 接口在网上已经很多且大都封装成了 API 供别人调用.支持前台跨域请求,以GET/POST方式提交即可.爱站权重查询 API 接口可以查询百度权重.搜狗等级.360权重.神马权重 ...

  7. ajax jsp模糊查询源码,ajax模糊查询api

    ajax模糊查询api 内容精选 换一换 使用标签过滤实例POST /v2/{project_id}/{resource_type}/resource_instances/action请求样例acti ...

  8. trackingmore快递查询平台_快递查询API接口(trackingmore)

    快递查询接口 目前提供快递查询的接口平台有: 不同接口的区别: (1)Trackingmore支持380家快递公司,其中有55家为国内的快递,其余325家为国际快递.具体的价格为0.6分钱/单号左右, ...

  9. 从申请到调用:全国快递物流查询 API 使用教程

    引言 面对越来越多的快递需求和快递公司的日益增多,手动查询快递状态的工作变得愈发繁琐.此时,一个全国快递物流查询 API 的出现能够极大地提高查询的效率和准确性,解决人工查询的问题,为用户提供更加便捷 ...

最新文章

  1. 24个为Web开发人员准备的CSS3实用教程
  2. JavaScript移除绑定在元素上的匿名事件处理函数
  3. C中 #define
  4. [Android] 环境配置之Android Studio开发NDK
  5. CMakeListx.txt 编辑语法学习
  6. 【php】正则无法截取\反斜杠的解决方法
  7. Bringing up interface eth0: Device eth0 does not seem to be present, delaying initialization
  8. 23种设计模式之命令模式
  9. 阿里如何做到百万量级硬件故障自愈?
  10. python3.0如何画表格_Python图表绘制工具:Matplotlib_Part 3
  11. 微信搜一搜产品团队:三大能力助力内容优质呈现、品牌精细增长、服务精准触达
  12. 【Vegas原创】ProC环境搭建
  13. python开发基础作业02:三级菜单,使用字典dic及列表
  14. 乒乓球训练机_比教练更牛的全新乒乓球机器人,超拟人黑科技,引领未来体育浪潮...
  15. WinAPI: Rectangle - 绘制矩形
  16. 线性表文档之静态链表
  17. 青岛大学计算机专业春考,青岛大学春季高考分数线2020
  18. FT2004(D2000)开发实战之W25X10CL固件烧写
  19. linux账号延期,Linux用户密码过期延期
  20. 关于云计算必知的关键核心技术

热门文章

  1. SpringMVC的视图解析器
  2. synchronized原理_Java并发编程 -- synchronized保证线程安全的原理
  3. SpringMVC 参数校验
  4. 抽象工厂模式设计模式_创新设计模式:抽象工厂模式
  5. tls 使用java生成_同时使用传入和传出连接时,相互TLS身份验证存在Java问题
  6. javafx css_JavaFX缺少的功能调查:CSS
  7. javafx_JavaFX在这里留下来!
  8. jax-ws和jax-rs_带有JAX-WS和Spring的Web服务应用程序
  9. java中集合选取怎么选_集合中的可选
  10. gradle排除依赖_如何从Gradle中的所有依赖项中排除库