总结:
#{}:先编译sql语句,再给占位符传值,底层是PreparedStatement实现。可以防止sql注入,比较常用。
${}:先进行sql语句拼接,然后再编译sql语句,底层是Statement实现。存在sql注入现象。只有在需要进行sql语句关键字拼接的情况下才会用到。
#{}的使用场景:
#{}
的工作机制:
MyBatis 会将 #{}
中的参数替换为 SQL 预编译语句 的占位符(即 ?
),然后将参数通过 JDBC 预编译的方式传递给数据库。
Mapper 接口
public interface CarMapper {
List<Car> selectByCarType(String carType);
}
XML 配置
<select id="selectByCarType" resultType="com.powernode.mybatis.pojo.Car">
select
id, car_num as carNum, brand, guide_price as guidePrice,
produce_time as produceTime, car_type as carType
from
t_car
where
car_type = #{carType}
</select>
Service 层调用
List<Car> cars = carMapper.selectByCarType("SUV");
在这里,"SUV"
这个参数会通过 MyBatis 的 #{}
绑定机制,替换到 SQL 语句中的占位符 ?
处,然后执行查询。
MyBatis 转换后的 SQL:
在 MyBatis 运行过程中,这段 SQL 会被转换成如下形式:
select
id, car_num as carNum, brand, guide_price as guidePrice,
produce_time as produceTime, car_type as carType
from
t_car
where
car_type = ?
实际执行过程:
假设用户调用了如下 Java 代码:
List<Car> cars = carMapper.selectByCarType("SUV");
在实际执行时,JDBC 会生成如下的最终 SQL 语句,并将 "SUV"
参数绑定到 ?
占位符:
select
id, car_num as carNum, brand, guide_price as guidePrice,
produce_time as produceTime, car_type as carType
from
t_car
where
car_type = 'SUV';
${}:
${}
的使用和 #{}
不同,它是直接拼接参数到 SQL 语句中,而不是作为 JDBC 预编译的占位符。因此,${}
具有一定的风险(SQL 注入),但在某些场景下它仍然是有用的。
1. ${} 的工作原理
${}
会将参数的值直接拼接到 SQL 语句中,在 SQL 执行前完成字符串替换。- 它不经过预编译,因此无法避免 SQL 注入风险。
示例:使用 ${}
拼接参数
<select id="selectByCarType" resultType="com.powernode.mybatis.pojo.Car">
select
id, car_num as carNum, brand, guide_price as guidePrice,
produce_time as produceTime, car_type as carType
from
t_car
where
car_type = ${carType}
</select>
如果 Java 代码中传入参数为 "SUV"
:
carMapper.selectByCarType("SUV");
执行的 SQL 语句会被拼接成:
select
id, car_num as carNum, brand, guide_price as guidePrice,
produce_time as produceTime, car_type as carType
from
t_car
where
car_type = SUV;
这条 SQL 会出错,因为它缺少引号来包裹字符串类型的值。根据 SQL 规范,在 SQL 语句中,字符串类型的值(如 "SUV"
)必须用 单引号 '
包裹起来,否则会报语法错误。
如何解决报错?
加上单引号即可
<?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">
<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
<select id="selectByCarType" resultType="com.powernode.mybatis.pojo.Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from
t_car
where
<!--car_type = #{carType}-->
<!--car_type = ${carType}-->
car_type = '${carType}'
</select>
</mapper>
${} 的适用场景 :
需求:通过向sql语句中注入asc或desc关键字,来完成数据的升序或降序排列。
CarMapper接口:
List<Car> selectAll(String ascOrDesc);
CarMapper.xml文件:
<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from
t_car
order by carNum #{key}
</select>
测试程序
@Test
public void testSelectAll(){
CarMapper mapper = (CarMapper) SqlSessionUtil.openSession().getMapper(CarMapper.class);
List<Car> cars = mapper.selectAll("desc");
cars.forEach(car -> System.out.println(car));
}
采用这种方式传值,最终sql语句会是这样:
select
id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from t_car order by carNum 'desc'
desc是一个关键字,不能带单引号的,所以在进行sql语句关键字拼接的时候,必须使用${}!!
使用${} 改造 :
<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from
t_car
<!--order by carNum #{key}-->
order by carNum ${key}
</select>
批量删除
需求:一次删除多条记录。
对应的sql语句:
-
delete from t_user where id = 1 or id = 2 or id = 3;
-
delete from t_user where id in(1, 2, 3);
我们到底需要选择#{}还是${}呢?
正确选择应该是${},因为选择#{},sql语句会变成delete from t_user where id in('1,2,3') 执行错误:1292 - Truncated incorrect DOUBLE value: '1,2,3'
正确写法:
<delete id="deleteBatch">
delete from t_car where id in(${ids})
</delete>
对应的接口:
/**
* 根据id批量删除
* @param ids
* @return
*/
int deleteBatch(String ids);
模糊查询:
查询奔驰系列的汽车。【只要品牌brand中含有奔驰两个字的都查询出来。】
使用${}
/**
* 根据品牌进行模糊查询
* @param likeBrank
* @return
*/
List<Car> selectLikeByBrand(String likeBrank);
<select id="selectLikeByBrand" resultType="Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from
t_car
where
brand like '%${brand}%'
</select>
对应的sql语句:
select
id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from
t_car
where
brand like '%宝马%'
@Test
public void testSelectLikeByBrand(){
CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
List<Car> cars = mapper.selectLikeByBrand("奔驰");
cars.forEach(car -> System.out.println(car));
}
使用#{}
第一种:concat函数
<select id="selectLikeByBrand" resultType="Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from
t_car
where
brand like concat('%',#{brand},'%')
</select>
第二种:双引号方式(比较常用)
<select id="selectLikeByBrand" resultType="Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from
t_car
where
brand like "%"#{brand}"%"
</select>