(七)Mybatis传值中#{}和${}的区别,及别名机制
文章目录
- 环境
- #{}与${}的区别
- 使用#{}
- 使用${}
- 情况一:升序与降序
- 情况二:拼接表名
- 情况三:批量删除
- 情况四:模糊查询
- 别名机制
上一篇:(六)Mybatis中接口代理机制及使用
下一篇:(八)MyBatis中参数的处理
环境
数据库:汽车表t_car、t_log_20220901
Mybatis工具类
依赖
mybatis-config.xml、logback.xml
pojo类:Car、Log
除了t_log_20220901、Log,其他可以去复制之前的
t_log_20220901:
- id 主键(自增)[int]
- log 日志信息 [varchar]
- time 日志时间 [varchar]
Log类
package com.antis.pojo;
public class Log {
private Integer id;
private String log;
private String time;
public Log() {
}
public Log(Integer id, String log, String time) {
this.id = id;
this.log = log;
this.time = time;
}
@Override
public String toString() {
return "Log{" +
"id=" + id +
", log='" + log + '\'' +
", time='" + time + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLog() {
return log;
}
public void setLog(String log) {
this.log = log;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
#{}与${}的区别
在Mybatis的sql语句传值中,除了能够用#{}进行传值之外,还可以使用${}进行传值,它们的区别是:
#{}:先编译sql语句,再给占位符传值,底层是PreparedStatement实现。可以防止sql注入,比较常用。
${}:先进行sql语句拼接,然后再编译sql语句,底层是Statement实现。存在sql注入现象。只有在需要进行sql语句关键字拼接的情况下才会用到。
使用#{}
创建一个CarMapper接口
public interface CarMapper {
/**
* 通过汽车类型查询
* @param carType
* @return
*/
List<Car> selectByCarType(String carType);
}
创建CarMapper.xml映射文件放在类路径下,注意namespace是接口的全限定类名,id是接口的方法名
<?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.antis.mapper.CarMapper">
<select id="selectByCarType" resultType="com.antis.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>
</mapper>
测试程序
@Test
public void testSelectByCarType(){
SqlSession session = SqlSessionUtil.getSession();
CarMapper mapper = session.getMapper(CarMapper.class);
List<Car> cars = mapper.selectByCarType("燃油车");
cars.forEach(car -> {
System.out.println(car);
});
SqlSessionUtil.close(session);
}
执行之后可以清楚的看到,sql语句中是带有 ‘?’ 的,这个 ‘?’ 就是占位符,专门用来接收值的。会把‘燃油车’这个字符串类型传给 ‘?’。
这就是 #{},它会先进行sql语句的预编译,然后再给占位符传值
使用${}
还是上面的程序,我们在CarMapper映射文件里把#替换成$试试
<!--where car_type = #{carType}-->
where car_type = ${carType}
运行测试程序会出现错误,因为${}是先进行字符串拼接,再进行编译,出现语法错误是正常的,因为 ‘燃油车’ 是⼀个字符串,在sql语句中应该添加单引号
在CarMapper映射文件修改如下:
<!--where car_type = #{carType}-->
<!--where car_type = ${carType}-->
where car_type = '${carType}'
再次运行,我们发现它真的是把字符串拼接到sql语句里面
一般来说,不是特殊的需求,一般都用#{},而不会用${}
当需要进行sql语句关键字拼接的时候,必须使用${}
情况一:升序与降序
通过向sql语句中注⼊asc或desc关键字,来完成数据的升序或降序排列。
CarMapper接口中新增一个方法
/**
* 查询所有的信息,通过asc或desc进行升序和降序
* @param ascOrDesc
* @return
*/
List<Car> selectAllByAscOrDesc(String ascOrDesc);
先#{}进行测试
CarMapper.xml文件:
<select id="selectAllByAscOrDesc" resultType="com.antis.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
produce_time #{ascOrDesc}
</select>
测试程序
@Test
public void testSelectAllByAscOrDesc(){
SqlSession session = SqlSessionUtil.getSession();
CarMapper mapper = session.getMapper(CarMapper.class);
List<Car> cars = mapper.selectAllByAscOrDesc("asc");
cars.forEach(car -> {
System.out.println(car);
});
SqlSessionUtil.close(session);
}
运行发现出现sql语句异常,原因是sql语句不合法,因为采用这种方式传值,最终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 'asc'
asc是关键字,不应该是一个字符串,所以只能用KaTeX parse error: Expected 'EOF', got '#' at position 22: …rMapper.xml文件中把#̲该为
再次运行,成功排序
情况二:拼接表名
业务背景:实际开发中,有的表数据量非常庞大,可能会采用分表方式进行存储,比如每天生成一张表,表的名字与日期挂钩,例如: 2022年8月1日生成的表: t _user20220108. 2000年1 月1日生成的表: t _user20000101。此时前端在进行查询的时候会提交一个具体的日期, 比如前端提交的日期为:2000年1月1日,那么后端就会根据这个日期动态拼接表名为: t _user20000101。有了这个表名之后,将表名拼接到sq|语句当中,返回查询结果。那么大家思考-下,拼接表名到sq|语句当中应该使用#0}还是${}呢?
使用#{}会是这样:select * from ‘t_userXXX’
使用${}会是这样:select * from t_usrXXX
表名不应该是一个字符串,所以只能使用${}
创建一个LogMapper接口中添加一个方法:
public interface LogMapper {
/**
* 根据日期查询不同的表,获取表所有信息
* @param date
* @return
*/
List<Log> selectAllByTable(String date);
}
创建LogMapper.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">
<mapper namespace="com.antis.mapper.LogMapper">
<select id="selectAllByTable" resultType="com.antis.pojo.Log">
select *
from t_log_${date};
</select>
</mapper>
测试程序:
@Test
public void testSelectAllByTable(){
SqlSession session = SqlSessionUtil.getSession();
LogMapper mapper = session.getMapper(LogMapper.class);
List<Log> logs = mapper.selectAllByTable("20220901");
logs.forEach(log -> {
System.out.println(log);
});
}
运行
情况三:批量删除
业务背景:用户想要删除信息的使用,勾选多个信息,进行批量删除
批量删除的SQL语句有两种写法:
delete from t_car where id = 1 or id = 2 or id = 3;
delete from t_car where id in(1, 2, 3);
假设现在使用in的方式处理,前端传过来的字符串:1, 2, 3
使用#{} :
delete from t_user where id in('1,2,3')
会出现语法错误,因为in里面不是一个字符串类型
所以只能使用${}
在CarMapper接口中新增一个方法
/**
* 批量删除,根据id
* @param ids 传一个字符串id,用','隔开
* @return
*/
int deleteBatch(String ids);
在CarMapper.xml进行配置
<delete id="deleteBatch">
delete
from t_car
where id in(${ids});
</delete>
测试程序
@Test
public void testDeleteBatch(){
SqlSession session = SqlSessionUtil.getSession();
CarMapper mapper = session.getMapper(CarMapper.class);
int i = mapper.deleteBatch("16,17");
System.out.println(i);
session.commit();
SqlSessionUtil.close(session);
}
结果:
情况四:模糊查询
需求:通过汽车品牌模糊查询。
第一种方案:
‘%${brand}%’
第二种方案:concat函数,这个是mysql数据库当中的一个函数,专门进行字符串拼接
concat(‘%’,#{brand},‘%’)
第三种方案:比较鸡肋了。可以不算。
concat(‘%’,‘${brand}’,‘%’)
第四种方案:
“%”#{brand}“%”
别名机制
起别名标签:typeAlias
-
type:被指定的别名
-
alias:指定别名,可以省略,省略后别名默认是类的简称,例如com.antis.pojo.Car就是car(不区分大小写)
到时候在映射文件中的resultType就可以使用这个别名,别名不区分大小写
<typeAlias type="com.antis.pojo.Car" alias="iii" />
<typeAlias type="com.antis.pojo.Car" alias="iii" />
</typeAliases>
自动通过包起别名标签:package
自动把name这个包下的所有类自动起别名,是类的简称,不区分大小写。
<typeAlias type="com.antis.pojo.Car" alias="iii" />
<package name="com.antis.pojo"/>
</typeAliases>