Loading...

MyBatis 详细教程

一、MyBatis 概述

MyBatis 是一款优秀的持久层框架,支持定制化 SQL、存储过程以及高级映射。它避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的工作,可以使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJO 映射成数据库中的记录。

作为半自动的 ORM 框架,MyBatis 让开发者将主要精力放在 SQL 上,通过灵活的映射方式自由生成满足需要的 SQL 语句。相较于 Hibernate 和 JPA 这类全自动框架,MyBatis 的 SQL 和 Java 编码分离,功能边界清晰——Java 代码专注业务,SQL 语句专注数据。

二、核心配置文件(mybatis-config.xml)详解

MyBatis 的核心配置文件通常命名为 mybatis-config.xml,是 MyBatis 的启动文件,负责整体引导。该文件以标准 XML 格式开头,根元素为 <configuration>。

2.1 配置文件的基本结构

<?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>
    <!-- 属性配置 -->
    <properties resource="jdbc.properties" />
    
    <!-- 全局设置 -->
    <settings>
        ...
    </settings>
    
    <!-- 类型别名 -->
    <typeAliases>
        ...
    </typeAliases>
    
    <!-- 环境配置 -->
    <environments default="development">
        ...
    </environments>
    
    <!-- 映射器配置 -->
    <mappers>
        ...
    </mappers>
</configuration>

核心配置文件中的标签必须按照固定顺序排列:

properties?, settings?, typeAliases?, typeHandlers?,
objectFactory?, objectWrapperFactory?, reflectorFactory?,
plugins?, environments?, databaseIdProvider?, mappers?

2.2 properties 属性配置

<properties> 元素用于引入外部属性文件,最常用的方式是引入数据库连接配置文件。

方式一:引入外部 properties 文件

<properties resource="jdbc.properties" />

其中 jdbc.properties 文件内容如下:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_example
jdbc.username=root
jdbc.password=123456

方式二:直接配置属性

<properties>
    <property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
    <property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis_example"/>
    <property name="jdbc.username" value="root"/>
    <property name="jdbc.password" value="123456"/>
</properties>

在后续配置中通过 ${key} 方式引用:

<dataSource type="POOLED">
    <property name="driver" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</dataSource>

2.3 settings 全局设置

<settings> 元素用于配置 MyBatis 的全局行为,以下是常用的设置项:

<settings>
    <!-- 全局开启或关闭所有映射器中已配置的二级缓存 -->
    <setting name="cacheEnabled" value="true"/>
    
    <!-- 延迟加载的全局开关,当开启时所有关联对象都会延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    
    <!-- 当启用时,任何方法的调用都会加载该对象的所有属性 -->
    <setting name="aggressiveLazyLoading" value="false"/>
    
    <!-- 允许 JDBC 自动生成主键 -->
    <setting name="useGeneratedKeys" value="true"/>
    
    <!-- 是否开启自动驼峰命名规则映射 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    
    <!-- 配置默认的执行器:SIMPLE/REUSE/BATCH -->
    <setting name="defaultExecutorType" value="SIMPLE"/>
    
    <!-- 设置超时时间,它决定驱动等待数据库响应的秒数 -->
    <setting name="defaultStatementTimeout" value="25"/>
    
    <!-- 指定 MyBatis 所用日志的具体实现 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

其中 mapUnderscoreToCamelCase 非常实用,开启后数据库字段 user_name 会自动映射到实体类的 userName 属性。lazyLoadingEnabled 开启延迟加载后,关联对象会在真正被使用时才加载,特定关联关系可通过 fetchType 属性覆盖该开关状态。

2.4 typeAliases 类型别名

类型别名可以为 Java 类型设置一个简短的名字,简化映射文件中的配置。

方式一:单个别名定义

<typeAliases>
    <typeAlias alias="User" type="com.example.model.User"/>
    <typeAlias alias="Order" type="com.example.model.Order"/>
</typeAliases>

方式二:包扫描定义别名

<typeAliases>
    <package name="com.example.model"/>
</typeAliases>

通过 <package> 元素,MyBatis 会扫描指定包下的所有类,将它们的简单类名(首字母小写)作为别名。

2.5 environments 环境配置

<environments> 元素用于配置多个数据库环境,default 属性指定默认使用的环境。一个 environment 代表一个数据库环境,包含事务管理器和数据源配置。

<environments default="development">
    <!-- 开发环境 -->
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
    
    <!-- 生产环境 -->
    <environment id="production">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.prod.url}"/>
            <property name="username" value="${jdbc.prod.username}"/>
            <property name="password" value="${jdbc.prod.password}"/>
        </dataSource>
    </environment>
