为JPA的本机查询API键入安全查询

当您使用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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/359913.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vb.net详解MDI窗体操作方法

MDI窗体可以避免打开窗体的时候被无数个子窗体困扰&#xff0c;我将为大家一一的介绍一下vb.net中MDI窗体的操作方法 一、如何创建MDI窗体&#xff1f; 1、创建mdi主窗体 新建建立一个默认空白的Windows应用程序&#xff0c;在Form1窗体的属性窗口中找到IsMDIContainer 属性&am…

lokijs可以用mysql_JavaScript实现的内存数据库LokiJS介绍和入门实例_javascript技巧

LokiJS是一个内存数据库&#xff0c;将性能考虑放在第一位。LokiJS支持索引和更快的文档访问&#xff0c;执行性能非常好(近50万OPS/秒)。其内置DynamicView类可以用于数据子集的索引&#xff0c;甚至获取更快的性能。*阅读这篇文章来看一看LokiJS的性能表现。LokiJS支持collec…

除了修改WEBCONFIG会导致WEB服务重启外,还有其他的什么操作会导致重启?

1、修改WEBCONFIG文件 2、BIN文件夹下&#xff0c;添加、删除、覆盖文件 3、IIS应用程序池回收 参考文章:http://blog.csdn.net/hb_gx/archive/2007/05/21/1619941.aspx转载于:https://www.cnblogs.com/niaowo/p/3686097.html

院队选拔赛

结束了&#xff0c;大半年的干活结束了&#xff0c;后面就是自己算法的大干活了。恩恩&#xff0c;目测就是下一站省赛了&#xff0c;早点睡觉。晚安~ 留个地址后面ak掉。hust stodgersma 转载于:https://www.cnblogs.com/stodgers/p/3898317.html

pandaboard 安装_linux fb设备(pandaboard) | 学步园

fb设备主要作用是获取帧buffer&#xff0c; 并设置&#xff0c;用于显示。fbmem是主要的框架层和抽象层。 每个具体的平台的帧设备是在调用platform_driver_register注册之后&#xff0c;由其probe函数调用create framebuffer将fbinfo设置给registered_fb(数组&#xff0c; 不同…

WinForm窗体之间传值

当程序需要将一个窗体中的一些信息传给另一个窗体并让其使用时,就需要用到这个知识点 方法一:通过接受参数的窗体的构造函数传值 例:现有Form1和Form2两个窗体,二者都包含一个文本框,Form1还包含一个按钮.程序从Form1开始运行,当点击Form1上的按钮时,Form2弹出,并将Form1中文本…

