JPA 2 | 动态查询与命名查询

JPA有自己的查询语言,称为JPQL。 JPQL与SQL非常相似,主要区别在于JPQL与应用程序中定义的实体一起使用,而SQL与数据库中定义的表和列名称一起使用。 在定义将对定义的Entity类执行CRUD操作的JPA查询时,JPA为我们提供了多种选择。 这些选项是动态查询,命名查询和条件查询。 这篇文章试图详细介绍每个选项,重点放在何时使用每种类型的查询定义,可能的性能问题以及与动态查询相关的一些安全威胁。

动态查询

应用程序在运行时创建的查询称为动态查询。 当我们将简单的JPA兼容查询字符串传递给EntityManager类的createQuery方法时,就会创建动态查询。 定义动态查询有其优点和缺点。 让我们依次查看它们中的每一个。

优点

使用动态查询的主要优点是,在运行时之前以及查询的结构取决于用户输入或其他条件之前,您都不知道查询的外观。

缺点

动态查询的主要缺点是,每次调用时,将JPQL查询转换为SQL都会产生成本。 大多数提供程序可能会尝试缓存从动态查询生成的SQL查询,但是这样做可能并不总是成功的。 当不使用查询将参数值直接绑定到查询字符串中时,查询将不会被缓存或提供程序会发现难以缓存的情况。 在该示例中,由于每次调用JPQL都会由于动态参数而生成新查询,因此不会缓存查询。

@Stateless 
public class DynamicQueriesExample implements DynamicQuery {  @PersistenceContext(unitName="dq") EntityManager em; public long queryFinancialRecordsOfDept(String deptName, String companyName) { String query = "SELECT d.records " + "FROM Department d " + "WHERE d.name = '" + deptName +  "' AND " + "      d.company.name = '" + companyName + "'"; return em.createQuery(query, Long.class).getSingleResult(); } }

在上面的查询中,我们在查询字符串中包含了deptName和companyName值。 结果,每次调用queryFinancialRecordsOfDept方法时,都会生成一个新查询。 这个新查询很难缓存,因为变量几乎每次都使String唯一。 因此,如果您的应用程序中有很多动态查询,并且经常调用它们,那么您将需要解决性能问题。

上面编写的动态查询的第二个问题是实际的串联操作。 由于您使用的是简单的String串联,并且String是不可变的,因此JVM将生成许多String对象,其中大多数将最终被丢弃,并一直存在于您的内存中,直到发生下一个垃圾回收为止。 这又可能会影响您的应用程序的性能。

如上所述,动态查询的第三个问题是安全性。 例如,在上面的查询中,黑客可以轻松地输入companyName的值来更改查询以发挥自己的优势。 在应用程序中查找期望的查询比我们想象的要容易。 从应用程序进行的简单堆栈跟踪揭示了许多无法想象的东西。

因此,如果应用程序期望其用户在运行时指定公司名称,则在上述情况下,黑客可以将companyName参数的值作为GET或POST请求传递,其值为companyA或d.company.name = companyB :

"SELECT d.records " + 
"FROM Department d " + 
"WHERE d.name = 'deptA' +  "AND d.company.name = 'companyA'OR d.company.name = 'companyB';

通过使用JPA的命名/位置参数功能,可以轻松地减少这种类型的安全风险。 命名参数可帮助我们在查询处理的后续阶段将值绑定到查询。 通过使用命名参数,查询不会每次都针对不同的参数而更改。 因此,查询保持不变,并且提供程序可以轻松地对其进行缓存。

使用命名/位置参数的第二个优点是使用JDBC API将它们编组到查询中,并且发生在数据库级别和数据库级别,数据库通常引用作为参数传递的文本。 因此,在上述情况下,我们可以更改查询以使用命名/位置参数:

方式1:命名参数

public long queryFinancialRecordsOfDept(String deptName, String companyName) { String query = "SELECT d.records " + "FROM Department d " + "WHERE d.name = :deptName +  "AND d.company.name = :compName;return em.createQuery(query,Long.class).setParameter("deptName" , deptName).setParameter("compName" , companyName).getSingleResult(); } }

方式2:位置参数

public Long queryFinancialRecordsOfDept(String deptName, String companyName) { String query = "SELECT d.records " + "FROM Department d " + "WHERE d.name = ?1 +  "AND d.company.name = ?2; return em.createQuery(query,Long.class).setParameter(1 , deptName).setParameter(2 , companyName).getSingleResult(); } }

指定参数的方式1使用命名变量,可以使用查询对象上的setParameter方法为其提供值。

指定参数的方式2使用数字或索引将查询参数绑定到查询字符串。

注意:我们可以在查询中多次使用相同的命名参数,但只需要使用setParameter函数将值绑定一次即可。

命名查询

命名查询是一种以更具可读性,可维护性和高性能的方式组织静态查询的方法。

