MyBatis 之三(查询操作 占位符#{} 与 ${}、like查询、resultMap、association、collection)

回顾一下,在上一篇 MyBatis 之二(增、删、改操作)中,学习了针对 MyBatis 的增、删、改操作三步走

  1. 在 mapper(interface)里面添加增删改方法的声明
  2. 在 XMl 中添加 增删改标签和对应的 sql 代码
  3. 在 **Mapper 中右键 Generate 点击 Test 生成测试类

本篇将学习用 MyBatis 进行数据库的查询相关操作

1. 参数占位符 #{} 和 ${} 的区别

使用 #{} 得到 JDBC 的代码【针对 Integer 类型】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wQVBHzxs-1676203852995)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675756664478.png)]

使用 ${} 得到 JDBC 的代码【针对 Integer 类型】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UMvgcCR5-1676203852995)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675756687871.png)]

使用 #{} 得到 JDBC 的代码【针对 String 类型】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lRDfahIv-1676203852996)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675757137901.png)]

使用 ${} 得到 JDBC 的代码【针对 String 类型】出错了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2GnK7MTE-1676203852996)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675757384910.png)]

#{} 和 ${} 区别:

  1. 定义不同:#{} 预处理;而 ${} 是直接替换
  2. 使用不同:#{} 适用于所有类型的参数匹配;但 ${} 只使用于数值类型
  3. 安全性不同:#{} 性能高,并且没有安全性问题;但 ${} 存在 SQL 注入的安全问题

代码如下

@Mapper
public interface UserMapper {
    
    

    // 根据用户 id 查询用户
    public Userinfo getUserById(@Param("id") Integer id);

    // 根据全面查询用户对象(非模糊查询)
    public Userinfo getUserByFullName(@Param("username") String username);

}
<?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.mybatisdome1.mapper.UserMapper">

    <!-- 根据 id 查询用户 -->
    <select id="getUserById" resultType="com.example.mybatisdome1.model.Userinfo">
        select * from userinfo where id=${id}
    </select>

    <!-- 根据全面查询用户对象(非模糊查询) -->
    <select id="getUserByFullName" resultType="com.example.mybatisdome1.model.Userinfo">
        select * from userinfo where username=${username}
    </select>

</mapper>
@SpringBootTest
@Slf4j
class UserMapperTest {
    
    

    @Autowired
    private UserMapper userMapper;

    @Test
    void getUserById() {
    
    
        Userinfo userinfo = userMapper.getUserById(1);
        log.info("用户信息:" + userinfo);
    }

    @Test
    void getUserByFullName() {
    
    
        Userinfo userinfo = userMapper.getUserByFullName("张三");
        log.info("用户信息:" + userinfo);
    }
}

2. ${} 的优点

使用 ${} 可以实现排序查询

当传递的是一个 SQL 关键字 (SQL 命令 ,例如 desc)的时候,只能使用 ${} ,此时如果使用 #{} 就会认为传递的为一个普通的值,会给这个值加上单引号,这样就识别不出来 SQL 命令了,所以执行就会报错

使用 ${} 注意:因为使用 ${} 会存在 SQL 注入的问题,所以当不得不使用 ${} 时,那么一定要在业务代码中,对传递的值进行安全校验(当代码走到 Mapper 这一层时就必须要执行了,所以一定要在业务代码中,也就是 Controller 这一层对传递过来的 order 进行安全效验,不但要判断是否为 null,还要判断是不是 desc 或 asc,如果都不是代码就不要往下执行)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hwRv8g97-1676203852997)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675758827267.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X6a5i5tB-1676203852997)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675758847571.png)]


3. SQL 注入问题

当账号密码正确时,可以查询到用户信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hMgcTs2l-1676203852998)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675760364205.png)]

如果是 SQL 注入时

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bx7weRAc-1676203852998)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675761132558.png)]


4. like 查询

使用 #{} 报错,但使用 ${} 在业务层的值又不能穷举 (如果使用 ${} 必须列出所有可能,比如前面的 order 只有 desc 或 asc 两种可能所以可以使用 ${},但这里如果是 username 不可能列举所有可能,所以不能使用 ${})

所以就可以使用 concat() 进行拼接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bbqo7JtH-1676203853001)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675762375261.png)]


5. 返回字典映射:resultMap

前面在xml 的 标签中写返回类型 resultType 时,直接就是定义到某个实体类就行,但这种情况只适用于字段名称和程序中属性名相同的情况下,这种就是写起来方便

但如果是字段名称和属性名不同时,继续使用 resultType 就会报错,此时就要使用 resultMap 来配置映射

在一对一、一对多关系中可以使用 resultMap 映射并查询数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZEtWN7Ms-1676203853002)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675763776043.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5m9OJ4Ts-1676203853002)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675763677139.png)]