</environments>

事务管理器类型(transactionManager):

  • JDBC:直接使用 JDBC 的提交和回滚设置
  • MANAGED:让容器来管理事务的整个生命周期

数据源类型(dataSource):

  • UNPOOLED:不使用连接池,每次请求都打开和关闭连接
  • POOLED:使用连接池,复用连接对象
  • JNDI:使用容器提供的 JNDI 数据源

2.6 mappers 映射器配置

<mappers> 元素用于配置 Mapper 映射文件的位置,有四种配置方式:

<mappers>
    <!-- 方式一:使用相对于类路径的资源引用 -->
    <mapper resource="mapper/UserMapper.xml"/>
    
    <!-- 方式二:使用完全限定资源定位符(URL) -->
    <mapper url="file:///var/mappers/UserMapper.xml"/>
    
    <!-- 方式三:使用映射器接口实现类的完全限定类名 -->
    <mapper class="com.example.mapper.UserMapper"/>
    
    <!-- 方式四:将包内的映射器接口全部注册为映射器 -->
    <package name="com.example.mapper"/>
</mappers>

推荐使用方式四(包扫描) ,自动注册指定包下的所有 Mapper 接口。

三、XML 映射文件详解

3.1 映射文件基本结构

<?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.example.mapper.UserMapper">
    
    <!-- 结果映射 -->
    <resultMap id="BaseResultMap" type="User">
        <id property="id" column="id"/>
        <result property="username" column="user_name"/>
        <result property="password" column="password"/>
    </resultMap>
    
    <!-- SQL 片段 -->
    <sql id="Base_Column_List">
        id, user_name, password
    </sql>
    
    <!-- 查询 -->
    <select id="selectById" resultMap="BaseResultMap">
        SELECT <include refid="Base_Column_List"/>
        FROM user WHERE id = #{id}
    </select>
    
    <!-- 插入 -->
    <insert id="insert" parameterType="User" 
            useGeneratedKeys="true" keyProperty="id">
        INSERT INTO user (user_name, password)
        VALUES (#{username}, #{password})
    </insert>
    
    <!-- 更新 -->
    <update id="update" parameterType="User">
        UPDATE user
        SET user_name = #{username}, password = #{password}
        WHERE id = #{id}
    </update>
    
    <!-- 删除 -->
    <delete id="deleteById">
        DELETE FROM user WHERE id = #{id}
    </delete>
</mapper>

3.2 resultMap 自定义结果映射

当数据库字段名与 Java 属性名不一致时,需要使用 <resultMap> 进行自定义映射。

<resultMap id="UserResultMap" type="com.example.model.User">
    <!-- id 用于映射主键 -->
    <id property="id" column="id"/>
    <!-- result 用于映射普通字段 -->
    <result property="username" column="user_name"/>
    <result property="email" column="email_address"/>
    <result property="createTime" column="create_time"/>
</resultMap>

3.3 一对一关联映射(association)

一对一关联使用 <association> 标签。例如,一个学生对应一个班级:

<resultMap id="studentMapper" type="com.example.pojo.Student">
    <id property="sid" column="sid"/>
    <result property="name" column="name"/>
    <result property="age" column="age"/>
    <result property="sex" column="sex"/>
    <!-- 一对一对象映射 -->
    <association property="classes" column="classId" 
                 javaType="com.example.pojo.Classes">
        <id property="cid" column="cid"/>
        <result property="className" column="className"/>
    </association>
</resultMap>

<select id="findAll" resultMap="studentMapper">
    SELECT * FROM student 
    LEFT JOIN classes ON student.classId = classes.cid
</select>

3.4 一对多关联映射(collection)

一对多关联使用 <collection> 标签。例如,一个班级对应多个学生:

<resultMap id="classesMapper" type="com.example.pojo.Classes">
    <id property="cid" column="cid"/>
    <result property="className" column="className"/>
    <!-- 一对多集合映射 -->
    <collection property="studentList" ofType="com.example.pojo.Student">
        <id property="sid" column="sid"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
    </collection>
</resultMap>

<select id="findClassesWithStudents" resultMap="classesMapper">
    SELECT c.cid, c.class_name, s.sid, s.name, s.age
    FROM classes c
    LEFT JOIN student s ON c.cid = s.class_id
    WHERE c.cid = #{cid}
</select>

3.5 动态 SQL

动态 SQL 是 MyBatis 最强大的特性之一,可以根据不同条件动态生成 SQL 语句。

(1)if 标签

<select id="findActiveBlogWithTitleLike" resultType="Blog">
    SELECT * FROM BLOG WHERE state = 'ACTIVE'
    <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)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>