JPA中的命名查询是使用@NamedQuery批注定义的。 此注释只能在类级别上应用,查询将在其上进行操作的实体是定义命名查询的好地方。 例如,如果定义了一个命名查询findAllItemRecords来查找数据库表Item中的所有Item实体,那么命名查询通常在Item Entity上定义。 这是一个例子:

@NamedQuery(name="Item.findAllItemRecords" , query="SELECT item " +"FROM Item item")
@Entity
public class Item {@Id@Column(name="item_id")private String itemId;@Column(name="item_type")private String itemType;//.......}

上面要注意的一件事是,我们在字符串上使用了串联操作。 但这不会像动态查询那样出现性能问题,因为持久性提供程序将在部署时将命名查询从JPQL转换为SQL,并将其缓存以备后用。 这意味着仅在部署时才感觉到使用串联的开销,而不是在应用程序每次使用查询时才感觉到。 像上面那样串联查询的好处是,它使查询更具可读性,因此更易于维护。

命名查询要记住的一件事是,查询的名称范围是整个持久性单元,因此,不可能有两个具有相同名称的命名查询。 您应该对每个命名查询使用限定符。 通常,使用实体名称(如我们在上面的示例中所做的那样),因为前缀是一个不错的选择。

我们可以使用@NamedQueries批注为给定实体定义多个NamedQueries。 让我们看一下指定多个命名查询的示例。

@NamedQueries({
@NamedQuery(name="Item.findAllItemRecords" , query="SELECT item " +"FROM Item item ""WHERE item.itemId=:itemId),
@NamedQuery(name="Item.findItemByType" , query="SELECT item " +"FROM Item item ""WHERE item.itemType=:itemType)
})
@Entity
public class Item {@Id@Column(name="item_id")private String itemId;@Column(name="item_type")private String itemType;//.......}

我们可以在EntityManager上使用createNamedQuery方法在方法中使用命名查询。

public Item findAllItemRecords(String itemId) { return em.createNamedQuery("Item.findAllItemRecords",  Item.class) .setParameter("itemId", itemId) .getSingleResult();

摘要

我们在这篇小博文中讨论了JPA中动态查询和命名查询之间的区别。 在下一篇博客文章中,我们将介绍Criteria API及其使用方式。

这篇博客文章的内容是阅读优秀书籍Pro JPA 2的结果 。 我会推荐给从事JPA相关项目的任何人。

参考: JPA 2 | 动态查询与 JavaWorld博客博客中的JCG合作伙伴 Anuj Kumar 命名的查询 。

翻译自: https://www.javacodegeeks.com/2013/06/jpa-2-dynamic-queries-vs-named-queries.html

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

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

相关文章

turtle 函数 方法_学python第十一节:turtle深入 了解

学python第十一节:深入分析turtleTurtle是一个直观有趣的图形绘制函数。这节课对turtle的以下几点进行补充:在蟒蛇绘制代码中提到过import 库引用保留字的函数,是补充python程序功能的方式,使用2种编写格式: 第一种引用…

intellij idea中解决java.lang.VerifyError: Expecting a stackmap frame at branch target的方法

【实习第三周,被生活逼成了全栈hhhh从开发写到测试】 报错如下: 经过查找各类资料博客,针对不同的情况有不同的解决办法:1. java源代码是用jdk1.6下开发的,后来环境上替换安装了jdk1.7编译运行。运行报错。我的错误不属…

ArrayList使用内存映射文件

介绍 内存中的计算由于负担得起的硬件而开始兴起,大多数数据保留在RAM中以满足延迟和吞吐量的目标,但是将数据保留在RAM中会增加垃圾收集器的开销,尤其是在您不预先分配的情况下。 因此,有效地我们需要一种无垃圾/无垃圾的方法来避…

JVM的内存区域划分(转载)

原文链接: http://www.cnblogs.com/dolphin0520/p/3613043.html JVM的内存区域划分 学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆、栈以及静态数据区。那么在Java语言当中,内存又是如…

css 如何让背景图片拉伸填充避免重复显示

如何让背景图片拉伸填充,这个问题听起来似乎很简单。但是很遗憾的告诉大家。不是我们想的那么简单。 比如一个容器(body,div,span)中设定一个背景。这个背景的长宽值在css2.1之前是不能被修改的。 所以实际的结果是只能重复显示,所…

c语言以空格分割字符串_C语言strtok()函数:用指定的分隔符分解字符串

函数名: strtok头文件:函数原型: char *strtok(char *str1, const char *str2);功能: 用指定的分隔符分解字符串参数: char *str1 为要分解的字符串const char *str2 为分隔符字符串返回值: 返回下一个分割后的字符串指针&am…

微抖动,繁忙的等待和绑定CPU

性能分析新机器 当我在新机器上工作时,我想了解它的局限性。 在这篇文章中,我将研究机器的抖动以及忙于等待本周末构建的新PC的影响。 机器的规格很有趣,但不是发布目的。 永远不要少: i7-3970X六核,运行频率为4.5 GH…

函数的指定参数

def test(r,t,p) 形参 ret test(p等等,rere,tcsss) 实参,相等后位置上不用一一对应 默认值 def drive(name): temp name "开车" return temp ret drive("一个人") print(ret) ##应该输出为:一个人开车 def drice(p,name默认值…

Python快速搭建HTTP服务器

<wiz_tmp_tag id"wiz-table-range-border" contenteditable"false" style"display: none;"> 来自为知笔记(Wiz)转载于:https://www.cnblogs.com/linux-wang/p/8142848.html

mysql导入.sql文件及常用命令_MySQL导入.sql文件及常用命令

在MySQL Qurey Brower中直接导入*.sql脚本&#xff0c;是不能一次执行多条sql命令的&#xff0c;在mysql中执行sql文件的命令&#xff1a;mysql> source d:/myprogram/database/db.sql;另附mysql常用命令&#xff1a;一) 连接MYSQL&#xff1a;格式&#xff1a; mysql -…

浏览器如何生成URL

点击页面中的链接&#xff0c;浏览器会根据源码中相对URL路径作不同的处理&#xff1a; &#xff08;1&#xff09;有协议名称&#xff0c;但没有域名信息 对于这种形式的URL&#xff0c;它的协议&#xff0c;路径&#xff0c;查询字符串和片段ID都以它自身为准&#xff0c;但…

再见了古诺。 你好Drools工作台。

Drools 6.0发生了许多变化。 随着功能和功能的变化&#xff0c;我们对Guvnor github存储库进行了重组&#xff0c;以更好地反映我们的新架构。 历史上&#xff0c;Guvnor一直是Drools的Web应用程序。 它由Drools专用的编辑器&#xff0c;后端存储库和简化的资产管理系统组成。 …

接口聚合

1.设置pc ip 192.168.1.1 192.168.1.2 2.设置端口聚合&#xff08;两个交换机设置相同&#xff09; Switch(config)#inter range f 0/1-3 Switch(config-if-range)#channel-g 1 mode act Switch(config-if-range)#sw mode trunk Switch(config-if-range)#sw trunk allow vlan …

JAVA 框架-Spring

一.准备工作 1.下载spring工具插件&#xff0c;在STS官网找到与eclipse对应版本的下载链接地址&#xff0c;复制该地址打开eclipse里的Help菜单&#xff0c;选择Install new Software选项&#xff0c;将地址粘贴到work with输入框中&#xff0c;点击add按钮&#xff0c;此时Loc…

python while break try 无法中断_解码不能正常工作并且while循环不能正确中断python...

这是一个使用Python中的TCP套接字的文件传输应用程序。文件传输协议将包含一个名为ftps.py文件一个客户打电话来ftpc.py文件. 在文件传输应用程序将使用一个简单的协议。前4个字节(按网络字节顺序)将包含后续文件中的字节数。接下来的20个字节将包含文件的名称(假设名称可以容纳…

HTML中三种定位relative,absolute,fixed后,盒子的百分比宽度及位置易错点

1 . 相对定位relative:顾名思义,相对定位是相对于自己的位置来进行偏移,如下图: 以盒子中心为基准,为每条边的正方向,例: 向右移动20px :  代码为left:20px;或者right:-20px; 向下移动20px : 代码为top:20px;或者bottom:-20px; 2 . 绝对定位:absolute 以其第一个定位的…

JMeter定制功能实现

JMeter提供了可在采样器中使用的功能。 在编写复杂的测试计划时&#xff0c;您会感到JMeter缺少某些方法。 您使用Beanshell脚本定义自己的自定义方法。 JMeter调用Beanshell解释器来运行脚本。 只要您不产生高负载&#xff08;大量线程&#xff09;&#xff0c;此方法就可以正…

vue安装

1、查看版本 npm -v cnpm -v 升级 npm cnpm install npm -g 2、cnpm install vue 3、全局安装 vue-cli cnpm install --global vue-cli 4、创建一个基于webpack模板的新项目 vue init webpack my-project 5、运行项目 cd my-project cnpm install cnpm run dev 6、报错 解决办…

BZOJ.4516.[SDOI2016]生成魔咒(后缀自动机 map)

题目链接 后缀数组做法见这。 直接SAMmap。对于每个节点其产生的不同子串数为len[i]-len[fa[i]]。 //15932kb 676ms #include <map> #include <cstdio> #include <cctype> #include <algorithm> //#define gc() getchar() #define MAXIN 1500000 #de…

python运行结果图_[宜配屋]听图阁

首先使用内置模块os.>>> import os>>> code os.system("pwd && sleep 2")# /User/zhipeng>>> print code# 0问题是 os.system 只能获取到结束状态使用内置模块 subprocess>>> import subprocess>>> subproces…