XMl基本操作

引言

使⽤Mybatis的注解⽅式,主要是来完成⼀些简单的增删改查功能. 如果需要实现复杂的SQL功能,建议使⽤XML来配置映射语句,也就是将SQL语句写在XML配置⽂件中.

之前,我们学习了,用注解的方式来实现MyBatis


接下来我们学习XML的⽅式

MyBatis XML的⽅式需要以下两步
  1. 配置数据库连接字符串和MyBatis
  2. 写持久层代码

 


MyBatis XML配置⽂件

配置连接字符串和MyBatis

此步骤需要进⾏两项设置,数据库连接字符串设置和 MyBatis 的 XML ⽂件配置。
# 数据库连接配置
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?
characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver
# 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件//mapper是目录,自己设置
//文件名的后缀部分必须是Mapper.xml不能修改,前面可以添加内容
//这里的*代表通配符
mybatis.mapper-locations=classpath:/myBatis/*Mapper.xml
mybatis:mapper-locations: classpath:mapper/**Mapper.xml

写持久层代码

持久层代码分两部分
  1. ⽅法定义 Interface
  2. ⽅法实现: XXX.xml

添加 mapper 接⼝
数据持久层的接⼝定义:
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserInfoXMlMapper {List<UserInfo> queryAllUser();
}
添加 UserInfoXMLMapper.xml

在resources目录下添加前面在配置文件里面写的xml路径

在xml文件里面添加如下内容:

其中,mapper标签里面的namespace属性里面填的是 mapper接口的路径

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper"></mapper>

查询所有⽤⼾的具体实现 :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserInfoXMlMapper"><select id="queryAllUser" resultType="com.example.demo.model.UserInfo">select username,`password`, age, gender, phone from userinfo</select>
</mapper>

单元测试
@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid queryAllUser() {List<UserInfo> userInfoList = userInfoMapper.queryAllUser();System.out.println(userInfoList);}}

增删改查操作

增(Insert)
UserInfoMapper接⼝
    Integer insertUser(UserInfo userInfo);
UserInfoMapper.xml实现
<insert id="insertUser">insert into userinfo (username, `password`, age, gender, phone) values (#
{username}, #{password}, #{age},#{gender},#{phone})
</insert>
如果使⽤@Param设置参数名称的话, 使⽤⽅法和注解类似UserInfoMapper接⼝:
 Integer insertUser(@Param("userinfo") UserInfo userInfo);
UserInfoMapper.xml实现:
<insert id="insertUser">insert into userinfo (username, `password`, age, gender, phone) values(#{userinfo.username},#{userinfo.password},#{userinfo.age},#
{userinfo.gender},#{userinfo.phone})
</insert>

返回⾃增 id
接⼝定义不变, Mapper.xml 实现 设置useGeneratedKeys 和keyProperty属性
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">insert into userinfo (username, `password`, age, gender, phone) values(#{userinfo.username},#{userinfo.password},#{userinfo.age},#
{userinfo.gender},#{userinfo.phone})
</insert
删(Delete)
UserInfoMapper接⼝:
Integer deleteUser(Integer id);
UserInfoMapper.xml实现:
<delete id="deleteUser">delete from userinfo where id = #{id}
</delete>
改(Update)
UserInfoMapper接⼝:
Integer updateUser(UserInfo userInfo);
UserInfoMapper.xml实现:
<update id="updateUser">update userinfo set username=#{username} where id=#{id}
</update>
查(Select)
同样的, 使⽤XML 的⽅式进⾏查询, 也存在数据封装的问题我们把SQL语句进⾏简单修改, 查询更多的字段内容.
<select id="queryAllUser" resultType="com.example.demo.model.UserInfo">select id, username,`password`, age, gender, phone, delete_flag, 
create_time, update_time from userinfo
</select>
运⾏结果:
解决办法和注解类似:
  • 起别名
  • 结果映射
  • 开启驼峰命名
其中1,3的解决办法和注解⼀样,不再多说, 接下来看下xml如果来写结果映射
Mapper.xml
<resultMap id="BaseMap" type="com.example.demo.model.UserInfo"><id column="id" property="id"></id><result column="delete_flag" property="deleteFlag"></result><result column="create_time" property="createTime"></result><result column="update_time" property="updateTime"></result>
</resultMap>
<select id="queryAllUser" resultMap="BaseMap">select id, username,`password`, age, gender, phone, delete_flag, 
create_time, update_time from userinfo
</select>

#{} 和 ${}

MyBatis 参数赋值有两种⽅式, 咱们前⾯使⽤了 #{} 进⾏赋值, 接下来我们看下⼆者的区别
#{} 和${} 使⽤
先看Interger类型的参数
@Select("select username, `password`, age, gender, phone from userinfo where 
id= #{id} ")
UserInfo queryById(Integer id);
观察我们打印的⽇志
发现我们输出的SQL语句:
select username, `password`, age, gender, phone from userinfo where id= ?
我们输⼊的参数并没有在后⾯拼接,id的值是使⽤ ? 进⾏占位. 这种SQL 我们称之为"预编译SQL"
我们把 #{} 改成 ${} 再观察打印的⽇志:
@Select("select username, `password`, age, gender, phone from userinfo where 
id= ${id} ")
UserInfo queryById(Integer id);

可以看到, 这次的参数是直接拼接在SQL语句中了.
接下来我们再看String类型的参数
@Select("select username, `pasword`, age, gender, phone from userinfo where 
username= #{name} ")
UserInfo queryByName(String name);
观察我们打印的⽇志, 结果正常返回

我们把 #{} 改成 ${} 再观察打印的⽇志
@Select("select username, `password`, age, gender, phone from userinfo where 
username= ${name} ")
UserInfo queryByName(String name);

可以看到, 这次的参数依然是直接拼接在SQL语句中了, 但是字符串作为参数时, 需要添加引号 '' , 使
${} 不会拼接引号 '' , 导致程序报错.
修改代码如下:
@Select("select username, `password`, age, gender, phone from userinfo where 
username= '${name}' ")
UserInfo queryByName(String name);
再次运⾏, 结果正常返回
从上⾯两个例⼦可以看出:
#{} 使⽤的是预编译SQL, 通过 ? 占位的⽅式, 提前对SQL进⾏编译, 然后把参数填充到SQL语句中.
#{} 会根据参数类型, ⾃动拼接引号 '' .
${} 会直接进⾏字符替换, ⼀起对SQL进⾏编译. 如果参数为字符串, 需要加上引号 '' .
参数为数字类型时, 也可以加上, 查询结果不变, 但是可能会导致索引失效, 性能下降.
#{} 和 ${}区别

#{} 和 ${} 的区别就是预编译SQL即时SQL 的区别.

简单回顾:
当客⼾发送⼀条SQL语句给服务器后, ⼤致流程如下:
  1. 解析语法和语义, 校验SQL语句是否正确
  2. 优化SQL语句, 制定执⾏计划
  3. 执⾏并返回结果
⼀条 SQL如果⾛上述流程处理, 我们称之为 Immediate Statements(即时 SQL)

 性能更⾼

绝⼤多数情况下, 某⼀条 SQL 语句可能会被反复调⽤执⾏, 或者每次执⾏的时候只有个别的值不同(⽐ 如 select 的 where ⼦句值不同, update 的 set ⼦句值不同, insert 的 values 值不同). 如果每次都需要经过上⾯的语法解析, SQL优化、SQL编译等,则效率就明显不⾏了.
预编译SQL,编译⼀次之后会将编译后的SQL语句缓存起来,后⾯再次执⾏这条语句时,不会再次编译(只是输⼊的参数不同), 省去了解析优化等过程, 以此来提⾼效率
更安全(防⽌SQL注⼊)
SQL注⼊:是通过操作输⼊的数据来修改事先定义好的SQL语句,以达到执⾏代码对服务器进⾏攻击的⽅法。
由于没有对⽤⼾输⼊进⾏充分检查,⽽SQL⼜是拼接⽽成,在⽤⼾输⼊参数时,在参数中添加⼀些
SQL关键字,达到改变SQL运⾏结果的⽬的,也可以完成恶意攻击。
sql 注⼊代码: ' or 1='1
@Select("select username, `password`, age, gender, phone from userinfo where 
username= '${name}' ")
List<UserInfo> queryByName(String name);
测试代码:
正常访问情况:

 

@Test
void queryByName() {List<UserInfo> userInfos = userInfoMapper.queryByName("admin");System.out.println(userInfos);
}
结果运⾏正常
SQL注⼊场景:
@Test
void queryByName() {List<UserInfo> userInfos = userInfoMapper.queryByName("' or 1='1");System.out.println(userInfos);
}
结果依然被正确查询出来了, 其中参数 or被当做了SQL语句的⼀部分
可以看出来, 查询的数据并不是⾃⼰想要的数据. 所以⽤于查询的字段,尽量使⽤ #{} 预查询的⽅式

SQL注⼊是⼀种⾮常常⻅的数据库攻击⼿段, SQL注⼊漏洞也是⽹络世界中最普遍的漏洞之⼀. 如果发⽣在⽤⼾登录的场景中, 密码输⼊为 ' or 1='1 , 就可能完成登录(不是⼀定会发⽣的场景, 需要看登录代码如何写)

 排序功能
从上⾯的例⼦中, 可以得出结论: ${} 会有SQL注⼊的⻛险, 所以我们尽量使⽤#{}完成查询
既然如此, 是不是 ${} 就没有存在的必要性了呢?
当然不是.
Mapper实现
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +"from userinfo order by id ${sort} ")
List<UserInfo> queryAllUserBySort(String sort);
使⽤ ${sort} 可以实现排序查询, ⽽使⽤ #{sort} 就不能实现排序查询了.
注意: 此处 sort 参数为String类型, 但是SQL语句中, 排序规则是不需要加引号 '' 的, 所以此时的${sort} 也不加引号
我们把 ${} 改成 #{}
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +"from userinfo order by id #{sort} ")
List<UserInfo> queryAllUserBySort(String sort);

可以发现, 当使⽤ #{sort} 查询时, asc 前后⾃动给加了引号, 导致 sql 错误
#{} 会根据参数类型判断是否拼接引号 ''
如果参数类型为String, 就会加上 引号.
除此之外, 还有表名作为参数时, 也只能使⽤ ${}
like 查询

like 使⽤ #{} 报错

@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +"from userinfo where username like '%#{key}%' ")
List<UserInfo> queryAllUserByLike(String key);
把 #{} 改成 ${} 可以正确查出来, 但是${}存在SQL注⼊的问题, 所以不能直接使⽤ ${}.
解决办法: 使⽤ mysql 的内置函数 concat() 来处理,实现代码如下:
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +"from userinfo where username like concat('%',#{key},'%')")
List<UserInfo> queryAllUserByLike(String key);

数据库连接池

数据库连接池负责分配、管理和释放数据库连接,它允许应⽤程序重复使⽤⼀个现有的数据库连接,⽽不是再重新建⽴⼀个.
  • 没有使⽤数据库连接池的情况: 每次执⾏SQL语句, 要先创建⼀个新的连接对象, 然后执⾏SQL语句, SQ语句执⾏完, 再关闭连接对象释放资源. 这种重复的创建连接, 销毁连接⽐较消耗资源
  • 使⽤数据库连接池的情况: 程序启动时, 会在数据库连接池中创建⼀定数量的Connection对象, 当客⼾ 请求数据库连接池, 会从数据库连接池中获取Connection对象, 然后执⾏SQL, SQL语句执⾏完, 再把 Connection归还给连接池
优点:
  1. 减少了⽹络开销
  2. 资源重⽤
  3. 提升了系统的性能
常⻅的数据库连接池:
  • C3P0
  • DBCP
  • Druid
  • Hikari
⽬前⽐较流⾏的是 Hikari, Druid
Hikari : SpringBoot默认使⽤的数据库连接池

 

 

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

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

相关文章

【STM32】按键控制LED光敏传感器控制蜂鸣器(江科大)

一、按键控制LED LED.c #include "stm32f10x.h" // Device header/*** 函 数&#xff1a;LED初始化* 参 数&#xff1a;无* 返 回 值&#xff1a;无*/ void LED_Init(void) {/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENAB…

数据结构(稀疏数组)

简介 稀疏数组是一种数据结构&#xff0c;用于有效地存储和处理那些大多数元素都是零或者重复值的数组。在稀疏数组中&#xff0c;只有非零或非重复的元素会被存储&#xff0c;从而节省内存空间。 案例引入 假如想把下面这张表存入文件&#xff0c;我们会怎么做&#xff1f;…

超简单安装指定版本的clickhouse

超简单安装指定版本的clickhouse 命令执行shell脚本 idea连接 命令执行 参考官网 # 下载脚本 wget https://raw.githubusercontent.com/183461750/doc-record/d988dced891d70b23c153a3bbfecee67902a3757/middleware/data/clickhouse/clickhouse-install.sh # 执行安装脚本(中…

记录些Spring+题集(12)

11种API性能优化方法 一、索引优化 接口性能优化时&#xff0c;大家第一个想到的通常是&#xff1a;优化索引&#xff0c;优化索引的成本是最小的。 你可以通过查看线上日志或监控报告&#xff0c;发现某个接口使用的某条SQL语句耗时较长。 这条SQL语句是否已经加了索引&…

PHP Program to print pyramid pattern (打印金字塔图案的程序)

编写程序打印由星星组成的金字塔图案 例子 &#xff1a; 输入&#xff1a;n 6输出&#xff1a; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 我们强烈建…

基于术语词典干预的机器翻译挑战赛笔记Task2 #Datawhale AI 夏令营

上回&#xff1a; 基于术语词典干预的机器翻译挑战赛笔记Task1 跑通baseline Datawhale AI 夏令营-CSDN博客文章浏览阅读718次&#xff0c;点赞11次&#xff0c;收藏8次。基于术语词典干预的机器翻译挑战赛笔记Task1 跑通baselinehttps://blog.csdn.net/qq_23311271/article/d…

Linux网络:应用层协议HTTP(一)

一、什么是HTTP协议 虽然我们说, 应用层协议是我们程序猿自己定的. 但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP(超文本传输协议)就是其中之一。 在互联网世界中&#xff0c;HTTP&#xff08;HyperText Transfer Protocol&…

【考研数学】线代满分经验分享+备考复盘

我一战二战复习都听了李永乐的线代课&#xff0c;二战的时候只听了一遍强化&#xff0c;个人感觉没有很乱&#xff0c;永乐大帝的课逻辑还是很清晰的。 以下是我听向量这一章后根据听课内容和讲义例题总结的部分思维导图&#xff0c;永乐大帝讲课的时候也会特意点到线代前后联…

基于百度地图API实现地图位置选取的移动端页面开发教程 (JQuery+Html+JavaScript+CSS)

本文详细讲解了如何使用百度地图API实现移动端页面中的地图位置选取功能。文章首先介绍了百度地图API的2.0和3.0版本功能&#xff0c;并重点采用3.0 API。接着&#xff0c;逐步展示了如何构建基本的地图页面&#xff0c;如何通过点击地图获取经纬度和地理信息&#xff0c;以及如…

uniapp vue3 上传视频组件封装

首先创建一个 components 文件在里面进行组件的创建 下面是 vvideo组件的封装 也就是图片上传组件 只是我的命名是随便起的 <template><!-- 上传视频 --><view class"up-page"><!--视频--><view class"show-box" v-for"…

【Android面试八股文】荣耀面试算法题:输入一个N阶方阵(0<N<10),输出此方阵顺时针旋转M(0<=M<=10000)次后的方阵

文章目录 1. 算法题:输入一个N阶方阵(0<N<10),输出此方阵顺时针旋转M(0<=M<=10000)次后的方阵1.1 题目描述1.2 算法实现1.2.1 步骤说明:1.2.2 算法实现1.2.3 代码实现:1.2.4 程序说明:1.2.5 示例详细讲解如何将一个矩阵顺时针旋转90度1. 算法题:输入一个N阶方…

达梦数据库DM8-索引篇

目录 一、前景二、名词三、语法1、命令方式创建索引1.1 创建索引空间1.2.1 创建普通索引并指定索引数据空间1.2.2 另一种没验证&#xff0c;官方写法1.3 复合索引1.4 唯一索引1.5 位图索引1.6 函数索引 2、创建表时候创建索引3、可视化方式创建索引3.1 打开DM管理工具3.2 找到要…

生成式人工智能落地校园与课堂的15个场景

生成式人工智能正在重塑教育行业&#xff0c;为传统教学模式带来了革命性的变化。随着AI的不断演进&#xff0c;更多令人兴奋的应用场景将逐一显现&#xff0c;为学生提供更加丰富和多元的学习体验。 尽管AI在教学中的应用越来越广泛&#xff0c;但教师们也不必担心会被完全替代…

[电机控制]-三相鼠笼电机simulink建模

三相鼠笼电机simulink建模 1 方程 电机方程&#xff1a; d i s α d t K 1 i s α K 2 ϕ r α K 3 ω r ϕ r β K 4 v s α \frac{di_{s\alpha}}{dt}K_{1}i_{s\alpha}K_{2}\phi_{r\alpha}K_{3}\omega_{r}\phi_{r\beta}K_{4}v_{s\alpha} dtdisα​​K1​isα​K2​ϕrα…

VSCODE 下 openocd Jlink 的配置笔记

title: VSCODE 下 openocd Jlink 的配置笔记 tags: STM32HalCubemax 文章目录 内容VSCODE 下 openocd Jlink 的配置笔记安装完成后修改jlink的配置文件然后修改你的下载器为jlink烧录你的项目绝对会出现下面的问题那么打开下载的第一个软件 &#xff08;点到这个jlink右键&…

视频分帧【截取图片】(YOLO目标检测【生成数据集】)

高效率制作数据集【按这个流程走&#xff0c;速度很顶】 本次制作&#xff0c;1059张图片【马路上流动车辆】 几乎就是全自动了&#xff0c;只要视频拍得好&#xff0c;YOLO辅助制作数据集就效率极高 视频中的图片抽取&#xff1a; 【由于视频内存过大&#xff0c;遇到报错执行…

IO半虚拟化-vhost学习笔记

参考&#xff1a;系《深入浅出dpdk》学习笔记以及redhat的官方博客 vhost属于virtio-net网络设备的后端驱动&#xff0c;经历了从virtio-net后端&#xff0c;到内核态vhost-net&#xff0c;到vhost-user的演进过程。先过一下背景知识&#xff0c; 背景知识 QEMU QEMU 是一个…

几种常用排序算法

1 基本概念 排序是处理数据的一种最常见的操作&#xff0c;所谓排序就是将数据按某字段规律排列&#xff0c;所谓的字段就是数据节点的其中一个属性。比如一个班级的学生&#xff0c;其字段就有学号、姓名、班级、分数等等&#xff0c;我们既可以针对学号排序&#xff0c;也可…

huawei USG6001v1学习---防火墙高可靠性(双机热备)

1.什么是双机热备 如图&#xff1a;当左图的防火墙发生故障时&#xff0c;整个系统都会收到影响&#xff0c;而右图即使有防火墙发生故障&#xff0c;但是还有一台防火墙做备份&#xff0c;相对于只有一台防火墙&#xff0c;要可靠些。 由于防火墙上不仅需要同步配置信息&…

【Linux】—— 进程的基本概念、PCB、fork

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;Linux跬步积累 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日一题 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0…