MyBatis_3

上一篇文章,我们学习了使用XML实现MyBatis进行增、删、查、改等操作,本篇文章,我们将学习#{ }和${ }获取方法参数的区别和使用MyBatisXML实现动态SQL语句。

#{ }和${ }的区别

在之前的文章中我们都是使用#{ }进行赋值,但实际上Mybatis也支持${ } 对参数进行赋值。

Integer类型的参数

我们先来看Integer类型的参数的赋值:

mapper:

  //Integer@Select("select * from userinfo where id = #{id} ")List<Userinfo> getById(Integer id);@Select("select * from userinfo where id = ${id} ")List<Userinfo> getById2(Integer id);

测试:

    @Testvoid getById() {System.out.println(mapper.getById(1));}@Testvoid getById2() {System.out.println(mapper.getById(1));}

#{ }: 

${ }: 

可以看到查询到的结果是一样的,都能够正常查到。此时,我们再换一个String类型试试。

String类型的参数

mapper:

   //String@Select("select * from userinfo where username = #{username} ")List<Userinfo> getByName(String username);@Select("select * from userinfo where username = ${username} ")List<Userinfo> getByName2(String username);

测试:

   @Testvoid getByName() {System.out.println(mapper.getByName("xmy"));}@Testvoid getByName2() {System.out.println(mapper.getByName2("xmy"));}

#{ }: 

测试通过。 

 ${ }:

可以看到报出BadSql异常。

此时观察日志,我们传过去的sql语句是这样的:通过观察,我们发现xmy处少了引号,正确的sql语句是‘xmy’。我们手动给sql语句加上引号,修改代码如下: 

    @Select("select * from userinfo where username = '${username}' ")List<Userinfo> getByName2(String username);

测试通过。

通过对比#{ }和${ }打出来的日志我们发现#{ }是预编译sql(通过?占位的方式,提前对sql进行编译然后把参数填充到SQL语句中),#{ }会根据参数类型自动拼接引号;而${ }则是直接进行字符替换,一起对SQL进行编译(即时sql)。如果参数为字符串,需要加上引号。 

在开发环境下,我们能使用#{ }就不要使用${ }。不仅仅因为#{ }的效率比${ }更高,而是因为${ }可能会产生sql注入的问题。

那么什么是sql注入呢?下面我们通过代码来演示sql注入。

${ }引发sql注入问题

sql注入:通过操作输入的数据来修改事先定义好的sql语句,以达到执行代码对服务器进行攻击的方法。

我们先尝试在数据库中使用下面的sql语句进行查询:

SELECT * FROM `userinfo` where username = ' OR 1 = '1;

 可以看到此时我们的代码是有问题的:

下面我们使用#{ }和${ } 分别进行查询:

  //String@Select("select * from userinfo where username = #{username} ")List<Userinfo> getByName(String username);@Select("select * from userinfo where username = '${username}' ")List<Userinfo> getByName2(String username);

 测试:

    @Testvoid getByName() {System.out.println(mapper.getByName("' OR 1 = '1"));}@Testvoid getByName2() {System.out.println(mapper.getByName2("' OR 1 = '1"));}

#{ }: 

${ }: 可以看到,${ }依然正常查询出来了,其中参数or被当作了SQL语句的一部分:

${ }的作用

从上面的例子中,我们可以知道:${}会有sql注入的风险,所以我们尽量使用#{}完成查询。

既然如此,${ }是不是就没有存在的必要了呢?

当然不是。接下来我们通过代码来看看${ }的作用。

1、使用${}实现排序功能

mapper: 

 @Select("select * from userinfo order by  id ${order}")List<Userinfo> getByOrder(String order);

测试: 

    @Testvoid getByOrder() {System.out.println(mapper.getByOrder("desc"));}

可以看到,能够根据id逆序输出结果。此时我们将${ }改成#{ } 。

    @Select("select * from userinfo order by id #{order}")List<Userinfo> getByOrder(String order);

测试结果: 

可以发现,当使用#{sort}查询时,desc前后加上了引号,导致sql错误。

2、使用${ }实现模糊查询

 mapper:

    @Select("select * from userinfo where username like '%${username}%'")List<Userinfo> getByLike(String username);

测试: 

 @Testvoid getByLike() {System.out.println(mapper.getByLike("zhangsan"));}

