创建数据库和表
-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;
-- 使用数据数据
use mycnblog;
-- 创建表[用户表]
drop table if exists userinfo;
create table userinfo(
id int primary key auto_increment,
username varchar(100) not null,
password varchar(32) not null,
photo varchar(500) default '',
createtime datetime default now(),
updatetime datetime default now(),
`state` int default 1
) default charset 'utf8mb4';
添加实体类
package com.example.demo.model;
import lombok.Data;
import java.util.Date;
/**
* 普通的实体类,用于 Mybatis 做数据库表(userinfo表)的映射
* 注意事项:保证类属性名称和userinfo表的字段完全一致!
*/
@Data
public class UserInfo {
private int id;
private String name;
private String password;
private String photo;
private Date createtime;
private Date updatetime;
private int state;
}
添加 mapper 接口 (数据持久层)
package com.example.demo.mapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
}
创建与接口对应的 xml 文件
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
mapper>
#{}:预编译处理。
预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使⽤ PreparedStatement 的 set ⽅法来赋值。
${}:字符直接替换。
直接替换:是MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。
预编译处理和字符串替换的区别故事(头等舱和经济舱乘机分离的故事):
乘坐飞机,头等舱和经济舱的区别是很⼤的。
username 唯一。
插入一条数据:
-- 添加一个用户信息
INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES
(1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);
UserMapper 接口:
// 根据用户姓名完全匹配查询
public UserInfo getUserByName(@Param("username") String username);
UserMapper.xml:
<select id="getUserByName" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username=#{username}
select>
单元测试:
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 当前测试的上下文环境为 springboot
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void getUserByName() {
UserInfo userInfo = userMapper.getUserByName("admin");
System.out.println("userinfo -> " + userInfo);
}
}
经测试,成功打印了我们插入的数据信息。
UserMapper.xml:
<select id="getUserByName" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username=${username}
select>
''
在其外加一层单引号:'${username}'
即可。
那么无论是 #{} 还是 ${},都加上
''
不就可以了吗?
理论上是可以的,但是会产生很多隐式类型转换!一旦存在隐式类型转换就不会走索引了,效率会大大降低!!!
例如排序时:order by xxx asc/desc
此时传递的虽然也是一个字符串,但是我们并不希望给它加上引号!
所以只能使用 ${} 字符直接替换!
UserMapper 接口:
// 查询所有的信息(根据排序条件进行排序)
public List<UserInfo> getAllByOrder(@Param("order") String order);
UserMapper.xml:
<select id="getAllByOrder" resultType="com.example.demo.model.UserInfo">
select * from userinfo order by id ${order}
select>
但因为 SQL 注入问题,这种场景使用 ${} 时必须在 controller 中验证一下参数 (字符串必须是 asc/desc,否则代码就不要继续往下执行了!)
<select id="isLogin" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username='${name}' and password='${pwd}'
select>
sql 注入代码:' or 1='1
…
${} 字符直接替换,这时就忽略了密码直接登录了!十分危险!!!
因此绝大部分场景都会避免使用 ${}。
<select id="findUserByName2" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username like '%#{username}%';
select>
使用 #{} 会报错!
预编译会识别为字符串类型自动加上引号,不符合预期
相当于是:select * from userinfo where username like ‘%‘username’%’;
此时也并不能直接使用 ${}:会有 SQL 注入风险。在 controller 进行检查判断也不可取,因为传来的字符串是有无数种可能的,无法检查判断!
这时可以使用 MySQL 的内置函数 concat() 来处理,实现代码如下:
<select id="findUserByName3" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username like concat('%',#{username},'%');
select>
这种写法完全没有问题 ~