(3)where 标签

<where> 只会在子元素有内容的情况下才插入 WHERE 子句,而且会自动去除子句开头的 AND 或 OR。

<select id="findUserByCondition" resultType="User">
    SELECT * FROM users
    <where>
        <if test="name != null">
            AND name = #{name}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
    </where>
</select>

(4)set 标签(用于 UPDATE 语句)

<set> 会自动在 SQL 前添加 SET 关键字,并去除多余的逗号。

<update id="updateUser" parameterType="User">
    UPDATE user
    <set>
        <if test="username != null">user_name = #{username},</if>
        <if test="password != null">password = #{password},</if>
        <if test="email != null">email = #{email},</if>
    </set>
    WHERE id = #{id}
</update>

(5)foreach 标签

<foreach> 非常适合动态构建 IN 查询或处理批量操作。

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

<!-- 批量插入 -->
<insert id="batchInsert">
    INSERT INTO user (user_name, password) VALUES
    <foreach collection="users" item="user" separator=",">
        (#{user.username}, #{user.password})
    </foreach>
</insert>

<foreach> 标签的属性说明:

  • collection:要遍历的集合,可以是 List、Set、Map、数组
  • item:集合中每个元素的变量名
  • open:开始符号
  • separator:分隔符
  • close:结束符号

(6)trim 标签

<trim> 标签可以自定义 SQL 片段的拼接,去除前缀或后缀,也可以替换特定的前缀或后缀。常用于替代 <where> 和 <set>:

<!-- 等价于 <where> -->
<trim prefix="WHERE" prefixOverrides="AND |OR ">
    ...
</trim>

<!-- 等价于 <set> -->
<trim prefix="SET" suffixOverrides=",">
    ...
</trim>

(7)sql 和 include 标签(SQL 片段复用)

<!-- 定义 SQL 片段 -->
<sql id="Base_Column_List">
    id, user_name, password, email, create_time
</sql>

<!-- 引用 SQL 片段 -->
<select id="selectAll" resultMap="BaseResultMap">
    SELECT <include refid="Base_Column_List"/> FROM user
</select>

3.6 selectKey 获取自增主键

对于支持自增主键的数据库(如 MySQL),可以在 <insert> 中使用 useGeneratedKeys 和 keyProperty 获取插入后的主键值:

<insert id="insertUser" parameterType="User" 
        useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user (user_name, password)
    VALUES (#{username}, #{password})
</insert>

对于不支持自增主键的数据库(如 Oracle),可以使用 <selectKey> 标签:

<insert id="insertUser" parameterType="User">
    <selectKey keyProperty="id" keyColumn="id" resultType="int" order="BEFORE">
        SELECT SEQ_USER.NEXTVAL FROM DUAL
    </selectKey>
    INSERT INTO user (id, user_name, password)
    VALUES (#{id}, #{username}, #{password})
</insert>

对于 MySQL,使用 order="AFTER" 配合 SELECT LAST_INSERT_ID() 获取自增 ID:

<insert id="insertUser">
    INSERT INTO user (user_name, password) VALUES (#{username}, #{password})
    <selectKey keyProperty="id" resultType="int" order="AFTER">
        SELECT LAST_INSERT_ID()
    </selectKey>
</insert>

四、注解方式开发

注解方式可以省去 XML 映射文件,直接在 Mapper 接口上用注解编写 SQL,代码更加简洁。

4.1 基础 CRUD 注解

MyBatis 提供了 @Select、@Insert、@Update、@Delete 四个核心注解:

@Mapper
public interface UserMapper {
    
    @Select("SELECT * FROM users WHERE id = #{id}")
    User getUserById(int id);
    
    @Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
    void insertUser(User user);
    
    @Update("UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}")
    int updateUser(User user);
    
    @Delete("DELETE FROM users WHERE id = #{id}")
    int deleteUser(int id);
}

4.2 @Param 注解(多参数传递)

当方法有多个参数时,需要使用 @Param 注解指定参数名:

@Select("SELECT * FROM user WHERE id = #{id} AND user_name = #{name}")
User getUserByIdAndName(@Param("id") Long id, @Param("name") String username);

@Param 指定的参数名应与 SQL 中 #{} 的参数名一致。

4.3 @Results 和 @Result 注解(结果映射)

当数据库字段名与 Java 属性名不一致时,使用 @Results 和 @Result 解决:

@Select("SELECT * FROM user WHERE id = #{id}")
@Results({
    @Result(property = "username", column = "user_name"),
    @Result(property = "password", column = "password")
})
User getUser(Long id);

4.4 @ResultMap 注解(引用 XML 中的 resultMap)

如果 XML 中已经定义了 <resultMap>,可以使用 @ResultMap 注解直接引用,减少重复代码:

@Select("SELECT * FROM user WHERE id = #{id}")
@ResultMap("BaseResultMap")
User getUser(Long id);

4.5 @Options 注解(配置选项)

@Options 注解用于配置自定义默认行为,最常用的场景是获取自增主键:

@Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertUser(User user);

4.6 @One 和 @Many 注解(关联映射)

注解方式下,一对一使用 @One,一对多使用 @Many:

// 一对一:查询用户时同时查询其角色
@Select("SELECT * FROM user WHERE id = #{userId}")
@Results({
    @Result(property = "id", column = "user_id"),
    @Result(property = "username", column = "username"),
    @Result(property = "role", column = "role_id",
            one = @One(select = "com.example.mapper.RoleMapper.getRoleById"))
})
User getUserWithRole(int userId);

4.7 @SelectKey 注解(获取主键)

@SelectKey(statement = "SELECT identity('users')", keyProperty = "id", 
           before = true, resultType = int.class)
@Insert("INSERT INTO users (id, name) VALUES(#{id}, #{name})")
boolean insert(User user);

4.8 @Provider 注解(动态 SQL)

当注解中的 SQL 比较复杂时,可以使用 Provider 注解将 SQL 生成逻辑移到 Java 方法中。常见 Provider 注解包括 @SelectProvider、@InsertProvider、@UpdateProvider、@DeleteProvider

@SelectProvider(type = UserSqlProvider.class, method = "getUserByCondition")
User getUserByCondition(Map<String, Object> params);

其中 UserSqlProvider 类中定义 getUserByCondition 方法动态生成 SQL 字符串。

4.9 @CacheNamespace 注解(注解方式开启二级缓存)

对于使用注解的 Mapper,在接口上使用 @CacheNamespace 注解即可启用二级缓存:

@CacheNamespace
@Mapper
public interface UserMapper {
    // ...
}

五、Spring Boot 集成 MyBatis

5.1 依赖导入

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

mybatis-spring-boot-starter 已经集成了 mybatis 和 mybatis-spring 的依赖,无需单独引入。

5.2 application.yml 配置

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_db?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    hikari:
      maximum-pool-size: 10
      max-lifetime: 1770000

mybatis:
  # 指定别名设置的包
  type-aliases-package: com.example.entity
  # mapper映射文件位置
  mapper-locations:
    - classpath:mapper/*.xml
  # 全局配置
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true
    lazy-loading-enabled: true
    aggressive-lazy-loading: false
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

其中 map-underscore-to-camel-case: true 开启了驼峰命名规范,数据库字段 user_name 会自动映射到实体类的 userName 属性。

5.3 application.properties 配置(等价写法)

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_db
spring.datasource.username=root
spring.datasource.password=123456

mybatis.type-aliases-package=com.example.entity
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.cache-enabled=true

5.4 @MapperScan 注解

在 Spring Boot 启动类上使用 @MapperScan 注解,自动扫描 Mapper 接口:

@SpringBootApplication
@MapperScan("com.example.mapper")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

六、缓存机制

MyBatis 提供两级缓存:一级缓存和二级缓存。

6.1 一级缓存(SqlSession 级别)

一级缓存默认开启,是 SqlSession 级别的缓存,只对当前 SqlSession 有效。当 SqlSession 关闭或提交时,一级缓存会被清空。当在同一个 SqlSession 中执行相同的查询时,第二次查询会直接从缓存中获取结果,不会访问数据库。

6.2 二级缓存(namespace 级别)

二级缓存需要手动开启,是基于 namespace 级别的缓存,可以被多个 SqlSession 共享。

开启步骤:

第一步:在 mybatis-config.xml 中全局开启(默认已开启)

<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

在 Spring Boot 的 yml 中:

mybatis:
  configuration:
    cache-enabled: true

第二步:在 Mapper XML 中配置 <cache> 标签

<mapper namespace="com.example.mapper.UserMapper">
    <!-- 开启当前 Mapper 的二级缓存 -->
    <cache eviction="FIFO"
           flushInterval="60000"
           size="512"
           readOnly="true"/>
    
    <select id="selectById" resultMap="BaseResultMap" useCache="true">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

<cache> 标签的属性说明:

属性说明默认值
eviction缓存回收策略(LRU/FIFO/SOFT/WEAK)LRU
flushInterval刷新间隔(毫秒),不设置则仅在调用语句时刷新无
size缓存最多存储的对象个数1024
readOnly是否只读false

注解方式开启二级缓存:

7.1 两种参数获取方式

MyBatis 获取参数值有两种方式:#{} 和 ${}。

特性#{}${}
本质占位符赋值(PreparedStatement 参数)字符串拼接
SQL 注入风险无有
字符串类型自动加引号是否(需手动加)
适用场景参数值传递动态表名、列名、排序字段

#{} 使用的是预编译 SQL,通过占位符 ? 赋值,可以防止 SQL 注入;${} 是直接进行字符串替换,存在 SQL 注入风险。

7.2 各种参数类型的使用

单个字面量类型参数: 可以使用任意名称获取值

@Select("SELECT * FROM user WHERE id = #{id}")
User getUserById(Long id);
// 或
@Select("SELECT * FROM user WHERE id = #{value}")
User getUserById(Long id);

多个字面量类型参数: 需要使用 @Param 注解

@Select("SELECT * FROM user WHERE id = #{id} AND name = #{name}")
User getUser(@Param("id") Long id, @Param("name") String name);

实体类类型参数: 直接使用属性名

@Insert("INSERT INTO user (name, age) VALUES (#{name}, #{age})")
void insertUser(User user);

Map 类型参数: 使用 Map 的 key

@Select("SELECT * FROM user WHERE id = #{userId} AND name = #{userName}")
User getUser(Map<String, Object> map);

推荐做法:无论参数多少,统一使用 @Param 标注参数名称,避免使用 arg0、arg1、param1 等自动生成的名称。

八、延迟加载

延迟加载(懒加载)是指在使用关联对象时才去执行查询,而不是在查询主对象时就立即加载。这可以减少不必要的数据库查询。

配置方式:

<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
  • lazyLoadingEnabled:开启延迟加载的全局开关
  • aggressiveLazyLoading:设置为 false 时按需加载,设置为 true 时任何属性的调用都会加载该对象的所有属性

在 Spring Boot 的 yml 中:

mybatis:
  configuration:
    lazy-loading-enabled: true
    aggressive-lazy-loading: false

在特定的关联关系上可以通过 fetchType 属性覆盖全局设置:

<association property="classes" javaType="Classes" 
             select="selectClassesById" column="classId"
             fetchType="lazy">
</association>

九、常见问题与最佳实践

9.1 字段映射问题

当数据库使用下划线命名(如 user_name),Java 使用驼峰命名(如 userName)时,解决方案有三种:

  1. 开启自动驼峰映射(推荐):在配置中设置 map-underscore-to-camel-case: true
  2. 使用 resultMap:在 XML 中自定义映射关系
  3. 使用 @Results 注解:在注解中定义映射关系

9.2 #{} 和 ${} 的选择

优先选择 #{},它可以防止 SQL 注入。只有在以下场景才使用 ${}:

  • 动态表名:SELECT * FROM ${tableName}
  • 动态列名:ORDER BY ${columnName}
  • 动态排序方向:ORDER BY id ${order}

9.3 注解和 XML 的选择

实际项目中,XML 和注解常结合使用。XML 适合管理复杂的 SQL 和结果映射,注解适合简单的 CRUD 操作。推荐的做法是:简单查询用注解,复杂 SQL 和动态 SQL 用 XML,两者混用可以互补。

9.4 事务管理

在 Spring Boot 中,使用 @Transactional 注解管理事务:

@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    @Transactional
    public void batchInsertUsers(List<User> users) {
        for (User user : users) {
            userMapper.insertUser(user);
        }
    }
}

9.5 日志配置

在开发阶段开启 SQL 日志输出,方便调试:

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

或在 application.properties 中配置指定包的日志级别:

logging.level.com.example.mapper=DEBUG

以上教程涵盖了 MyBatis 的核心配置文件、XML 映射文件、注解开发、Spring Boot 集成、动态 SQL、关联映射、缓存机制、延迟加载和参数传递等主要内容。建议从核心配置文件和基础 CRUD 开始学习,逐步深入到动态 SQL、关联映射和缓存等高级特性。

0

回到顶部