此时我们将${ } 改为#{ } :

 @Select("select * from userinfo where username like '%#{username}%'")List<Userinfo> getByLike(String username);

可以看到,依然是因为引号的关系,出现了异常。 

但是在模糊查询中使用#{ }也有解决办法 :我们可以使用concat()来拼接字符串:

  @Select("select * from userinfo where username like concat('%',#{username},'%')")List<Userinfo> getByLike1(String username);

测试: 

  @Testvoid getByLike1() {System.out.println(mapper.getByLike1("zhangsan"));}

总结: #{ }和${ }的区别

1、#{ }和${ }的区别就是预编译sql(占位)和即时sql(直接拼接)的区别。

2、#{ }使用预编译的形式所以性能会比${ }更高

绝大多数情况下,某一条sql语句可能会被反复调用执行,或者每次执行的时候只有个别的值不同(比如select的where子句值不同,insert的values值不同)。如果每次都需要上面语法解析,sql优化,sql编译等过程,效率就明显不行了。

预编译sql,编译一次之后会将编译后的sql语句缓存起来,后面再次执行这条语句时,不会再次编译(只是输入的参数不同),省去了解析优化等过程,以此来提高效率。

3、#{ }更安全(防止sql注入)

在使用${ }的场景下一定一定要考虑到sql注入问题,并采取措施进行防止:例如:1、在接口层(Controller层)进行判定,如果输入的结果不是我们想要的直接返回。2、直接给接口写死,根据用户输入的内容来决定调用哪个接口,如果没有接口符合,则返回。

4、在一些场景下仍然需要用到${ }

比如:排序,参数不需要引号…… 

数据库连接池

在MyBtis中,我们使用了数据库连接池技术,避免频繁地创建销毁连接。

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有地数据库连接,而不是重新建立一个。 

没有使用数据库连接池的情况:每次执行sql语句,要先创建一个新的连接对象,然后执行sql语句,sql语句执行完,再关闭连接对象释放资源。这种重复的创建连接,销毁连接比较消耗资源。

使用数据库连接池的情况:程序启动时,会在数据库连接池中创建一定数量的Connection对象,当客户请求数据库连接池,会从数据库连接池中获取Connection对象,然后执行sql,sql语句执行完,再把Connection归还给连接池。

优点:

1、减少了网络开销

2、资源重用

3、提升了系统性能 

动态SQL

动态sql是mybaatis的强大特性之一,能够完成不同条件下不同的sql拼接。

官方文档:动态 SQL_MyBatis中文网

 <if>标签

在注册用户的时候,可能会有这样一个问题,如下图所示:

注册分为两种字段:必填字段和非必填字段,那如果在添加用户的时候有不确定的字段传入,程序应该如何实现呢?

这时候就需要使用动态标签进行判断了,如添加的时候性别为非必填字段,具体实现如下(XML实现): 

Mapper:

  Integer insertBatch(Userinfo userinfo);

