🥦 MyBatis xml 映射

吞佛童子2022年10月10日
  • frame
  • MyBatis
大约 7 分钟

🥦 MyBatis xml 映射

1. Mapper 接口 映射 xml

1) 过程

  1. 解析 xml 得到 MappedStatement 对象
    • MyBatis 在初始化 SqlSessionFactoryBean 时,会找到 mapperLocations 配置路径下的所有 xml 文件进行解析
    • 创建 SqlSource 对象
      • MyBatis 会将每个 sql 标签,例如 <select> <where> <if> 等,封装成一个 SqlSource 对象
    • 创建 MappedStatement 对象
      • MyBatis 会为每个 sql 标签,生成一个 MappedStatement 对象,添加到 Configuration 实例中
        • 基于 类的全限定名(对应 <mapper namespace = "...">) + 方法名(对应 <select id = "...">) & SqlSource 对象 区分不同 MappedStatement 对象
  2. Mapper 接口动态代理 得到 代理类,执行 invoke()
    • 基于 @MapperScan(value = "com.example.flow.mapper") 注解或 Spring 的 xml 配置文件
    • 将包路径下的所有类注册到 Spring bean 中,并将其 beanClass 设置为 MapperFactoryBean,MapperFactoryBean 实现了 FactoryBean 接口
    • 当我们通过 @Autowired 注解注入某个 Mapper 接口时,返回的就是 MapperFactoryBean.getObject() 对象
    • 这个对象通过 JDK 动态代理,返回了 Mapper 接口的代理对象 MapperProxy
    • 当我们调用 Mapper 接口中的方法时,返回的就是 代理对象 MapperProxyinvoke() 方法
  3. invoke() 调用 SqlSession 执行 sql 请求
    • invoke() 方法 实际调用的是 SqlSession 中的方法
    • 通过 类的全限定名 + 方法名,得到 1. 中生成的 MappedStatement 对象
    • 然后通过 Executor.query(MappedStatement ms) 等方法执行对应 sql 请求,然后返回结果
  4. 这里用到了 命令模式,最终通过 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

4. #{} VS ${}

1) #{}

  • 占位符
  • 使用 #{} 时,MyBatis 会创建 PreparedStatement 参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)
  • 可以有效防止 sql 注入
  • 原理:

2) ${}

  • 拼接符
  • MyBatis 就不会修改或转义该字符串
  • 可能会发生 sql 注入
    • 要么不允许用户输入这些字段,要么自行转义并检验这些参数
  • 当 SQL 语句中的元数据(如表名或列名)是动态生成的时候,字符串替换将会非常有用
  • 例:select 一个表任意一列的数据时

5. 模糊查询 like

  • concat('%', #{question}, '%')
  • 比字符串的直接拼接容易引起 SQL 注入更加安全
上次编辑于: 2022/10/10 下午8:43:48
贡献者: liuxianzhishou