Skip to content

04-MyBatis 与动态 SQL

对应原始资料:数据库阶段_day07~08映射文件&动态SQL.md

一、MyBatis 概述

1. 是什么

MyBatis 是一款优秀的半自动化 ORM 持久层框架

  • JDBC:繁琐,需要手写参数设置、结果集映射、关资源。
  • MyBatis:把 SQL 写在 XML/注解里,参数和结果集自动映射,开发者专注 SQL。

2. ORM 思想

对象 关系 映射:表 ↔ 类,字段 ↔ 属性,记录 ↔ 对象。

  • MyBatis 是半自动(手写 SQL),Hibernate/JPA 是全自动(自动生成 SQL)。

二、快速入门

1. 依赖

xml
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.13</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

2. 核心配置 mybatis-config.xml

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///db1"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/itheima/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

3. Mapper 接口 + XML

java
public interface UserMapper {
    List<User> findAll();
    User findById(Integer id);
    int insert(User u);
}
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">
<mapper namespace="com.itheima.mapper.UserMapper">
    <select id="findAll" resultType="com.itheima.entity.User">
        SELECT * FROM user
    </select>
    <select id="findById" parameterType="int" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>
    <insert id="insert" parameterType="User">
        INSERT INTO user(name, age) VALUES (#{name}, #{age})
    </insert>
</mapper>

4. 执行

java
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
try (SqlSession sqlSession = factory.openSession()) {
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> list = mapper.findAll();
    // 增删改需手动提交(除非 openSession(true))
    sqlSession.commit();
}

三、核心配置项

  • <properties>:引入 properties 文件。
  • <settings>:如 mapUnderscoreToCamelCase(下划线转驼峰)、cacheEnabled
  • <typeAliases>:给类起别名(User 代替全限定名)。
  • <typeHandlers>:类型转换器。
  • <environments>:数据源、事务管理。
  • <mappers>:注册 Mapper。

四、Mapper 代理开发规范(推荐)

  1. Mapper 接口全限定名 == XML 的 namespace
  2. 方法名 == SQL 的 id
  3. 参数类型 == parameterType
  4. 返回类型 == resultType。 满足后即可用 sqlSession.getMapper(XxxMapper.class) 拿到代理对象。

五、输入/输出映射

1. 参数

  • 简单类型:#{id}
  • 对象:#{name} #{age}
  • 多参数:用 @Param("name") 或对象封装,SQL 中 #{name}
  • Map:#{key}

2. #{} vs ${}

  • #{} 预编译(PreparedStatement,? 占位),防注入,绝大多数情况用它。
  • ${} 字符串拼接,有注入风险,仅用于动态表名/列名(如排序字段)。

3. resultMap(解决字段名与属性名不一致)

xml
<resultMap id="userMap" type="User">
    <id property="id" column="id"/>
    <result property="userName" column="user_name"/>
</resultMap>
<select id="findAll" resultMap="userMap">
    SELECT * FROM user
</select>

六、动态 SQL(重点)

1. <if>

xml
<select id="search" resultType="User">
    SELECT * FROM user
    <where>
        <if test="name != null and name != ''">
            AND name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="minAge != null">
            AND age &gt;= #{minAge}
        </if>
    </where>
</select>

<where> 自动去掉首个多余的 AND/OR,并在有条件时加 WHERE。

2. <foreach> —— 批量

xml
<select id="findByIds" resultType="User">
    SELECT * FROM user
    <where>
        <foreach collection="ids" item="id" open="id IN (" separator="," close=")">
            #{id}
        </foreach>
    </where>
</select>

<insert id="batchInsert">
    INSERT INTO user(name, age) VALUES
    <foreach collection="list" item="u" separator=",">
        (#{u.name}, #{u.age})
    </foreach>
</insert>

3. <choose>/<when>/<otherwise>

类似 switch-case。

xml
<choose>
    <when test="type == 'name'"> AND name = #{kw} </when>
    <when test="type == 'age'"> AND age = #{kw} </when>
    <otherwise> AND 1 = 1 </otherwise>
</choose>

4. <set> —— 动态更新

xml
<update>
    UPDATE user
    <set>
        <if test="name != null">name = #{name},</if>
        <if test="age != null">age = #{age},</if>
    </set>
    WHERE id = #{id}
</update>

自动去掉末尾多余的逗号。

5. <sql> 抽取片段

xml
<sql id="userCols">id, name, age, email</sql>
<select>SELECT <include refid="userCols"/> FROM user</select>

七、多表查询

1. 一对一(resultMap + association)

xml
<resultMap id="orderUser" type="Order">
    <id property="id" column="id"/>
    <result property="no" column="no"/>
    <association property="user" javaType="User">
        <id property="id" column="uid"/>
        <result property="name" column="uname"/>
    </association>
</resultMap>

2. 一对多(collection)

xml
<resultMap id="deptWithEmps" type="Dept">
    <id property="id" column="id"/>
    <result property="name" column="dname"/>
    <collection property="emps" ofType="Emp">
        <id property="id" column="eid"/>
        <result property="name" column="ename"/>
    </collection>
</resultMap>

3. 多对多

本质是两个一对多,中间表用 collection 处理。

八、注解开发

简单的 CRUD 可用注解,无需 XML:

java
public interface UserMapper {
    @Select("SELECT * FROM user WHERE id = #{id}")
    User findById(Integer id);

    @Insert("INSERT INTO user(name, age) VALUES (#{name}, #{age})")
    int insert(User u);

    @Update("UPDATE user SET name = #{name} WHERE id = #{id}")
    int update(User u);

    @Delete("DELETE FROM user WHERE id = #{id}")
    int delete(Integer id);

    // 动态 SQL 仍需用 @SelectProvider 或 SQL 构造器
}

取舍:简单 SQL 用注解方便;复杂 SQL/动态 SQL 用 XML 更清晰。

九、MyBatis 高级特性(预告)

特性说明
一级缓存SqlSession 级别,默认开启
二级缓存Mapper 级别,<cache/> 开启,跨 session 共享
延迟加载association/collection 按需查询,提升性能
插件分页(PageHelper)、SQL 打印、逻辑删除

这些内容在 SpringBoot 阶段会结合实战讲解;进阶的 MyBatis-Plus 见中间件模块。

练习建议

  1. 用 MyBatis 完成用户表 CRUD(XML 版)。
  2. 写一个多条件查询(<where> + <if>)。
  3. <foreach> 实现 IN 查询和批量插入。
  4. 实现「部门-员工」一对多查询(collection)。
  5. 完成资料中的动态 SQL、多表查询、注解开发练习。