文章目录
- 1. 参数占位符 #{} 和 ${} 的区别
- 2. ${} 的优点
- 3. SQL 注入问题
- 4. like 查询
- 5. 返回字典映射:resultMap
- 6. 一对一查询:association
- 7. 一对多查询:collection
回顾一下,在上一篇 MyBatis 之二(增、删、改操作)中,学习了针对 MyBatis 的增、删、改操作三步走
- 在 mapper(interface)里面添加增删改方法的声明
- 在 XMl 中添加 增删改标签和对应的 sql 代码
- 在 **Mapper 中右键 Generate 点击 Test 生成测试类
本篇将学习用 MyBatis 进行数据库的查询相关操作
1. 参数占位符 #{} 和 ${} 的区别
使用 #{} 得到 JDBC 的代码【针对 Integer 类型】
使用 ${} 得到 JDBC 的代码【针对 Integer 类型】
使用 #{} 得到 JDBC 的代码【针对 String 类型】
使用 ${} 得到 JDBC 的代码【针对 String 类型】出错了
#{} 和 ${} 区别:
- 定义不同:#{} 预处理;而 ${} 是直接替换
- 使用不同:#{} 适用于所有类型的参数匹配;但 ${} 只使用于数值类型
- 安全性不同:#{} 性能高,并且没有安全性问题;但 ${} 存在 SQL 注入的安全问题
代码如下
@Mapper
public interface UserMapper {
// 根据用户 id 查询用户
public Userinfo getUserById(@Param("id") Integer id);
// 根据全面查询用户对象(非模糊查询)
public Userinfo getUserByFullName(@Param("username") String username);
}
<?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">
<!-- namespace 是设置要实现的接口的具体包名 + 类名-->
<mapper namespace="com.example.mybatisdome1.mapper.UserMapper">
<!-- 根据 id 查询用户 -->
<select id="getUserById" resultType="com.example.mybatisdome1.model.Userinfo">
select * from userinfo where id=${id}
</select>
<!-- 根据全面查询用户对象(非模糊查询) -->
<select id="getUserByFullName" resultType="com.example.mybatisdome1.model.Userinfo">
select * from userinfo where username=${username}
</select>
</mapper>
@SpringBootTest
@Slf4j
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void getUserById() {
Userinfo userinfo = userMapper.getUserById(1);
log.info("用户信息:" + userinfo);
}
@Test
void getUserByFullName() {
Userinfo userinfo = userMapper.getUserByFullName("张三");
log.info("用户信息:" + userinfo);
}
}
2. ${} 的优点
使用 ${} 可以实现排序查询
当传递的是一个 SQL 关键字 (SQL 命令 ,例如 desc)的时候,只能使用 ${} ,此时如果使用 #{} 就会认为传递的为一个普通的值,会给这个值加上单引号,这样就识别不出来 SQL 命令了,所以执行就会报错
使用 ${} 注意:因为使用 ${} 会存在 SQL 注入的问题,所以当不得不使用 ${} 时,那么一定要在业务代码中,对传递的值进行安全校验(当代码走到 Mapper 这一层时就必须要执行了,所以一定要在业务代码中,也就是 Controller 这一层对传递过来的 order 进行安全效验,不但要判断是否为 null,还要判断是不是 desc 或 asc,如果都不是代码就不要往下执行)
3. SQL 注入问题
当账号密码正确时,可以查询到用户信息
如果是 SQL 注入时
4. like 查询
使用 #{} 报错,但使用 ${} 在业务层的值又不能穷举 (如果使用 ${} 必须列出所有可能,比如前面的 order 只有 desc 或 asc 两种可能所以可以使用 ${},但这里如果是 username 不可能列举所有可能,所以不能使用 ${})
所以就可以使用 concat() 进行拼接
5. 返回字典映射:resultMap
前面在xml 的 标签中写返回类型 resultType 时,直接就是定义到某个实体类就行,但这种情况只适用于字段名称和程序中属性名相同的情况下,这种就是写起来方便
但如果是字段名称和属性名不同时,继续使用 resultType 就会报错,此时就要使用 resultMap 来配置映射
在一对一、一对多关系中可以使用 resultMap 映射并查询数
6. 一对一查询:association
进行一对一查询,需要使用 association 标签,表示一对一的结果映射,其中
- property 属性:指定其中所要一对一的那个属性
- resultMap 属性:指定那个属性所关联的结果集映射
- columnPrefix 属性:用来解决多表中相同字段数据覆盖的问题
一篇文章对应一个作者
(1)在 model 文件夹中创建 ArticleInfo 文章的实体类(一对一查询,注意加上属性 userInfo 作者)
@Data
public class ArticleInfo {
private int id;
private String title;
private String content;
private String createtime;
private String updatetime;
private int uid;
private int rcount;
private int state;
private UserInfo userInfo;
}
(2)在 Mapper 文件中创建 ArticleMapper 根据文章 id 获取文章
@Mapper
public interface ArticleMapper {
// 根据文章 id 获取文章
public ArticleInfo getArticleById(@Param("id") Integer id);
}
(3)在resources/mybatis 中写 ArticleMapper.xml 实现 (2)中的接口
<?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">
<!-- namespace 是设置要实现的接口的具体包名 + 类名-->
<mapper namespace="com.example.mybatisdome1.mapper.ArticleMapper">
<resultMap id="BaseMap" type="com.example.mybatisdome1.model.ArticleInfo">
<id column="id" property="id"></id>
<result column="title" property="title"></result>
<result column="content" property="content"></result>
<result column="createtime" property="createtime"></result>
<result column="updatetime" property="updatetime"></result>
<result column="uid" property="uid"></result>
<result column="rcount" property="rcount"></result>
<result column="state" property="state"></result>
<association property="userInfo"
resultMap="com.example.mybatisdome1.mapper.UserMapper.BaseMap"
columnPrefix="u_"></association>
</resultMap>
<select id="getArticleById" resultMap="BaseMap">
select a.*,u.id u_id,u.username u_username,u.password u_password from articleinfo a left join userinfo u on a.uid = u.id where a.id = #{id}
</select>
</mapper>
(4)生成测试类
@SpringBootTest
@Slf4j
class ArticleMapperTest {
@Resource
private ArticleMapper articleMapper;
@Test
void getArticleById() {
ArticleInfo articleInfo = articleMapper.getArticleById(1);
log.info("文章详情:" + articleInfo);
}
}
运行程序发现 userInfo 中属性没有查询到,这是因为前面写的 UserMapper.xml 中 写的 resultMap 有问题,要把所有属性都映射上
(5)修改 UserMapper.xml 中的 resultMap
(6)运行程序
7. 一对多查询:collection
和一对一 标签类似,一对多也需要标签 ,来表示一对多的结果映射
其中也是需要设置 property(对象中的属性名)、resultMap(映射对象对应的字典)、columnPrefix(一般不要省略,解决了多张表中相同字段查询数据覆盖的问题)
一个用户多篇文章
(1)在实体类 UserInfo 中添加多篇文章的属性
@Data
public class UserInfo {
private Integer id;
private String name;
private String password;
private String photo;
private String createtime;
private String updatetime;
private int state;
private List<ArticleInfo> artlist;
}
(2)在 UserMapper 接口中写出查询用户及用户发表的所有文章的方法
@Mapper
public interface UserMapper {
// 查询用户及用户发表的所有文章
public UserInfo getUserAndArticleByUid(@Param("uid") Integer uid);
}
(3)在 UserMapper.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">
<!-- namespace 是设置要实现的接口的具体包名 + 类名-->
<mapper namespace="com.example.mybatisdome1.mapper.UserMapper">
<resultMap id="BaseMap" type="com.example.mybatisdome1.model.UserInfo">
<!-- 主键映射 -->
<id column="id" property="id"></id>
<!-- 普通属性映射 -->
<result column="username" property="name"></result>
<result column="password" property="password"></result>
<result column="photo" property="photo"></result>
<result column="createtime" property="createtime"></result>
<result column="updatetime" property="updatetime"></result>
<result column="state" property="state"></result>
<collection property="artlist"
resultMap="com.example.mybatisdome1.mapper.ArticleMapper.BaseMap"
columnPrefix="a_">
</collection>
</resultMap>
<!-- 查询用户及用户发表的所有文章 -->
<select id="getUserAndArticleByUid" resultMap="BaseMap">
select u.*,a.id a_id,a.title a_title,a.content a_content,a.createtime a_createtime,a.updatetime a_updatetime
from userinfo u left join articleinfo a on u.id = a.uid where u.id = #{uid}
</select>
</mapper>
(4)生成测试方法
运行程序