${} 和 #{} 有啥区别 ?
通过上面结果可以看到使用 ${} 时, MySQL 执行如下 :
而使用 #{} 替代, 则是这样 :
${} 和 #{} 都是 MyBatis 中用来替换参数的.
${} 是直接替换, 而 #{} 是预执行.
#{} 规定了在 ? 里必须是一个参数, 不能是 SQL 命令或 SQL关键字, 而 ${} 里则可以是 SQL 命令或 SQL 语句.
这也就体现了 #{} 是安全的, ${} 是不安全的, 会有 SQL 注入问题.
使用 ${} 返回的参数, 一定得要能被穷举.
使用 ${} 会出现的问题
就拿用户登录来举例, 一般来说登录都是要通过用户名和密码的 :
@Mapper public interface UserMapper { //用户登录 Userinfo login(@Param("username")String username, @Param("password")String password); }
<?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.example.demo.dao.UserMapper"> <select id="login" resultType="com.example.demo.model.Userinfo"> select * from userinfo where username=${username} and password=${password} </select> </mapper>
@SpringBootTest class UserMapperTest { @Test void login() { Userinfo userinfo = userMapper.login("admin","admin"); System.out.println(userinfo.toString()); } }
可以看到这个 SQL 语句明显有问题, 正常写法得加上单引号 ‘admin’, 使用 #{} 则没有这个问题.
这个问题也可以解决, 通过手动加单引号就行 :
<select id="login" resultType="com.example.demo.model.Userinfo"> select * from userinfo where username='${username}' and password='${password}' </select>
这样明显更繁琐了, 而且不安全, 会有 SQL 注入风险 :
@Test void login() { Userinfo userinfo = userMapper.login("admin","' or 1='1"); System.out.println(userinfo.toString()); }
这里通过特殊语句, 来达到非法登录的目的 :
可以看到, 我明明没有输入正确密码, 却可以获取到用户信息, 这就是 SQL注入.
like 查询
List<Userinfo> getLikeList(@Param("username") String username);
用 数据库函数 concat 进行拼接 :
<select id="getLikeList" resultType="com.example.demo.model.Userinfo"> select * from userinfo where username like concat('%',#{username},'%') </select>
@Test void getLikeList() { System.out.println(userMapper.getLikeList("管")); }
删除操作
删除、修改、添加操作默认返回的是受影响的行数
@Mapper public interface UserMapper { //删除操作 int delById(@Param("id") Integer id); }
<!--这里返回类型可以省略--> <delete id="delById"> delete from userinfo where id=#{id} </delete>
修改操作
@Mapper public interface UserMapper { //修改操作 int update(Userinfo userinfo); }
<update id="update"> update userinfo set username=#{username} where id=#{id} </update>
@Test void update() { Userinfo userinfo = new Userinfo(); userinfo.setId(1); userinfo.setUsername("管理员"); int result = userMapper.update(userinfo); System.out.println("受影响的行数 : " + result); }
添加操作
- 拿到受影响的行数
//添加操作 int add(Userinfo userinfo);
<insert id="add"> insert into userinfo(username,password,photo) value(#{username},#{password},#{photo}) </insert>
@Test void add() { Userinfo userinfo = new Userinfo(); userinfo.setUsername("王三"); userinfo.setPassword("123456"); userinfo.setPhoto("/image/ddd.png"); int result = userMapper.add(userinfo); System.out.println("受影响的行数 : " + result); }
- 拿到自增 id :
int insert(Userinfo userinfo);
<insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id"> insert into userinfo(username,password,photo) value(#{username},#{password},#{photo}) </insert>
keyColum : 设置数据库自增主键字段名
keyProperty : 设置数据库自增 id 赋值的属性
@Test void insert() { Userinfo userinfo = new Userinfo(); userinfo.setUsername("二哈"); userinfo.setPassword("123456456"); int result = userMapper.insert(userinfo); System.out.println("受影响的行数 : " + result + ", ID : " + userinfo.getId()); }