6. 一对一查询:association

进行一对一查询,需要使用 association 标签,表示一对一的结果映射,其中

  • property 属性:指定其中所要一对一的那个属性
  • resultMap 属性:指定那个属性所关联的结果集映射
  • columnPrefix 属性:用来解决多表中相同字段数据覆盖的问题

一篇文章对应一个作者

(1)在 model 文件夹中创建 ArticleInfo 文章的实体类(一对一查询,注意加上属性 userInfo 作者)

@Data
public class ArticleInfo {
    
    
    private int id;
    private String title;
    private String content;
    private String createtime;
    private String updatetime;
    private int uid;
    private int rcount;
    private int state;
    private UserInfo userInfo;
}

(2)在 Mapper 文件中创建 ArticleMapper 根据文章 id 获取文章

@Mapper
public interface ArticleMapper {
    
    

    // 根据文章 id 获取文章
    public ArticleInfo getArticleById(@Param("id") Integer id);

}

(3)在resources/mybatis 中写 ArticleMapper.xml 实现 (2)中的接口

<?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.mybatisdome1.mapper.ArticleMapper">

    <resultMap id="BaseMap" type="com.example.mybatisdome1.model.ArticleInfo">
        <id column="id" property="id"></id>
        <result column="title" property="title"></result>
        <result column="content" property="content"></result>
        <result column="createtime" property="createtime"></result>
        <result column="updatetime" property="updatetime"></result>
        <result column="uid" property="uid"></result>
        <result column="rcount" property="rcount"></result>
        <result column="state" property="state"></result>
        <association property="userInfo"
                     resultMap="com.example.mybatisdome1.mapper.UserMapper.BaseMap"
                     columnPrefix="u_"></association>
    </resultMap>

    <select id="getArticleById" resultMap="BaseMap">
        select a.*,u.id u_id,u.username u_username,u.password u_password from articleinfo a left join userinfo u on a.uid = u.id where a.id = #{id}
    </select>
</mapper>

(4)生成测试类

@SpringBootTest
@Slf4j
class ArticleMapperTest {
    
    

    @Resource
    private ArticleMapper articleMapper;

    @Test
    void getArticleById() {
    
    
        ArticleInfo articleInfo = articleMapper.getArticleById(1);
        log.info("文章详情:" + articleInfo);
    }
}

运行程序发现 userInfo 中属性没有查询到,这是因为前面写的 UserMapper.xml 中 写的 resultMap 有问题,要把所有属性都映射上

(5)修改 UserMapper.xml 中的 resultMap

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-giqJQCjE-1676203853002)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675819795845.png)]

(6)运行程序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C2VQ0khc-1676203853003)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1675822003612.png)]


7. 一对多查询:collection

和一对一 标签类似,一对多也需要标签 ,来表示一对多的结果映射

其中也是需要设置 property(对象中的属性名)、resultMap(映射对象对应的字典)、columnPrefix(一般不要省略,解决了多张表中相同字段查询数据覆盖的问题)

一个用户多篇文章

(1)在实体类 UserInfo 中添加多篇文章的属性

@Data
public class UserInfo {
    
    
    private Integer id;
    private String name;
    private String password;
    private String photo;
    private String createtime;
    private String updatetime;
    private int state;
    private List<ArticleInfo> artlist;
}

(2)在 UserMapper 接口中写出查询用户及用户发表的所有文章的方法

@Mapper
public interface UserMapper {
    
    

    // 查询用户及用户发表的所有文章
    public UserInfo getUserAndArticleByUid(@Param("uid") Integer uid);

}

(3)在 UserMapper.xml 中实现接口中的方法

<?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.mybatisdome1.mapper.UserMapper">

    <resultMap id="BaseMap" type="com.example.mybatisdome1.model.UserInfo">
        <!-- 主键映射 -->
        <id column="id" property="id"></id>
        <!-- 普通属性映射 -->
        <result column="username" property="name"></result>
        <result column="password" property="password"></result>
        <result column="photo" property="photo"></result>
        <result column="createtime" property="createtime"></result>
        <result column="updatetime" property="updatetime"></result>
        <result column="state" property="state"></result>
        <collection property="artlist"
                    resultMap="com.example.mybatisdome1.mapper.ArticleMapper.BaseMap"
                    columnPrefix="a_">
        </collection>
    </resultMap>

    <!-- 查询用户及用户发表的所有文章 -->
    <select id="getUserAndArticleByUid" resultMap="BaseMap">
        select u.*,a.id a_id,a.title a_title,a.content a_content,a.createtime a_createtime,a.updatetime a_updatetime
               from userinfo u left join articleinfo a on u.id = a.uid where u.id = #{uid}
    </select>

</mapper>

(4)生成测试方法

在这里插入图片描述

运行程序
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_58761900/article/details/128999100
今日推荐