mybatis 教程

331 阅读4分钟

mybatis 基础知识

动态标签

  • <select> , <insert> , <update> , <delete>

  • sql 标签

复用sql语句

<sql id="all_columns">
        country.Code as country_code,
        city.Population as city_population
    </sql>
<select id="selectCountryAndCity" parameterType="map" resultMap="countryAndCity1">
        SELECT <include refid="all_columns" />
        From country LEFT JOIN city ON country.code=city.countrycode WHERE country.code=#{code} AND city.name=#{name}
    </select>
  • where 标签

反例:

<select id="findActiveBlogLike" resultType="Blog">
  SELECT * FROM BLOG
  WHERE
  <if test="state != null">
    state = #{state}
  </if>
  <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>

正例:

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>
  • set 标签 用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列
<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>
  • trim 标签
<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE
  <if test="state != null">
    state = #{state}
  </if>
  <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>

如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:

SELECT * FROM BLOG
WHERE

这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:

SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>
  • foreach 标签 动态 SQL 的另一个常见使用场景是对集合进行遍历尤其是在构建 IN 条件语句的时候
<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list" open="(" separator="," close=")">
        #{item}
  </foreach>
</select>
  • bind 标签 元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如:
<select id="selectBlogsLike" resultType="Blog">
  <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>
  • choose when otherwish 标签

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 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>

mybatis 的缓存机制

  • 一级缓存

每个SqlSession中持有了Executor,每个Executor中有一个LocalCache。当用户发起查询时,MyBatis根据当前执行的语句生成MappedStatement,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache,最后返回结果给用户。

MyBatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺 MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement。

  • 二级缓存

MyBatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。

MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。

SQL注入

  • 直接拼接的方式,不对数值做预编译mybatis在为{} 直接拼接的方式,不对数值做预编译mybatis在为{}设置值时,不加引号存在sql注入的现象 编译之后的效果
SELECT * FROM BLOG WHERE state = ?

SELECT * FROM BLOG WHERE state = 'ONELINE'
  • #{} 是预编译的方式,相当于jdbc的占位符PrepareStatement,一个#{}就是一个占位符 mybatis在为#{}设置值时,会加引号。 拼接:
SELECT * FROM BLOG WHERE state = ONELINE

SQL 注入 如果传入的变量是 ONELINE OR 1 = 1

SELECT * FROM BLOG WHERE state = ONELINE OR 1 = 1

查询排序的处理 需要列名的地方,在原生jdbc开发中,不可以预编译 因为预编译会给值加上引号,需要列名的是直接进行拼接的

批量更新&新增和循环新增更新的区别

  • 循环遍历

  • 动态sql

<insert id="InsertOrUpdate_Info" parameterType="MyInfo">
    insert into movieinfo
    (id,title,type)
    values
    (#{id},#{title},#{type})
    ON DUPLICATE KEY UPDATE
    title = #{title},type = #{type}
</insert>
  • 批处理 可以在一次批处理中处理不同语义的SQL语句
    //Statement实现批处理操作
    public static void main(String[] args) {
		//try-with-resource
        try (Connection conn = JDBCUtils.getConnection();
             Statement stat = conn.createStatement()) {
            //添加进批
            stat.addBatch("create table t1(id int,name varchar(20))");
            stat.addBatch("insert into t1 values(1,'a')");
            stat.addBatch("insert into t1 values(2,'b')");
            stat.addBatch("insert into t1 values(3,'c')");
            //执行批
            int[] counts = stat.executeBatch();
            //long[] longs = stat.executeLargeBatch();
            System.out.println(Arrays.toString(counts));
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

将多条SQL添加到一个批中,一次性将批发送给数据库,数据库依次执行SQL语句,减少SQL语句发送的次数,提升程序运行的效率

快速开发工具

  • mybatis plus
  • tk mybatis