实例讲解mybatis中${}与#{}的区别

本文主要介绍MyBatis中${}和#{}参数替换的区别。${}是简单替换,默认参数名是value,字符串类型需手动加单引号,适用于表名、排序字段动态指定场景;#{}是占位符替换,会自动处理类型。还提及了${}参数名指定错误的解决办法,以及对SQL注入风险的看法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    mybatis的面试或者实际中,我们经常会遇到这个问题,下面我们先来总结一下他们之间大致的区别,最后通过实例演示一下。

    ${}:这个其实就是简单的替换,有些傻瓜式,比如name="zhangsan",sql: select * from users where name=${name},最后的语句就是:

select * from users where name = zhangsan

    乍一看,没毛病啊,但是执行的时候,会报一个错误:

org.springframework.jdbc.BadSqlGrammarException: 
### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: Unknown column 'zhangsan' in 'where clause'
### The error may exist in file [D:\eclipse\workspaces\demo\target\classes\mapper\UserMapper.xml]
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: select * from xx_user where name = zhangsan
### Cause: java.sql.SQLSyntaxErrorException: Unknown column 'zhangsan' in 'where clause'

    其实,正常的语句应该是:   

select * from users where name = 'zhangsan'

    但是这种傻瓜式并不是一点好处也没有,后面会讲到它的特殊之处。默认,${}替换的时候,它的变量名是value,所以在接口声明的时候,如果是一个参数,默认指定为value,比如findById(Integer value),或者findByName(String value)就可以了,最后在sql中,我们需要根据参数数据类型,如果是字符串类型,需要在sql中加上单引号('')如下所示:

select * from users where name = '${value}'

    如果是非字符串类型,比如整形,长整形,双精度类型,那么几乎就是和#{}用法没有区别了,这里假定我们的id字段是整形:   

select * from users where id = ${value}

    默认情况下参数名为value:

    接口声明:User findByDefault(String value);

    xml配置如下:

<select id="findByDefault" resultType="com.xxx.demo.domain.User">
    select * from xx_user where name = '${value}'
</select>

    值得一提的是,${}这种替换因为默认参数名是value,如果我们不小心指定了别的,比如name,age,email等等,也不是不可以,如果我们在接口声明的地方不作说明,那么就会报错:

   There is no getter for property named 'name' in 'class java.lang.String'

    解决这个问题,其实很简单,有两种办法:

    一种是通过注解给参数设置@Param("name"),@Param("age"),@Param("email")就行了。

        1、保证sql正确被替换,字符串类型,需要加上单引号:select * from xx_user where name = '${name}'

        2、接口方法使用@Param("name")注解参数:User findByName(@Param("name")String name);

    还有一种办法,就是把参数封装成一个key-value的键值对,即map,这样,参数相当于有了名字,最后替换的时候,就不会出错了。

        1、同样要确保sql中参数如果是字符串,需要单引号:select * from xx_user where name = '${name}'

        2、参数没有名字,就给他封装为map。需要修改service方法和dao方法声明:

        UserMapper.java

User findByName(Map<String,Object> params);

        UserService.java

public User findByName(String name){
    Map<String,Object> params = new HashMap<>();
    params.put("name", name);
    return userMapper.findByName(params);
}

    前面提到${}替换非常傻瓜,但是这种傻瓜式却有很好的一个场景,比如我们的表名、或者是排序字段也是动态指定的呢,这种场景就非常适合这种傻瓜式的替换了。

    比如:table_name = 'users' , order_column = "name" ,sql: select * from ${table_name} order by ${order_column},最后执行的sql会如下:    

select * from users order by name

    这里,因为table_name,order_column属性都是字符串类型,如果使用#{},那么在执行参数替换之后,执行的sql如下:

select * from 'users' order by 'name'

    #{}:mybatis推荐用法,它是占位符替换,开始,会将#{}参数替换为"?",之后,会根据对应的类型做参数替换,字符串类型,会自动加上单引号('')。前面说了如果表名或者排序字段是动态的,也需要做替换,这时候,使用#{},反而就会出现问题,因为表名和字段是字符串类型,替换之后,会默认加上单引号,这个场景适合${}。

    至于有的说${}可能会导致sql注入的风险,使用#{}能够避免,个人理解sql注入在实际中并不是很常见,当我们的程序对参数检测做的足够严格,其实就很好规避这类问题,只能说有可能会导致,并不是说使用${}就一定会导致sql注入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

luffy5459

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

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

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

打赏作者

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

抵扣说明:

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

余额充值