多线程写mysql数据库_多线程读写mysql数据库

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼unsigned int __stdcall scan(PVOID pM){char ip[20];strcpy(ip, (char*)pM);MYSQL mysql;MYSQL_RES* result;//初始化mysql句柄mysql_init(&mysql);//连接mysql数据库if(!mysql_real_connect(&mysql,"localhost"…

C++学习之路,漫长而遥远

一、C/C语言 如果你的基础很差&#xff0c; 建议不要一开始就学C语言&#xff0c;从C开始学起&#xff0c;对程序有个初步的认识&#xff0c;循序渐进。C语言的书嘛&#xff0c;先买一本 300 页以内的&#xff0c;把书中的每一个例子都通过键盘敲打进去到 Visual studio里面去&…

python图标icon_用Python提取exe图标icon

这里使用Python win32包中的win32gui.ExtractIconEx方法来提取exe的图标&#xff0c;除了安装Python&#xff0c;还需要到这里下载Pywin。第一份代码将指定的a.exe图标保存为bmp格式&#xff1a;import win32uiimport win32guilarge, small win32gui.ExtractIconEx(r"c:/…

python读取word图片_Python中如何读取Word中的图片

Python能够快速的编写、调试&#xff0c;用来提取各类软件中的图片再好不过了。今天小编就为大家带来在Python中提取Word图片的方法。方法需要批量的修改文件后缀名&#xff0c;并且解压之后将图片拷贝到需要存放的地方&#xff0c;然后将该文件夹清空留作下次的路径&#xff0…

layoutSubviews 详解

ios layout机制相关方法 - (CGSize)sizeThatFits:(CGSize)size - (void)sizeToFit——————- - (void)layoutSubviews - (void)layoutIfNeeded - (void)setNeedsLayout——————– - (void)setNeedsDisplay - (void)drawRectlayoutSubviews在以下情况下会被调用&#xff…

jdk中的设计模式_JDK中的设计模式

jdk中的设计模式Zen的JCG合作伙伴Brian Du Preez 是IT领域的合作伙伴&#xff0c; 在收集JDK中最常见的设计模式方面做得非常出色。 模式列表的确令人印象深刻且很长&#xff0c;因此让我们不再ba不休&#xff0c;然后将其呈现给您。 前几天&#xff0c;我在Enterprise Dev上看…

python鼠标选中事件_python对绑定事件的鼠标、按键的判断实例

当多个事件绑定了同一个命令&#xff0c;那么在命令内部根据不同的事件进行处理的时候&#xff0c;怎么确定哪个事件发生了呢&#xff0c;用下面的来检测&#xff0c;经过测试处理tab键和alt键不能识别&#xff0c;其他单个都能被识别。还有个事件的type属性&#xff0c;这个经…

PAT 1074. Reversing Linked List (25)

Given a constant K and a singly linked list L, you are supposed to reverse the links of every K elements on L. For example, given L being 1→2→3→4→5→6, if K 3, then you must output 3→2→1→6→5→4; if K 4, you must output 4→3→2→1→5→6. Input Sp…

jOOQ,H2和Maven入门

本文是我们学院课程的一部分&#xff0c;标题为jOOQ –类型安全的数据库查询 。 在SQL和特定关系数据库很重要的Java应用程序中&#xff0c;jOOQ是一个不错的选择。 当JPA / Hibernate抽象过多&#xff0c;JDBC过多时&#xff0c;这是一种替代方法。 它显示了一种现代的领域特…

sqlserver迁移数据到mysql_SQLServer数据库之将ABP的数据库从SQLSERVER迁移到MySql

本文主要向大家介绍了SQLServer数据库之将ABP的数据库从SQLSERVER迁移到MySql&#xff0c;通过具体的内容向大家展现&#xff0c;希望对大家学习SQLServer数据库有所帮助。安装MySql.Data.Entity然后你需要安装 MySql.Data.Entity和 MySql.Data 到你的 .EntityFramework 和 .We…

查询SQL中某表里有多少列包含某字段

select c.namefrom SYSCOLUMNS as c left join SYSOBJECTS as t on c.idt.id where c.name like 这里是某个字段% and t.name这里是表名 转载于:https://www.cnblogs.com/qiywtc/p/3719087.html

php与mysql连接程序_PHP与Mysql连接

首先请确保LAMP环境完全配置成功&#xff0c;否则请猛击我&#xff01;然后通过mysql的密码登陆到phpMyAdmin&#xff0c;在浏览器中输入http://127.0.0.1/phpMyAdmin登陆后就像是这样&#xff1a;首先我们来创建一个用于测试的数据库。偷懒的话直接在phpMyAdmin中创建就可以了…

java自动gc_具有Java 7中自动资源管理功能的GC

java自动gc这篇文章简要概述了Java 7中引入的称为自动资源管理或ARM的新功能。 文章探讨了ARM如何减少开发人员为有效释放分配的资源的JVM堆而必须编写的代码。 Java编程语言中编程的最甜蜜之处之一是对象取消分配的自动处理。 在Java世界中&#xff0c;这通常被称为垃圾收集。…

ASP.NET 程序优化

一、SqlDataRead和Dataset的选择 Sqldataread优点&#xff1a;读取数据非常快。如果对返回的数据不需做大量处理的情况下&#xff0c;建议使用SqlDataReader&#xff0c;其性能要比datset好很多。缺点&#xff1a;直到数据读完才可close掉于数据库的连接 (SqlDataReader 读数据…