[从零开始的账本项目02] SpringBoot 集成 MyBatis

264 阅读4分钟

上次写到数据库操作使用了MyBatis Plus, 但是考虑到之后的源码学习, 因此决定改用MyBatis

首先确定在依赖中引入mybatis

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>

进行mybatis的配置

mybatis:
  configuration:
    map-underscore-to-camel-case: true # 打开驼峰命名法
  mapper-locations: classpath:mapper/*.xml # 设置mapper文件位置

创建Dao接口类

@Mapper
public interface UserDao {

    Integer saveUser(@Param("user") User user);

    Integer deleteUserById(@Param("id") Long id);

    User getUserById(@Param("id") Long id);

    User login(@Param("account") String account, @Param("password") String password);

    Integer updateUserById(@Param("user") User user);
}

Mybatis Plus不同的就是Mybatis自身并不具备基本的SQL 需要我们自己进行添加.

<?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">
<mapper namespace="cn.bngel.bngelbookuserprovider8001.dao.UserDao">

    <insert id="saveUser">
        insert into bngel_user(username, password, phone, email, gender, birthday, register_date, profile)
         values (#{user.username}, #{user.password}, #{user.phone}, #{user.email}, #{user.gender},
                 #{user.birthday}, now(), #{user.profile});
    </insert>

    <delete id="deleteUserById">
        delete from bngel_user where id = #{id};
    </delete>

    <select id="getUserById" resultType="cn.bngel.bngelbookuserprovider8001.bean.User">
        select * from bngel_user where id = #{id};
    </select>

    <select id="login" resultType="cn.bngel.bngelbookuserprovider8001.bean.User">
        select * from bngel_user where (phone = #{account} or email = #{account}) and password = #{password};
    </select>

    <update id="updateUserById">
        update bngel_user
        <set>
            <if test="user.username != null">username=#{user.username},</if>
            <if test="user.password != null">password=#{user.password},</if>
            <if test="user.phone != null">phone=#{user.phone},</if>
            <if test="user.email != null">email=#{user.email},</if>
            <if test="user.gender != null">gender=#{user.gender},</if>
            <if test="user.email != null">birthday=#{user.birthday},</if>
            <if test="user.profile != null">profile=#{user.profile}</if>
        </set>
        where id = #{user.id};
    </update>
</mapper>

sql 映射文件: 写 sql 语句的, MyBatis 会执行这些 sql

  1.  <!DOCTYPE xxx> 指定约束文件
     mybatis-3-mapper.dtd 约束文件的名称, 扩展名为 dtd
 2.  约束文件作用:限制, 检查当前文件中出现的标签, 属性必须满足MyBatis的要求
 3.  mapper 是当前文件的根标签, 是必需的
     namespace: 命名空间, 是唯一值, 可以是自定义的字符串
                 要求使用 dao 接口的全限定名称
 4.  在当前文件中, 可以使用特定的标签, 表示数据库的特定操作
     <select>: 表示执行查询
     <update>: 表示更新数据库的操作, 也就是在<update>标签中写的是 update 的 sql 语句
     <insert>: 表示插入, 放的是 insert 的 sql 语句
     <delete>: 表示删除
 5.  id 表示要执行的 sql 语句的唯一标识, MyBatis 会使用这个 id 的值来找到要执行的 sql 语句
     可以自定义, 但是一般使用接口中的方法名
     resultType 表示结果类型, 是遍历 sql 语句执行后得到的 ResultSet 的 java 对象的类型
     值为类型的全限定名称

其中比较特殊的就是update方法.
因为我们在更新用户信息的时候不一定需要将所有的信息, 或者说基本上都是不需要的.
只需要将其中的个别属性进行修改
因此就需要使用动态SQL语句
mybatis官方文档 | 动态 SQL

动态SQL语句

sql的内容是变化的, 可以根据条件获取到不同的sql语句, 主要是where部分发生变化

动态sql的实现, 使用的是MyBatis提供的标签: <if> 、 <where> 、 <foreach>

  1. <if> 判断条件标签

    1. 动态sql使用时, 方法要使用java对象作为参数

    2. 在mapper中使用<if>标签

      <select id="selectStudentIf" resultType="student">
          select `id`,`name`,`email`,`age` from student
          where
          <if test="name != null and name != '' ">
              name = #{name}
          </if>
          <if test="age > 0">
              and age > #{age}
          </if>
      </select>
      

      在不同的情况下所得到的sql语句为:

      # 当两个if同时成立时
      select `id`,`name`,`email`,`age` from student where name = ? and age > ? 
      # 当姓名为空时 报错
      select `id`,`name`,`email`,`age` from student where and age > ?
      # 当年龄为负数时
      select `id`,`name`,`email`,`age` from student where name = ?
      # 当两个条件同时不满足时 报错
      select `id`,`name`,`email`,`age` from student where
      

      因此需要进行修改

      <select id="selectStudentIf" resultType="student">
          select `id`,`name`,`email`,`age` from student
          <if test="name != null and name != '' ">
              where name = #{name}
              <if test="age > 0">
                  and age > #{age}
              </if>
          </if>
      </select>
      

      也就是<if>标签之间可以互相嵌套.

      或者在主sql语句中, 增加 1=1

      <select id="selectStudentIf" resultType="student">
          select `id`,`name`,`email`,`age` from student
          where 1 = 1
          <if test="name != null and name != '' ">
              and name = #{name}
          </if>
          <if test="age > 0">
              and age > #{age}
          </if>
      </select>
      
  2. <where> 标签用来包含多个 <if>

    当多个<if>标签有一个成立时, <where>会自动添加一个where关键字, 并去掉<if>中多余的 and, or 等.

    避免了sql语法错误导致的报错

    <select id="selectStudentWhere" resultType="student">
        select `id`,`name`,`email`,`age` from student
        <where>
            <if test="">
                <if test="name != null and name != '' ">
                    and name = #{name}
                </if>
                <if test="age > 0">
                    and age > #{age}
                </if>
            </if>
        </where>
    </select>
    

    此时在组装sql语句时, 就会自动添加where关键字, 并且去除多余的 and, or 等关键字.

  3. <foreach>标签做循环处理

    循环java中的数组, list集合, 主要用在sql的in语句中, 例如:

    select * from student where id in (1001,1002,1003)

    因此需要从java代码中传入List数组. 作为sql语句的in的参数.

    1. 需要循环整数(基本类型)集合时

      <select id="selectStudentForEach" resultType="student">
          select * from student where id in
          <!--
          collection: 表示接口中的方法参数的类型, 数组为array, list集合使用list
          item:       自定义的, 表示数组和集合成员的变量
          open:       循环开始时的字符
          close:      循环结束时的字符
          separator:  集合成员之间的分隔符
          -->
          <foreach collection="list" item="myId" open="(" close=")" separator=",">
              #{myId}
          </foreach>
      </select>
      
    2. 需要循环对象集合时

      <select id="selectStudentForEach2" resultType="student">
          select * from student where id in
          <foreach collection="list" item="student" open="(" close=")" separator=",">
              #{student.id}
          </foreach>
      </select>
      

      直接在对应的对象后使用点运算符访问属性即可. student.id 访问当前student对象的id属性.

  4. 代码片段

    格式: <sql id="用来调用的唯一标识"> sql代码片段 </sql>

    调用时使用:

    格式: <include refid="需要调用的代码片段"/>


通过用户传入的`User`, 不为`null`的则表示需要修改
之后直接通过注入进行使用即可.
@Autowired
private UserDao userDao;

欢迎各位来对我的小项目提出各种宝贵的意见, 这对我的进步非常重要, 谢谢大家.
GitHub地址: Bngel/bngelbook