MyBatis中 “#{}“和”${}“的区别

目录

1.介绍

2.区别

3.总结:


1.介绍

  • MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
  • 动态 sql 是 mybatis 的主要特性之一,在mybatis中我们可以把参数传到xml文件,由mybatis对sql及其语法进行解析,mybatis支持使用{}和#{}的作用一样,其实他们的功能相似,但还是有区别的。

2.区别

(1). 使用${}方式传入的参数,mybatis不会对它进行特殊处理,而使用#{}传进来的参数,mybatis默认会将其当成字符串。可能在赋值给如id=#{id}和id=${id}看不出多大区别,但是作为表名或字段参数时可以明显看出,可以看看下面的例子

 "#{}"是将传入的值按照字符串的形式进行处

     SELECT
             *
        FROM
            `t_information`
        where
             project_id=#{projectId}
             AND is_delete=0
        order by sets

MyBaits会首先对其进行预编译,将#{user_ids}替换成?占位符,然后在执行时替换成实际传入的user_id值,**并在两边加上单引号,以字符串方式处理。下面是MyBatis执行日志:

9:30:20.247 [main] DEBUG william.mybatis.quickstart.mapper.UserMapper.selectInformation- ==>  Preparing:SELECT *FROM `t_information` where project_id=? AND is_delete=0
order by sets
9:30:20.285 [main] DEBUG william.mybatis.quickstart.mapper.UserMapper.selectInformation- ==> Parameters: 1(Long)

"${}"是做简单的字符串替换,即将传入的值直接拼接到SQL语句中,且不会自动加单引号。将上面的SQL语句改为:

     SELECT
             *
        FROM
            `t_information`
        where
             project_id=${projectId}
             AND is_delete=0
        order by sets

再观察MyBatis的执行日志:

9:30:20.247 [main] DEBUG william.mybatis.quickstart.mapper.UserMapper.selectInformation- ==>  Preparing:SELECT *FROM `t_information` where project_id= 1 AND is_delete=0
order by sets
9:30:20.285 [main] DEBUG william.mybatis.quickstart.mapper.UserMapper.selectInformation- ==> Parameters: 

可以看到,参数是直接替换的,且没有单引号处理,这样就有SQL注入的风险。

sql注入

什么?还不懂SQL注入,我湖了QAQ。。。那就来个最简单的例子:一般开发,肯定是在前台有两个输入框,一个用户名,一个密码,会在后台里,读取前台传入的这两个参数,拼成一段SQL,

例如:

select count(1) from tab where usesr=userinput and pass = passinput

把这段SQL连接数据后,看这个用户名/密码是否存在,如果存在的话,就可以登陆成功了,如果不存在,就报一个登陆失败的错误。对吧。 但是有这样的情况,这段SQL是根据用户输入拼出来,如果用户故意输入可以让后台解析失败的字符串,这就是SQL注入,例如,用户在输入密码的时候,输入 '''' ' or 1=1'', 这样,后台的程序在解析的时候,拼成的SQL语句,可能是这样的: select count(1) from tab where user=userinput and pass='' or 1=1; 看这条语句,可以知道,在解析之后,用户没有输入密码,加了一个恒等的条件 1=1,这样,这段SQL执行的时候,返回的 count值肯定大于1的,如果程序的逻辑没加过多的判断,这样就能够使用用户名 userinput登陆,而不需要密码。 防止SQL注入,首先要对密码输入中的单引号进行过滤,再在后面加其它的逻辑判断,或者不用这样的动态SQL拼

  所以 预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。我们知道,SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。而预编译机制则可以很好的防止SQL注入。预编译完成之后,SQL的结构已经固定,即便用户输入非法参数,也不会对SQL的结构产生影响,从而避免了潜在的安全风险。

  • #{}解析前:selec * from #{table};
    #{}解析后:select * from "test";

  • ${}解析前 :select * from ${table};
    ${}解析后 :select * from test;

很明显,前者多了字符串的引号,会失败,后者正常查询会成功;
所以对于传入分组(order)字段或者排序字段(order),应使用${},避免出现order by "id" 等情况

(2). #$在预编译处理中是不一样的。#类似jdbc中的PreparedStatement,对于传入的参数,在预处理阶段会使用?代替,看看下面的例子:

1).'#{}'待真正查询的时候即在数据库管理系统中(DBMS)才会代入参数
select * from student where id = ?;   

2).而'${}'则是简单的替换
select * from student where id = 2;

3.总结:


1 . 能使用#{}的地方应尽量使用#{}

2.像PreparedStatement ,#{}可以有效防止sql注入,${}则可能导致sql注入成功。所谓sql注入,就是指把用户输入的数据拼接到sql语句后面作为sql语句的一部分执行。例子:

  • 用户输入用户名admin和密码123456' or 'abc' = 'abc',那么拼接出来的语句就为

select * from user where name=' admin ' and password='123456' or 'abc'= 'abc';

3.防止sql注入的常见的几种方式:

  • jdbc使用 PreparedStatement代替Statement, PreparedStatement 不仅提高了代码的可读性和可维护性.而且也提高了安全性,有效防止sql注入;
  • 在程序代码中使用正则表达式过滤参数。使用正则表达式过滤可能造成注入的符号,如' --等;
  • 在页面输入参数时也进行字符串检测和提交时进行参数检查,同样可以使用正则表达式,不允许特殊符号出现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Upaaui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值