XML:

    <insert id="insertBatch">insert into userinfo (username,password,age<if test="gender!=null">,gender</if>)values (#{username},#{password},#{age}<if test="gender!=null">,#{gender}</if>)</insert>

测试(有性别): 

    @Testvoid insertBatch() {Userinfo userinfo = new Userinfo();userinfo.setUsername("lisi");userinfo.setAge(16);userinfo.setPassword("lisi666");userinfo.setGender(2);mapper.insertBatch(userinfo);}

测试(无性别): 

    @Testvoid insertBatch() {Userinfo userinfo = new Userinfo();userinfo.setUsername("lisi");userinfo.setAge(16);userinfo.setPassword("lisi666");mapper.insertBatch(userinfo);}
 if标签详解:

<trim>标签

mapper:

Integer insertBatch2(Userinfo userinfo);

假如我们有许多的元素需要选填,那么我们此时的XML语句会变成这样:

    <insert id="insertBatch2">insert into userinfo(<if test="username != null">username</if><if test="password != null">,password</if><if test="age != null">,age</if><if test="gender != null">,gender  </if>)values (<if test="username != null">#{username}</if><if test="password != null">,#{password}</if><if test="age != null">,#{age}</if><if test="gender != null">,#{gender}</if>)</insert>

我们测试的时候选填其中两个参数(性别,年龄): 

 @Testvoid insertBatch2() {Userinfo userinfo = new Userinfo();userinfo.setGender(1);userinfo.setAge(16);mapper.insertBatch2(userinfo);}

 测试不通过,观察报错日志发现是因为<if>标签的原因,导致我们sql语句多加了个逗号。

那么我们能不能把逗号加在后面呢?同样的,如果将逗号加在后面,那么后面也会多一个逗号。

下面我们使用<trim>标签来解决问题:

 <insert id="insertBatch2">insert into userinfo<trim prefix="(" suffix=")" prefixOverrides=","><if test="username != null">username</if><if test="password != null">,password</if><if test="age != null">,age</if><if test="gender != null">,gender</if></trim>values <trim prefix="(" suffix=")" prefixOverrides=","> <if test="username != null">#{username}</if><if test="password != null">,#{password}</if><if test="age != null">,#{age}</if><if test="gender != null">,#{gender}</if></trim></insert>

 测试通过:

那么<trim>标签的作用是什么呢? 

<trim>标签详解:

<where>标签

我们在淘宝上逛东西时,通常会有一些按钮能够动态组装我们的查询条件。

这种功能如何实现呢? 

1、通过上面的<trim>标签和<if>标签实现

mapper:

  List<Userinfo> queryByConditin(Userinfo userinfo);

XML: 

   <select id="queryByConditin" resultType="com.example.mybatis.model.Userinfo">select * from userinfo<trim prefix="where" prefixOverrides="and"><if test="username != null">username = #{username}</if><if test="age != null">and age = #{age}</if><if test="gender != null">and gender = #{gender}</if><if test="password != null">and password = #{password}</if></trim></select>

测试(查询姓名为:“lisi” 年龄为:16的用户): 

  @Testvoid queryByConditin() {Userinfo userinfo = new Userinfo();userinfo.setUsername("lisi");userinfo.setAge(16);System.out.println(mapper.queryByConditin(userinfo));

 这种查询方法固然能够成功,但是并不专业而且如果我们不添加任何查询条件时sql语句会多出来一个where导致Badsql异常,我们可以使用where标签来代替<trim>标签。

2、使用<where>标签实现

XML代码: 

    <select id="queryByConditin" resultType="com.example.mybatis.model.Userinfo">select * from userinfo<where><if test="username != null">username = #{username}</if><if test="age != null">and age = #{age}</if><if test="gender != null">and gender = #{gender}</if><if test="password != null">and password = #{password}</if></where></select>

测试: 

测试不加任何条件:

    @Testvoid queryByConditin() {Userinfo userinfo = new Userinfo();System.out.println(mapper.queryByConditin(userinfo));}

可以发现我们传入的sql语句并没有where。 

<where>标签详解:

<set>标签

与选择查询相同有时候我们也要更新一些用户的选项值,而保证其他值不变,比如:用户修改手机号、用户修改密码等。

同样的,这一功能也能通过<trim>标签和<if>标签实现:

mapper:

 Integer updateByConditin(Userinfo userinfo);

XML:

    <update id="updateByConditin">update userinfo<trim prefix="set" suffixOverrides=","><if test="username != null">username = #{username},</if><if test="age != null">age = #{age},</if><if test="gender != null">gender = #{gender},</if><if test="password != null">password = #{password},</if></trim>where id = #{id}</update>

测试(修改id为6的用户名和密码): 

    @Testvoid updateByConditin() {Userinfo userinfo = new Userinfo();userinfo.setPassword("123456");userinfo.setUsername("wangwu");userinfo.setId(6);mapper.updateByConditin(userinfo);}

同样的,我们也可以使用<set>标签来代替这里的<trim>标签和<if>标签: 

    <update id="updateByConditin">update userinfo<set><if test="username != null">username = #{username},</if><if test="age != null">age = #{age},</if><if test="gender != null">gender = #{gender},</if><if test="password != null">password = #{password},</if></set>where id = #{id}</update>

 测试(修改id为7的用户名和密码):

 @Testvoid updateByConditin() {Userinfo userinfo = new Userinfo();userinfo.setPassword("666666");userinfo.setUsername("wuwuwu");userinfo.setId(7);mapper.updateByConditin(userinfo);}

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

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

相关文章

智能图书馆管理系统开发实战系列(一):项目架构设计与技术选型

项目背景 智能图书馆管理系统&#xff08;ILMS&#xff09;是一个现代化的桌面应用程序&#xff0c;采用前后端分离架构&#xff0c;结合了Web技术的灵活性和桌面应用的用户体验。本项目从高保真原型设计开始&#xff0c;经过完整的软件开发生命周期&#xff0c;最终实现为一个…

应急前端“黄金3分钟”设计:极端场景下的操作界面极速搭建技术

摘要**地震突发&#xff0c;应急指挥系统的操作界面却因加载缓慢无法及时调取数据&#xff1b;火灾现场&#xff0c;消防员手持终端的操作步骤繁琐&#xff0c;延误救援时机。在分秒必争的极端场景中&#xff0c;传统前端操作界面为何频频 “掉链子”&#xff1f;怎样才能在 “…

【Android】三种弹窗 Fragment弹窗管理

三三要成为安卓糕手 零&#xff1a;布局转换 在很多工程当中用的都是LinearLayout和relativelayout&#xff0c;这两者都可以转化为Constrainlayout 注&#xff1a;这种用法并不能精确转换&#xff0c;具体还是要根据自己的需求来做布局约束一&#xff1a;snackbar显示弹窗 ((2…

【AI绘画】Stable Diffusion webUI 与 ComfyUI 全解析:安装、模型、插件及功能对比

一、Stable Diffusion 与 UI 工具概述 Stable Diffusion 是当前最主流的开源 AI 绘画模型&#xff0c;通过文本描述生成高质量图像。为降低使用门槛&#xff0c;开发者推出了多种图形界面&#xff08;UI&#xff09;工具&#xff0c;其中AUTOMATIC1111 webUI&#xff08;简称 …

ABP VNext + GraphQL Federation:跨微服务联合 Schema 分层

ABP VNext GraphQL Federation&#xff1a;跨微服务联合 Schema 分层 &#x1f680; 在微服务架构下&#xff0c;服务之间往往需要相互通信&#xff0c;而 GraphQL Federation 提供了一个有效的解决方案&#xff0c;帮助我们将多个微服务的 GraphQL API 聚合成一个统一的入口…

小程序组件的生命周期,以及在小程序中进行接口请求的方法设置

微信小程序组件生命周期与接口请求方法详解一、小程序组件生命周期微信小程序组件的生命周期指的是组件在不同阶段自动触发的函数&#xff0c;开发者可以利用这些钩子函数在特定时机执行相应操作。小程序组件的生命周期主要分为两类&#xff1a;组件自身生命周期和组件所在页面…

在线游戏玩家与物品交互处理

玩家与物品接触后的判定if (hit ! null && hit.CompareTag("Item")){Debug.Log("捡东西");var worldItem hit.gameObject.GetComponent<WorldItem>();if (worldItem ! null){var inventory GetComponent<PlayerInventory>();if (inv…

深入解析Java Stream 构建:AbstractPipeline

Java Stream 宏观介绍见&#xff1a;深入解析 Java Stream 设计&#xff1a;从四幕剧看流水线设计与执行机制-CSDN博客 PipelineHelper PipelineHelper 是 Java Stream API 内部一个至关重要的辅助类。正如其名&#xff0c;它是一个“管道助手”。可以把它想象成一个执行上下文…

《林景媚与命运回响》

《林景媚与命运回响》——当数据库开始回响命运&#xff0c;现实是否还能被信任&#xff1f;《林景媚数据库宇宙》系列第九部第一章&#xff1a;命运的涟漪公元 2089 年&#xff0c;数据库神谕的运行已趋于稳定&#xff0c;PostgreSQL Quantum Engine&#xff08;PQE&#xff0…

图神经网络入门:从GNN开始01图卷积网络GCN节点分类 02图注意力网络GAT 03图自编码器GAE 04 门控图神经网络GGNN

目录 一.基础1-[图论、图算法、CNN] 二.基础2-[图卷积神经网络GCN] 三.torch-geometric.nn工具包安装&#xff08;包含各种算法和数据集&#xff09; 四.GCN任务[节点分类-Cora 数据集] 五.图注意力网络&#xff08;GAT&#xff09; 六.图自编码器&#xff08;GAE&#x…

001 Configuration结构体构造

目录DramSys 代码分析1 Configuration结构体构造1.1 from_path 函数详解1.2 构造过程总结这种设计的好处2 Simulator 例化过程2.1 instantiateInitiatorDramSys 代码分析 1 Configuration结构体构造 好的&#xff0c;我们来详细解释一下 DRAMSysConfiguration.cpp 文件中 fro…

以太坊十年:智能合约与去中心化的崛起

以太坊10周年&#xff0c;敬开发者&#xff0c;敬构建者&#xff0c;敬还在链上的我们 以太坊即将迎来十周年纪念,作为一名在这个生态中深耕了8到9年的见证者&#xff0c;我亲历了它从一纸白皮书的构想到成长为全球领先去中心化平台的全过程。这十年间&#xff0c;以太坊经历了…

kafka 3.9.1版本: kraft + sasl+ standlone 模式完整可行安装步骤

Kafka 3.9.1 Kraft 单机模式安装 安装 OpenJDK 11 CentOS/RHEL yum install -y java-11-openjdk-develUbuntu/Debian apt install -y openjdk-11-jdk下载安装包 wget https://mirrors.aliyun.com/apache/kafka/3.9.1/kafka_2.12-3.9.1.tgz tar -zxvf kafka_2.12-3.9.1.tgz -C /…

Gitee DevOps平台深度评测:本土化优势与功能特性全面解析

Gitee DevOps平台深度评测&#xff1a;本土化优势与功能特性全面解析 在数字化转型浪潮下&#xff0c;企业软件开发流程的自动化与协作效率成为核心竞争力。作为国内领先的代码托管与DevOps平台&#xff0c;Gitee&#xff08;码云&#xff09;凭借其本土化服务与全流程支持能力…

从零开始本地化部署Dify:开源大模型应用平台搭建全指南

在AI应用开发的浪潮中&#xff0c;Dify作为一款开源的大语言模型(LLM)应用开发平台&#xff0c;正逐渐成为开发者和企业的首选工具。它巧妙地融合了后端即服务&#xff08;BaaS&#xff09;和LLMOps的理念&#xff0c;让开发者能够快速搭建生产级的生成式AI应用。无论是构建智能…

Qt 多媒体开发:音频与视频处理

Qt 多媒体模块提供了一套完整的 API&#xff0c;用于开发音频和视频处理应用。从简单的媒体播放到复杂的音视频编辑&#xff0c;Qt 都提供了相应的工具和组件。本文将从基础到高级全面解析 Qt 多媒体开发。 一、Qt 多媒体模块概述 1. 主要组件 Qt 多媒体模块包含以下核心组件&a…

Mac 专业图像处理 Pixelmator Pro

原文地址&#xff1a;Pixelmator Pro Mac 专业图像处理 Pixelmator Pro&#xff0c;是一款非常强大、美观且易于使用的图像编辑器&#xff0c;专为 Mac 设计。 采用单窗口界面、基于机器学习的智能图像编辑、自动水平检测&#xff0c;智能快速选择及更好的修复工具等功能优点…

iptables和IPVS比较

iptables 和 IPVS (IP Virtual Server) 都是 Linux 系统上用于处理网络流量的强大工具&#xff0c;但它们的设计目标、工作原理和适用场景有显著区别&#xff1a; 核心区别&#xff1a;主要目的&#xff1a; iptables&#xff1a; 核心是一个包过滤防火墙和网络地址转换工具。它…

语音识别指标计算 WER

目录 CER&#xff08;Character Error Rate&#xff09; WER Word Error Rate&#xff08;词错误率&#xff09; &#x1f9ee; WER 计算方式 &#x1f4cc; 示例 ✅ 理解要点 CER&#xff08;Character Error Rate&#xff09; 语音识别中的 CER&#xff08;Character …

【前端基础篇】JavaScript之jQuery介绍

文章目录前言JQuery基本介绍和使用方法引入依赖jQuery语法jQuery选择器jQuery事件操作元素获取/设置元素内容获取/设置元素属性获取/返回css属性添加元素删除元素总结&#xff1a;常用的jQuery方法 - 详细解释与示例事件处理拓展 - 详细解释与示例其他拓展内容前言 在阅读过程…