前言:
大家好,我是良辰丫,今天还是我们的mybatis的学习,主要内容有两个占位符,sql注入问题,like模糊匹配,以及多表查询等,不断提升我们的编程能力,加油哈! ! !
个人主页:良辰针不戳
所属专栏:javaEE进阶篇之框架学习
励志语句:生活也许会让我们遍体鳞伤,但最终这些伤口会成为我们一辈子的财富。
期待大家三连,关注,点赞,收藏。
作者能力有限,可能也会出错,欢迎大家指正。
愿与君为伴,共探Java汪洋大海。
目录
- 1. 参数占位符 #{} 和 ${}
- 2. $符号处理关键字
- 3. sql注入问题
- 4. like模糊匹配
- 4.1 like与%连接通过#号预处理
- 4.2 like与%连接通过$符号处理
- 4.3 使用sql的拼接函数concat处理like
- 5. sql字段与java后端字段不统一
- 5.1 使用resultMap映射
- 5.2 通过mysql的as别名
- 7. 多表联查
1. 参数占位符 #{} 和 ${}
- #{}:预编译处理。
- ${}:字符直接替换。
- 预编译处理:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为” />Stu getStuName(@Param(“name”) String name);
xml配置,通过#号处理字段
<select id="getStuName" resultType="com.example.demo.entity.Stu">select * from stu where name = #{name}</select>
测试单元
@Testvoid getStuName() {Stu stu = stuMapper.getStuName("李白");Assertions.assertEquals(10,stu.getId());}
测试通过
接下来使用$号
<select id="getStuName" resultType="com.example.demo.entity.Stu">select * from stu where name = ${name}</select>
这个时候就会出现问题了,字符串是直接替换没有加引号.
我们如果想要去使用$去处理字符串也可以,我们只需要再xml里面的字段加上引号即可.2. $符号处理关键字
使用$
可以处理关键字,比如我们sql的升序降序关键字,因为$符号是直接替换,重要的事情要多次说,这样方便大家记忆.创建接口字段
List<Stu> getStuAll(String str);
xml配置文件
<select id="getStuAll" resultType="com.example.demo.entity.Stu">select * from stu order by id ${str};</select>
测试单元
@Testvoid getStuAll() {List<Stu> list = stuMapper.getStuAll("desc");}
如果我们传关键字的时候使用#号就会报错.
3. sql注入问题
- 接下来我们来模拟一个登录操作,来简单描述一下我们的注入问题
- 我们首先需要把stu表中只留下一个数据,否则属性注入会出现问题.
定义接口信息
Stu login(@Param("id") Integer id,@Param("name") String name);
xml配置
<select id="login" resultType="com.example.demo.entity.Stu">select * from stu where id = #{id} and name = #{name}</select>
- #是不会出现属性注入的.
- 我们主要看$符号
$符号在处理字符串的时候需要给它加引号,sql里面单引号和双引号都可以.xml信息
<select id="login" resultType="com.example.demo.entity.Stu">select * from stu where id = ${id} and name = '${name}'</select>
测试单元
@Testvoid login() {int id = 5;String name = "候六";//String name = "' or 1='1";Stu stu = stuMapper.login(id,name);if(stu != null){System.out.println("登录成功");}else {System.out.println("登录失败");}}
如果我们把name属性的key值写成下面的呢” />@Testvoid login() {int id = 5;String name = “‘ or 1=’1”;Stu stu = stuMapper.login(id,name);if(stu != null){System.out.println(“登录成功”);}else {System.out.println(“登录失败”);}}
我们会惊奇的发现这样也能查到数据库信息.
为什么会出现上面的问题呢” /><select id=“login” resultType=“com.example.demo.entity.Stu”>select * from stu where id = #{id} and name = #{name}<!—select * from stu where id = ${id} and name = ‘${name}‘—></select>
测试单元
@Testvoid login() {int id = 5;//String name = "候六";String name = "' or 1='1";Stu stu = stuMapper.login(id,name);if(stu != null){System.out.println("登录成功");}else {System.out.println("登录失败");}}
为什么#号不会出现sql注入,因为#号是预处理,它只会把那个句子当成一个value值,不会当成sql语句
4. like模糊匹配
like模式匹配会出问题” />4.1 like与%连接通过#号预处理
List<Stu> getStuSel(String str);
<select id="getStuSel" resultType="com.example.demo.entity.Stu">select * from stu where name like '#{str}%'</select>
@Testvoid getStuSel() {String str = "李";stuMapper.getStuSel(str);}
我们惊奇的发现此时使用#号出错了,与%联合使用并没有真正的做到拼接作用.
4.2 like与%连接通过$符号处理
如果我们把#号改成$号,会测试成功嘛” />
此时竟然测试成功.
- 虽然$符号测试成功,但是我们在上面了解到$会有sql注入的问题,那么接下来我们就要使用sql中的拼接函数concat实现我们相应的功能.
- 这样就能保证我们既使用#号,又可以成功查询
4.3 使用sql的拼接函数concat处理like
<select id="getStuSel" resultType="com.example.demo.entity.Stu"><!--select * from stu where name like '${str}%'-->select * from stu where name like concat(#{str},'%')</select>
5. sql字段与java后端字段不统一
如果我们java后端属性和sql的字段名字不统一的时候会出现什么问题呢” /> create table stu2(id int primary key auto_increment, username varchar(20), age int);
注意我们这个表是username属性,但是在我们java中是name属性
然后添加一条数据.
insert into stu2 values(null,'张三',20);
添加接口属性
Stu getStuId2(@Param("id") Integer id);
添加xml配置
<select id="getStuId2" resultType="com.example.demo.entity.Stu">select * from stu2 where id=${id}</select>
测试单元
@Testvoid getStuId2() {Stu stu = stuMapper.getStuId2(1);System.out.println(stu);}
运行单元测试代码,此时我们发现后端获取到的name属性为null.
5.1 使用resultMap映射
这个时候我们需要使用resultMap映射,把数据库的属性和java的属性一一映射.
添加接口方法.
Stu getStuId2(@Param("id") Integer id);
映射字段.
<resultMap id="Map" type="com.example.demo.entity.Stu"><id column="id" property="id"></id><result column="username" property="name"></result><result column="age" property="age"></result></resultMap>
- id表示主键.
- column表示数据库的字段名.
- id = “Map”中的Map是标识.
- type后面添加的是要映射的实体类.
- property是程序中的属性名
- result普通的字段和属性,也就resultMap里的一个标签.
xml配置
<select id="getStuId2" resultMap="Map">select * from stu2 where id=${id}</select>
5.2 通过mysql的as别名
- resultMap映射中只映射name,在单表查询中没有问题,但是在多表查询有一定的问题.
- resultMap的方式比较复杂,有没有简单的方式呢” /><select id=“getStuId2” resultType=“com.example.demo.entity.Stu”>select id,username as name,age from stu2 where id=${id}</select>
7. 多表联查
接下来我们创建一个学生信息表
create table stuInfo(stuId int not null,classInfo varchar(100),ident varchar(20));
在entity创建一个学生信息实体类StuInfo
package com.example.demo.entity;import lombok.Data;@Datapublic class StuInfo {private int stuId;private String classInfo;private String ident;}
在entity包下面建一个包vo,vo里面创一个类StuInfoVO
package com.example.demo.entity.vo;import com.example.demo.entity.StuInfo;import lombok.Data;@Datapublic class StuInfoVO extends StuInfo {//不想写基础字段,可以去继承private String username;}
在mapper包里面创一个StuInfoMapper接口
package com.example.demo.mapper;import com.example.demo.entity.vo.StuInfoVO;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;@Mapperpublic interface StuInfoMapper {StuInfoVO getById(@Param("id") Integer id);}
在资源文件的mybatis中添加一个配置文件StuInfoMapper.xml
<" />="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace是命名空间,表示实现哪个接口--><mapper namespace="com.example.demo.mapper.StuInfoMapper"><select id="getById" resultType="com.example.demo.entity.vo.StuInfoVO">select id,name,classInfo,ident from stu,stuInfo where stu.id = stuInfo.stuIdand stu.id = #{id}</select></mapper>
insert into stuInfo values(13,'计算机2020班','学生会主席');
<?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"><!--namespace是命名空间,表示实现哪个接口--><mapper namespace="com.example.demo.mapper.StuInfoMapper"></mapper>
整体的目录结构.
接下来我们实现一个简单的联合查询
创建接口字段
StuInfoVO getById(@Param("id") Integer id);
xml文件配置
<select id="getById" resultType="com.example.demo.entity.vo.StuInfoVO">select id,name,classInfo,ident from stu,stuInfo where stu.id = stuInfo.stuIdand stu.id = #{id}</select>
测试单元
package com.example.demo.mapper;import com.example.demo.entity.vo.StuInfoVO;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTestclass StuInfoMapperTest {@Autowiredprivate StuInfoMapper stuInfoMapper;@Testvoid getById() {StuInfoVO stuInfoVO = stuInfoMapper.getById(13);System.out.println(stuInfoVO);}}
通过继承的方式后端获取不到信息.
那么,接下来我们不使用继承
package com.example.demo.entity.vo;import com.example.demo.entity.StuInfo;import lombok.Data;@Datapublic class StuInfoVO{//不想写基础字段,可以去继承private String username;private int stuId;private String classInfo;private String ident;}
这样后端就能成功获取到我们的sql字段.
在多表联查中我们可以使用数据库里面的语句.
- left join.
- inner join.