🥦 MyBatis xml 映射
2022年10月10日
- frame
🥦 MyBatis xml 映射
1. Mapper 接口 映射 xml
1) 过程
- 解析 xml 得到 MappedStatement 对象
- MyBatis 在初始化
SqlSessionFactoryBean
时,会找到 mapperLocations 配置路径下的所有 xml 文件进行解析 - 创建
SqlSource
对象- MyBatis 会将每个 sql 标签,例如
<select>
<where>
<if>
等,封装成一个 SqlSource 对象
- MyBatis 会将每个 sql 标签,例如
- 创建
MappedStatement
对象- MyBatis 会为每个 sql 标签,生成一个 MappedStatement 对象,添加到 Configuration 实例中
- 基于 类的全限定名(对应
<mapper namespace = "...">
) + 方法名(对应<select id = "...">
) & SqlSource 对象 区分不同MappedStatement
对象
- 基于 类的全限定名(对应
- MyBatis 会为每个 sql 标签,生成一个 MappedStatement 对象,添加到 Configuration 实例中
- MyBatis 在初始化
- Mapper 接口动态代理 得到 代理类,执行 invoke()
- 基于
@MapperScan(value = "com.example.flow.mapper")
注解或 Spring 的 xml 配置文件 - 将包路径下的所有类注册到 Spring bean 中,并将其 beanClass 设置为
MapperFactoryBean
,MapperFactoryBean 实现了FactoryBean
接口 - 当我们通过
@Autowired
注解注入某个 Mapper 接口时,返回的就是MapperFactoryBean.getObject()
对象 - 这个对象通过 JDK 动态代理,返回了 Mapper 接口的代理对象
MapperProxy
- 当我们调用 Mapper 接口中的方法时,返回的就是 代理对象
MapperProxy
的invoke()
方法
- 基于
- invoke() 调用 SqlSession 执行 sql 请求
invoke()
方法 实际调用的是SqlSession
中的方法- 通过 类的全限定名 + 方法名,得到 1. 中生成的
MappedStatement
对象 - 然后通过
Executor.query(MappedStatement ms)
等方法执行对应 sql 请求,然后返回结果
- 这里用到了 命令模式,最终通过 SqlSession 实例去运行 对象的 sql
2) Mapper 接口方法是否可以重载
- MyBatis 通过 类的全限定名 + 方法名 确定具体对应某个方法,然后生成 key 作为
MappedStatement
对象的 key 值,存储在 Configuration 实例中 - 方法重载, 说明 类的全限定名 + 方法名 相同,而 xml 文件中 类的全限定名 + 方法名 只能有唯一一个,即 key 唯一
- 因此要想实现重载,必须保证,多个重载的方法能够通过一条
<select>...</select>
等标签实现 - 举例:
2. xml 文件映射
1) select
2) insert & update & delete
3. 动态 SQL
- 从 sql 参数对象中计算表达式的值,根据表达式的值,动态拼接 SQL
1) if
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<!-- 若 查询结果 title != null,那么还会拼接 title like #{title} 条件 -->
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
2) where & set
3) choose & when & otherwise
<!--有点像 Java 中的 switch 语句-->
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
4) foreach
- 当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。
- 当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
- 当传入参数为 单参数 & 参数类型为 List 时,
collection="list"
- 当传入参数为 单参数 & 参数类型为 Array 时,
collection="array"
- 当传入参数为 多参数时,需要封装为 map,
collection="map 中 该 List 对应的 key 名称"
- 批量插入的行数在 20-50 条之间比较合适,超过 1000 条效率大大降低
- 此时若想解决,
- 每次控制 foreach 中集合数量
- 使用 BatchInsert
#{}
VS ${}
4. #{}
1) - 占位符
- 使用
#{}
时,MyBatis 会创建PreparedStatement
参数占位符,并通过占位符安全地设置参数(就像使用?
一样) - 可以有效防止 sql 注入
- 原理:
${}
2) - 拼接符
- MyBatis 就不会修改或转义该字符串
- 可能会发生 sql 注入
- 要么不允许用户输入这些字段,要么自行转义并检验这些参数
- 当 SQL 语句中的元数据(如表名或列名)是动态生成的时候,字符串替换将会非常有用
- 例:
select
一个表任意一列的数据时
5. 模糊查询 like
concat('%', #{question}, '%')
- 比字符串的直接拼接容易引起 SQL 注入更加安全