MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)

目录

查询操作

1.单表查询

1.1 参数占位符#{}和${}

1.2 ${}的优点

1.3 sql注入问题

​编辑

面试常问:${}与#{}的区别

1.4 like查询

2.多表查询

2.1 返回字典映射:resultMap

2.2 多表查询

(1)建立 Articalinfo 实体类:

(2)在 ArticleMapper 接口中写出通过用户id查询用户的方法

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

(4)生成测试方法


查询操作

1.单表查询

下面我们来实现⼀下根据用户 id 查询用户信息的功能

UserController 实现代码如下:


    @RequestMapping("/getuserbyid")
    public Userinfo geUserById(Integer id){
        if (id==null)
            return null;
        return userService.getUserById(id);
    }

UserMapper 实现代码如下:

/**
     * 根据用户id查询用户信息
     * @param id
     * @return
     */
    Userinfo getUserById(@Param("id") Integer id);

UserMapper.xml 实现代码如下:

1.1 参数占位符#{}和${}

  • #{}:预编译处理
  • ${}:字符直接替换

预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使用 PreparedStatement 的 set 方法来赋值。

直接替换:是MyBatis 在预处理 ${} 时,就会把 ${} 替换成变量的值。

在正常的我们用id来查询用户时:

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第1张图片

都是没问题的,但是当我们用username来查询时:

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第2张图片可以看到在我们使用 # 符时是占位符,使用 $ 符则是直接替换了,int类型的自然没有问题,但是字符串类型的就有问题了

对于MySQL是要加单引号的,而$是啥都不加,所以是错误的!!

1.2 ${}的优点

有人会说,#{}这么好使的话为什么还会有${}的存在呢?

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第3张图片

在进行排序时(需要传递关键字时)需要使用到${},而 #{sort} 就不能实现排序查询了,因为使用 #{sort} 查询时, 如果传递的值为 String 则会加单引号,就会导致 sql 错误。 

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第4张图片

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第5张图片

当使用#{}的时候呢? 

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第6张图片

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第7张图片

使用$是啥都不管,直接使用原样去替换,#会加上'' 

$使用注意事项:一定是可以穷举的值,在使用之前一定要对传递的值进行合法性验证(安全性验证)。

1.3 sql注入问题

先看两则对比:

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第8张图片

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第9张图片 在这里${}完美体现了它自身的特性,但是当我们改变一下password:

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第10张图片

 这里就细思极恐了,当我们使用'${}'替换#{}的时候会这样,那不是只要我们知道别人的账号就可以登录别人的账户了吗!,我们来看看#{}会不会这样:

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第11张图片

我们可以看到 $ 符是存在SQL注入的问题,而 # 不存在

我们使用了一个奇怪的字符时为什么会成功呢?

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第12张图片

那 # 为什么不会这样呢?MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第13张图片

它使用的是JDBC的预处理的方式,它就把password当成字符串即values来处理,只是值的替换,而不是SQl语句,这就是机制的不同的区别 

面试常问:${}与#{}的区别

  1. $ 存在失去了注入问题,而 # 不存在
  2. $ 是直接替换,# 是预处理

1.4 like查询

在使用like查询时,使用#{}会报错,下面我们来看看是怎么回事:

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第14张图片

这是因为使用#{}会当作字符串进行替换,就变成下面这样了 

而使用 $ :

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第15张图片

这里我们可以看到使用 $ 符是可以的,但是我们说过使用 $ 符一定要能穷举,但是这里的 '李' 是不能够穷举的 ,前面说了使用${}有SQL注入的风险,所有这是不能直接使用 ${},可以考虑使用 mysql 的内置函数 concat() 来处理,实现代码如下: 

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第16张图片

所以在使用like查询时应该搭配concat()函数使用。

2.多表查询

2.1 返回字典映射:resultMap

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

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

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

使用场景:实现程序中属性和表中字段映射的功能(当程序中的属性和表中的字段不一致时,可以强行的映射到一起)

我们更改一下userinfo中的属性:

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第17张图片

 MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第18张图片

 MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第19张图片

 MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第20张图片

或者使用as关键字(数据库重命名)

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第21张图片 

 当程序中的属性与数据库中的字段不一致时解决方案:

  1. 使用 resultMap 标签(在 mapper.xml 中定义);
  2. 使用数据库别名 as 重命名。

2.2 多表查询

(1)建立 Articalinfo 实体类:

@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;
}

新增一个ArticleinfoVO实体类来帮助完成:

@Data
public class ArticleinfoVO extends Articleinfo implements Serializable {
    private String username;
}

(2)在 ArticleMapper 接口中写出通过用户id查询用户的方法

@Mapper
public interface ArticleMapper  {
    ArticleinfoVO getById(@Param("id") Integer id);
}

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





    

(4)生成测试方法

@SpringBootTest
class ArticleMapperTest {

    @Autowired
    private ArticleMapper articleMapper;


    @Test
    void getById() {
        ArticleinfoVO articleinfoVO = articleMapper.getById(1);
        System.out.println(articleinfoVO);
    }
}

我们发现:

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第22张图片

 明明上面已经有结果了,但是下面的打印只有扩展字段,基类的字段去哪了?

 我们来梳理一下多表查询所经历的历程:

  1. 查询
  2. 打印

我们通过打点来看看是哪一步出现了错误:

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第23张图片

我们可以得到:查询操作是没有问题的

所以问题出在了打印这里了

我们去查看字节码后发现

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第24张图片

我使用的是Lombok的toString,而它只给我打印了一个拓展字段

这时候我们不需要Lombok的打印了,我们重写toString方法

在toString的时候加上父类的toString

 MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第25张图片

 MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第26张图片

MyBatis查询数据库之三(#{}vs${},like查询,resultMap,as,多表查询)_第27张图片 当存在Lombok的toString方法和用户的toString方法时,打印选择的是用户的方法!!用户大于一切

最终的一个实现:联表查询语句(left join/inner join)+XXXVO 解决。

你可能感兴趣的:(mybatis,java,数据库,spring,spring,boot)