MyBatis 从入门到精通

1、什么是 MyBatis

一、MyBatis 核心定义

MyBatis 是一款优秀的持久层(ORM)框架 ,前身是 Apache 的 iBatis,2010 年迁移到 Google Code 并更名为 MyBatis,现属于 Apache 顶级项目。它彻底简化了 JDBC 冗余代码,通过 XML 或注解的方式,将 SQL 语句与 Java 对象做映射,让开发者可以专注于 SQL 本身,同时实现数据库的灵活操作。

二、核心特点

SQL 与代码解耦 :SQL 写在 XML 中,与 Java 业务代码分离,便于维护、调优和权限控制✅ 灵活的 SQL 支持 :支持自定义 SQL、存储过程、多表关联、动态 SQL,适配复杂业务场景✅ 简化 JDBC 操作 :自动处理连接、Statement、ResultSet 封装,无需手动编写冗余代码✅ ORM 映射能力 :通过 ResultMap 实现数据库字段与 Java 对象属性的自动映射✅ 轻量无侵入 :无第三方依赖,学习成本低,完美适配 SSM、SpringBoot 等主流框架✅ 性能优异:原生 JDBC 封装,无额外性能损耗,支持连接池、缓存优化

三、MyBatis 与其他持久层技术对比

技术 核心优势 核心劣势 适用场景
原生 JDBC 性能最高、无依赖 代码冗余、SQL 耦合、手动处理连接 简单工具类、性能极致要求场景
MyBatis SQL 灵活、易优化、轻量 需手动编写 SQL(部分场景) 企业级项目、复杂查询、SSM 架构
JPA/Hibernate 全自动 ORM、无需写 SQL SQL 不灵活、调优困难、学习成本高 快速开发、简单 CRUD 项目
JdbcTemplate Spring 内置、简单轻量 动态 SQL 支持弱、复杂查询不便 Spring 项目简单数据库操作

四、核心架构

复制代码
┌─────────────────────────────────────────────────────────┐
│  应用层(Java代码):调用 MyBatis API,执行业务逻辑        │
└───────────────────────────┬─────────────────────────────┘
                            │
┌───────────────────────────▼─────────────────────────────┐
│  MyBatis 核心层:SqlSessionFactory、SqlSession、Executor  │
└───────────────────────────┬─────────────────────────────┘
                            │
┌───────────────────────────▼─────────────────────────────┐
│  映射层:Mapper 接口 + XML/注解,定义 SQL 与映射规则       │
└───────────────────────────┬─────────────────────────────┘
                            │
┌───────────────────────────▼─────────────────────────────┐
│  数据库层:MySQL/Oracle 等,执行 SQL 语句                 │
└─────────────────────────────────────────────────────────┘

2、第一个 MyBatis 程序

一、环境准备

  • 开发工具:IDEA、Maven 3.8+、MySQL 8.0+
  • 核心依赖pom.xml):

xml

复制代码
<dependencies>
    <!-- MyBatis 核心依赖 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.13</version>
    </dependency>
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
        <scope>runtime</scope>
    </dependency>
    <!-- 日志依赖(调试必备) -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <!-- JUnit 测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

<!-- 解决 Maven 资源过滤问题(XML 配置文件无法加载) -->
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
    </resources>
</build>

二、步骤 1:创建数据库与表

sql

复制代码
CREATE DATABASE IF NOT EXISTS mybatis_demo CHARACTER SET utf8mb4;
USE mybatis_demo;

CREATE TABLE `user` (
    `id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
    `username` VARCHAR(50) NOT NULL COMMENT '用户名',
    `password` VARCHAR(50) NOT NULL COMMENT '密码',
    `email` VARCHAR(50) COMMENT '邮箱',
    `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 插入测试数据
INSERT INTO `user` (username, password, email) VALUES 
('admin', '123456', 'admin@example.com'),
('test', '654321', 'test@example.com');

三、步骤 2:编写实体类(POJO)

复制代码
package com.example.pojo;

import java.util.Date;

// 实体类:对应数据库 user 表
public class User {
    // 属性名建议与数据库字段名一致(不一致需做映射)
    private Integer id;
    private String username;
    private String password;
    private String email;
    private Date createTime;

    // 必须有无参构造(MyBatis 反射创建对象使用)
    public User() {}

    // 有参构造(可选)
    public User(Integer id, String username, String password, String email, Date createTime) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.email = email;
        this.createTime = createTime;
    }

    // getter/setter 方法(必须)
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public Date getCreateTime() { return createTime; }
    public void setCreateTime(Date createTime) { this.createTime = createTime; }

    // toString 方法(方便打印)
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", email='" + email + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

四、步骤 3:编写 MyBatis 核心配置文件

src/main/resources 下创建 mybatis-config.xml

xml

复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!-- 环境配置:可配置多套环境(开发/测试/生产) -->
    <environments default="development">
        <environment id="development">
            <!-- 事务管理器:JDBC 表示手动管理事务 -->
            <transactionManager type="JDBC"/>
            <!-- 数据源:POOLED 表示使用 MyBatis 内置连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 映射器:注册 SQL 映射文件 -->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

五、步骤 4:编写 Mapper 映射文件

src/main/resources/mapper 下创建 UserMapper.xml

xml

复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- namespace:命名空间,必须唯一,建议写 Mapper 接口全类名 -->
<mapper namespace="com.example.mapper.UserMapper">
    <!-- 查询单个用户:id 为唯一标识,resultType 为返回值类型 -->
    <select id="getUserById" resultType="com.example.pojo.User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

六、步骤 5:编写工具类(SqlSession 工厂)

复制代码
package com.example.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

// MyBatis 工具类:获取 SqlSession
public class MyBatisUtils {
    // 静态工厂:全局唯一
    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            // 1. 加载核心配置文件
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 2. 构建 SqlSessionFactory
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
            // 抛出异常,避免工厂创建失败
            throw new RuntimeException("初始化 SqlSessionFactory 失败!", e);
        }
    }

    /**
     * 获取 SqlSession(默认手动提交事务)
     */
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }

    /**
     * 获取 SqlSession(自动提交事务)
     */
    public static SqlSession getSqlSession(boolean autoCommit) {
        return sqlSessionFactory.openSession(autoCommit);
    }
}

七、步骤 6:编写测试类

复制代码
package com.example.test;

import com.example.pojo.User;
import com.example.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class MyBatisTest {
    @Test
    public void testGetUserById() {
        // 1. 获取 SqlSession
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        try {
            // 2. 执行 SQL:namespace + id
            User user = sqlSession.selectOne("com.example.mapper.UserMapper.getUserById", 1);
            // 3. 打印结果
            System.out.println(user);
        } finally {
            // 4. 关闭资源(必须)
            sqlSession.close();
        }
    }
}

八、运行结果

text

复制代码
User{id=1, username='admin', password='123456', email='admin@example.com', createTime=...}

✅ 第一个 MyBatis 程序运行成功!


3、增删改查实现(CRUD)

一、核心 API 说明

操作 核心方法 标签 说明
查询单个 selectOne() <select> 返回单个对象
查询列表 selectList() <select> 返回集合
新增 insert() <insert> 执行 INSERT 语句
修改 update() <update> 执行 UPDATE 语句
删除 delete() <delete> 执行 DELETE 语句

二、Mapper 映射文件(UserMapper.xml 完整 CRUD)

xml

复制代码
<mapper namespace="com.example.mapper.UserMapper">
    <!-- 1. 查询单个用户 -->
    <select id="getUserById" resultType="com.example.pojo.User">
        SELECT * FROM user WHERE id = #{id}
    </select>

    <!-- 2. 查询所有用户 -->
    <select id="getUserList" resultType="com.example.pojo.User">
        SELECT * FROM user
    </select>

    <!-- 3. 新增用户 -->
    <insert id="addUser" parameterType="com.example.pojo.User">
        INSERT INTO user (username, password, email)
        VALUES (#{username}, #{password}, #{email})
    </insert>

    <!-- 4. 修改用户 -->
    <update id="updateUser" parameterType="com.example.pojo.User">
        UPDATE user
        SET username=#{username}, password=#{password}, email=#{email}
        WHERE id=#{id}
    </update>

    <!-- 5. 删除用户 -->
    <delete id="deleteUser" parameterType="int">
        DELETE FROM user WHERE id=#{id}
    </delete>
</mapper>

三、CRUD 测试类

复制代码
package com.example.test;

import com.example.pojo.User;
import com.example.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class CRUDTest {
    // 1. 查询所有用户
    @Test
    public void testGetUserList() {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        try {
            List<User> userList = sqlSession.selectList("com.example.mapper.UserMapper.getUserList");
            userList.forEach(System.out::println);
        } finally {
            sqlSession.close();
        }
    }

    // 2. 新增用户
    @Test
    public void testAddUser() {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        try {
            User user = new User(null, "zhangsan", "123456", "zhangsan@example.com", null);
            int rows = sqlSession.insert("com.example.mapper.UserMapper.addUser", user);
            // 增删改必须手动提交事务!
            sqlSession.commit();
            System.out.println("新增成功,影响行数:" + rows);
        } finally {
            sqlSession.close();
        }
    }

    // 3. 修改用户
    @Test
    public void testUpdateUser() {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        try {
            User user = new User(3, "zhangsan_update", "654321", "zhangsan_update@example.com", null);
            int rows = sqlSession.update("com.example.mapper.UserMapper.updateUser", user);
            sqlSession.commit();
            System.out.println("修改成功,影响行数:" + rows);
        } finally {
            sqlSession.close();
        }
    }

    // 4. 删除用户
    @Test
    public void testDeleteUser() {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        try {
            int rows = sqlSession.delete("com.example.mapper.UserMapper.deleteUser", 3);
            sqlSession.commit();
            System.out.println("删除成功,影响行数:" + rows);
        } finally {
            sqlSession.close();
        }
    }
}

四、核心注意事项

⚠️ 增删改必须提交事务 :MyBatis 默认关闭自动提交,执行 insert/update/delete 后必须调用 sqlSession.commit(),否则数据不会写入数据库!✅ 也可以在获取 SqlSession 时开启自动提交:MyBatisUtils.getSqlSession(true)


4、错误排查指导

一、常见错误与解决方案

错误现象 根本原因 解决方案
Invalid bound statement (not found) Mapper XML 与接口不匹配、namespace/id 错误、XML 未被加载 1. 检查 namespace 全类名正确;2. 保证接口方法名与 XML id 一致;3. 配置 Maven 资源过滤,确保 XML 被编译
ClassNotFoundException: com.mysql.cj.jdbc.Driver 驱动类名错误、未导入 MySQL 依赖 1. MySQL 8.0+ 驱动类为 com.mysql.cj.jdbc.Driver;2. 检查 pom.xml 依赖是否正确
Access denied for user 'root'@'localhost' 数据库账号 / 密码错误、URL 错误 检查 mybatis-config.xml 中的 username、password、url 配置
SQLException: Column 'xxx' not found 数据库字段与实体类属性名不一致 1. 起别名;2. 使用 ResultMap 做映射;3. 开启驼峰命名自动映射
SQLSyntaxErrorException SQL 语句语法错误 开启日志,查看实际执行的 SQL,定位语法问题
Transaction rolled back 事务未提交 / 回滚 增删改后必须调用 commit(),或开启自动提交
Maven 打包后 XML 丢失 Maven 默认过滤 Java 目录下的 XML 文件 在 pom.xml 中配置资源加载,包含 Java 目录下的 XML

二、调试神器:开启 Log4j 日志

  1. src/main/resources 下创建 log4j.properties

properties

复制代码
# 日志级别:DEBUG 可查看完整 SQL、参数、执行结果
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
  1. 作用:实时查看 MyBatis 执行的 SQL 语句、入参、结果,90% 的错误都能通过日志快速定位!

三、排查流程

  1. 开启日志,查看执行的 SQL 是否正确
  2. 复制 SQL 到 Navicat 手动执行,验证 SQL 语法
  3. 检查参数传递是否正确,实体类属性是否匹配
  4. 检查配置文件是否加载,Mapper 映射是否正确
  5. 检查事务是否提交,连接是否正常

5、Map 和模糊查询拓展

一、Map 传参:灵活传递参数

当实体类字段不足、或临时查询无需创建实体类时,使用 Map 传递参数,无需修改实体类。

1. Mapper XML

xml

复制代码
<!-- Map 传参:根据用户名和密码查询用户 -->
<select id="getUserByMap" resultType="com.example.pojo.User">
    SELECT * FROM user WHERE username = #{username} AND password = #{password}
</select>
2. 测试代码
复制代码
@Test
public void testGetUserByMap() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    try {
        Map<String, Object> map = new HashMap<>();
        map.put("username", "admin");
        map.put("password", "123456");
        User user = sqlSession.selectOne("com.example.mapper.UserMapper.getUserByMap", map);
        System.out.println(user);
    } finally {
        sqlSession.close();
    }
}

二、模糊查询:Like 语句实现

方式 1:Java 代码拼接通配符(推荐,防 SQL 注入)

xml

复制代码
@Test
public void testGetUserLike() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    try {
        // Java 代码中添加 % 通配符
        String keyword = "%ad%";
        List<User> userList = sqlSession.selectList("com.example.mapper.UserMapper.getUserLike", keyword);
        userList.forEach(System.out::println);
    } finally {
        sqlSession.close();
    }
}
方式 2:XML 中拼接通配符(不推荐,有 SQL 注入风险)

xml

复制代码
<select id="getUserLike" resultType="com.example.pojo.User">
    SELECT * FROM user WHERE username LIKE '%${value}%'
</select>

⚠️ 注意:${} 会直接拼接字符串,存在 SQL 注入风险,仅在特殊场景使用;#{} 是预编译,安全无注入。

三、多条件查询拓展

  • 方式 1:实体类传参:封装查询条件到实体类

  • 方式 2:Map 传参:灵活传递多个条件

  • 方式 3:@Param 注解传参(接口式编程推荐)

    // Mapper 接口
    public interface UserMapper {
    User getUserByParam(@Param("username") String username, @Param("password") String password);
    }


6、配置之属性优化

一、核心优化:抽离数据库配置到外部文件

将数据库连接信息从 mybatis-config.xml 抽离到 db.properties,实现解耦,便于多环境切换。

1. 创建 db.propertiessrc/main/resources

properties

复制代码
# 数据库连接配置
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
2. 修改 mybatis-config.xml,引入配置文件

xml

复制代码
<configuration>
    <!-- 1. 引入外部 properties 配置文件 -->
    <properties resource="db.properties"/>

    <!-- 2. 使用 ${} 引用配置 -->
    <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>
    </environments>

    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

二、其他核心配置优化

1. 开启驼峰命名自动映射

数据库字段 user_name 自动映射到实体类属性 userName,无需手动写 ResultMap:

xml

复制代码
<configuration>
    <settings>
        <!-- 开启驼峰命名自动映射 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 开启日志 -->
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    <!-- 其他配置... -->
</configuration>
2. 配置多套环境(开发 / 测试 / 生产)

xml

复制代码
<environments default="development">
    <!-- 开发环境 -->
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <!-- 开发环境配置 -->
        </dataSource>
    </environment>
    <!-- 测试环境 -->
    <environment id="test">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <!-- 测试环境配置 -->
        </dataSource>
    </environment>
</environments>

7、配置之别名优化

一、别名的作用

简化 resultTypeparameterType 中的全类名书写,无需每次写 com.example.pojo.User,直接写 User 即可。

二、别名配置方式

方式 1:单个别名配置(精准控制)

xml

复制代码
<configuration>
    <typeAliases>
        <!-- 为 User 类起别名:User -->
        <typeAlias type="com.example.pojo.User" alias="User"/>
    </typeAliases>
    <!-- 其他配置... -->
</configuration>

✅ 优点:别名自定义,精准控制;缺点:每个类都要配置,繁琐。

方式 2:包扫描别名(批量配置,推荐)

xml

复制代码
<configuration>
    <typeAliases>
        <!-- 扫描 com.example.pojo 包下的所有类,别名为类名(首字母大小写不敏感) -->
        <package name="com.example.pojo"/>
    </typeAliases>
    <!-- 其他配置... -->
</configuration>

✅ 优点:批量配置,无需逐个写;缺点:别名默认是类名,不可自定义。

三、别名使用示例

配置别名后,Mapper XML 中可直接写类名,无需全类名:

xml

复制代码
<!-- 配置前:全类名 -->
<select id="getUserById" resultType="com.example.pojo.User">
    SELECT * FROM user WHERE id = #{id}
</select>

<!-- 配置后:直接写别名 -->
<select id="getUserById" resultType="User">
    SELECT * FROM user WHERE id = #{id}
</select>

四、MyBatis 内置别名

MyBatis 为常用类型提供了内置别名,无需手动配置

类型 别名
int/Integer int/integer
String string
Map map
List list
Date date

8、配置之映射器说明

一、映射器(Mapper)的作用

映射器是 MyBatis 的核心,用于绑定 SQL 语句与 Java 方法,是 MyBatis 执行 SQL 的入口。

二、映射器的 4 种注册方式

方式 1:resource 方式(推荐,XML 方式)

xml

复制代码
<mappers>
    <!-- 从类路径加载 XML 文件 -->
    <mapper resource="mapper/UserMapper.xml"/>
</mappers>

✅ 优点:简单直观,XML 与接口分离;缺点:每个 XML 都要注册。

方式 2:class 方式(注解方式)

xml

复制代码
<mappers>
    <!-- 注册 Mapper 接口,使用注解写 SQL -->
    <mapper class="com.example.mapper.UserMapper"/>
</mappers>

✅ 优点:无需 XML,注解写 SQL;缺点:复杂 SQL 维护困难。

方式 3:package 方式(批量注册,推荐)

xml

复制代码
<mappers>
    <!-- 扫描 com.example.mapper 包下的所有 Mapper 接口,自动注册 -->
    <package name="com.example.mapper"/>
</mappers>

✅ 优点:批量注册,无需逐个写;要求:Mapper 接口与 XML 文件名一致,且在同一包下

方式 4:url 方式(不推荐)

xml

复制代码
<mappers>
    <mapper url="file:///D:/mapper/UserMapper.xml"/>
</mappers>

❌ 缺点:硬编码,环境迁移麻烦,仅特殊场景使用。

三、Mapper 接口式编程(企业级开发推荐)

1. 创建 Mapper 接口
复制代码
package com.example.mapper;

import com.example.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserMapper {
    // 查询单个用户
    User getUserById(Integer id);

    // 查询所有用户
    List<User> getUserList();

    // 新增用户
    int addUser(User user);

    // 修改用户
    int updateUser(User user);

    // 删除用户
    int deleteUser(Integer id);

    // Map 传参查询
    User getUserByMap(@Param("username") String username, @Param("password") String password);
}
2. Mapper XML 绑定接口

xml

复制代码
<!-- namespace 必须写 Mapper 接口全类名 -->
<mapper namespace="com.example.mapper.UserMapper">
    <!-- id 必须与接口方法名一致 -->
    <select id="getUserById" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>
    <!-- 其他方法... -->
</mapper>
3. 测试代码
复制代码
@Test
public void testMapperInterface() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    try {
        // 动态代理获取 Mapper 接口实现类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 直接调用接口方法
        User user = mapper.getUserById(1);
        System.out.println(user);
    } finally {
        sqlSession.close();
    }
}

✅ 优点:面向接口编程,代码更优雅,符合企业级开发规范,是 MyBatis 主流使用方式。

四、核心注意事项

  1. namespace 必须唯一:建议写 Mapper 接口全类名,保证唯一
  2. id 必须与接口方法名一致:否则无法绑定
  3. 参数类型与返回值类型必须匹配parameterType/resultType 与接口方法一致
  4. XML 与接口必须在同一包下(package 注册方式):文件名一致,路径一致

9、生命周期和作用域

一、核心作用域分类

MyBatis 中对象的生命周期直接影响线程安全、资源释放、数据隔离,核心分为 4 个层级:

作用域 生命周期 线程安全 核心对象 应用场景
SqlSessionFactoryBuilder 局部变量(方法内) 安全 构建工厂 仅用于创建工厂,使用后立即销毁
SqlSessionFactory 全局唯一(应用级) 安全 连接池、配置 贯穿整个应用,负责创建 SqlSession
SqlSession 一次请求 / 方法 不安全 会话、连接 非线程安全,需手动关闭,建议方法内使用
Mapper 接口 一次请求(方法内) 安全 数据操作 由 SqlSession 动态代理,方法执行后销毁

二、详细生命周期解析

1. SqlSessionFactoryBuilder(构建器)
  • 生命周期方法级,仅在加载配置文件、创建工厂的过程中使用

  • 作用 :加载 mybatis-config.xml,构建 SqlSessionFactory

  • 关键点:创建工厂后立即丢弃,无需全局保存,避免占用内存

  • 代码示例

    // 局部变量:使用后销毁
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(Resources.getResourceAsStream("mybatis-config.xml"));

2. SqlSessionFactory(工厂)
  • 生命周期应用级,随应用启动创建,随应用关闭销毁
  • 作用 :生产 SqlSession,管理连接池、环境配置
  • 关键点全局唯一,一个应用只需一个实例,避免重复创建造成资源浪费
  • 实现方式 :可通过单例模式、工具类静态代码块实现(参考之前的 MyBatisUtils
3. SqlSession(会话)
  • 生命周期请求级 / 方法级,一次请求或一个方法内有效

  • 作用:执行 SQL、管理事务、获取 Mapper

  • 关键点非线程安全! 不能共享,必须方法内创建、手动关闭

  • 核心方法

    • openSession():获取会话,默认不自动提交事务
    • commit():提交事务
    • rollback():回滚事务
    • close():关闭会话(必须! 释放数据库连接)
  • 代码示例

    // 正确使用方式:try-finally 保证关闭
    try (SqlSession sqlSession = MyBatisUtils.getSqlSession()) {
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.getUserById(1);
    sqlSession.commit();
    } // 自动关闭

4. Mapper 接口(映射器)
  • 生命周期方法级,一次方法调用内有效
  • 作用:定义 SQL 入口,封装参数与结果
  • 关键点 :由 SqlSession.getMapper() 动态代理创建,方法执行结束后销毁
  • 注意:Mapper 接口必须与 XML 映射文件、namespace 绑定,且方法名与 SQL id 一致

三、线程安全总结

⚠️ 核心结论

  • SqlSessionFactory线程安全,可全局共享
  • SqlSession非线程安全,每次请求都要新建
  • Mapper线程安全,无状态(仅封装方法)

10、ResultMap 结果集映射

一、核心作用

解决数据库字段名与 Java 实体类属性名不一致的问题,实现自定义映射规则。

  • 场景:数据库字段 user_name,实体类属性 userName;或字段 pwd,属性 password

二、两种实现方式

方式 1:起别名(简单场景)

在 SQL 语句中直接起别名,使别名与实体类属性名一致:

xml

复制代码
<select id="getUserById" resultType="User">
    <!-- 给字段起别名,与实体类属性一致 -->
    SELECT id, username, password AS pwd, email FROM user WHERE id = #{id}
</select>
方式 2:ResultMap 自定义映射(推荐,复杂场景)

定义 ResultMap 标签,手动指定字段与属性的映射关系,支持高级映射(如关联对象、集合)。

1. 定义 ResultMap

xml

复制代码
<mapper namespace="com.example.mapper.UserMapper">
    <!-- 
        id:唯一标识
        type:映射的实体类全类名
    -->
    <resultMap id="UserMap" type="User">
        <!-- 主键字段:id 与数据库主键一致 -->
        <id property="id" column="id"/>
        <!-- 普通字段:property 为实体类属性,column 为数据库字段 -->
        <result property="userName" column="user_name"/>
        <result property="password" column="pwd"/>
        <result property="email" column="email"/>
    </resultMap>

    <!-- 使用 ResultMap:resultMap 属性引用 id -->
    <select id="getUserById" resultMap="UserMap">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

三、高级 ResultMap 应用

1. 关联查询(一对一,如用户 + 角色)

实体类:User 中包含 Role 对象

复制代码
public class User {
    private Integer id;
    private String userName;
    private Role role; // 关联角色对象
}

Mapper XML :使用 <association> 标签

xml

复制代码
<resultMap id="UserRoleMap" type="User">
    <id property="id" column="id"/>
    <result property="userName" column="user_name"/>
    <!-- 关联对象:javaType 指定关联对象类型 -->
    <association property="role" javaType="Role">
        <id property="id" column="role_id"/>
        <result property="roleName" column="role_name"/>
    </association>
</resultMap>
2. 关联查询(一对多,如用户 + 订单)

实体类:User 中包含 List<Order> 集合

复制代码
public class User {
    private Integer id;
    private String userName;
    private List<Order> orderList; // 关联订单集合
}

Mapper XML :使用 <collection> 标签

xml

复制代码
<resultMap id="UserOrderMap" type="User">
    <id property="id" column="id"/>
    <result property="userName" column="user_name"/>
    <!-- 关联集合:ofType 指定集合元素类型 -->
    <collection property="orderList" ofType="Order">
        <id property="id" column="order_id"/>
        <result property="orderName" column="order_name"/>
        <result property="userId" column="id"/>
    </collection>
</resultMap>

四、核心注意事项

ResultMap 优先级高于 resultType✅ 复杂查询(多表关联、嵌套查询)必须使用 ResultMap✅ 数据库字段与实体类属性完全一致时,无需 ResultMap,直接用 resultType


11、日志工厂

一、核心作用

MyBatis 日志工厂用于输出 SQL 语句、执行参数、结果,是调试和排查问题的核心工具。

  • 核心日志实现:SLF4JLOG4JLOG4J2JDK_LOGGINGSTDOUT_LOGGING(标准输出)

二、开启标准日志(STDOUT_LOGGING)

最简单的日志方式,直接在配置文件中开启,无需额外依赖。

1. 配置 mybatis-config.xml

xml

复制代码
<configuration>
    <settings>
        <!-- 开启标准输出日志 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
</configuration>
2. 运行效果

控制台会打印执行的 SQL、参数、执行结果,示例:

text

复制代码
==>  Preparing: SELECT * FROM user WHERE id = ?
==> Parameters: 1(Integer)
<==    Columns: id, user_name, pwd, email
<==        Row: 1, admin, 123456, admin@example.com
<==      Total: 1

三、日志工厂核心配置

mybatis-config.xml 中通过 <setting name="logImpl" value="日志实现类"/> 配置日志工厂:

日志实现类 说明 依赖
STDOUT_LOGGING 标准输出(控制台)
LOG4J Log4j 日志框架 需导入 log4j 依赖
SLF4J 简单日志门面 需导入 SLF4J 依赖
JDK_LOGGING JDK 内置日志

12、Log4j 讲解

一、核心概念

Log4j 是 Apache 基金会的开源日志框架,是 MyBatis 最常用的日志实现,支持日志级别、输出格式、输出目的地的灵活配置。

二、使用步骤

1. 导入 Maven 依赖

xml

复制代码
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
2. 配置 log4j.properties

src/main/resources 下创建 log4j.properties,配置日志输出规则:

properties

复制代码
# 1. 配置根日志:级别(DEBUG)、输出源(stdout)
log4j.rootLogger=DEBUG, stdout

# 2. 配置输出源:控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# 输出格式:PatternLayout
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 日志格式:%5p 级别左对齐5位,%t 线程名,%m 日志内容,%n 换行
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

# 3. 可选:配置文件输出(将日志写入文件)
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=mybatis.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
3. 开启 Log4j 日志

mybatis-config.xml 中配置:

xml

复制代码
<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

三、核心日志级别

Log4j 日志级别从低到高:ALL < DEBUG < INFO < WARN < ERROR < FATAL < OFF

  • DEBUG:调试级别,输出 SQL、参数(开发必备)
  • INFO:信息级别,输出系统运行信息
  • WARN:警告级别,输出潜在问题
  • ERROR:错误级别,输出异常信息

四、优势

  • 灵活配置:可通过配置文件动态调整日志级别
  • 多输出目的地:控制台、文件、数据库等
  • 高性能:低损耗,不影响系统性能

13、Limit 实现分页

一、核心概念

分页是 Web 开发必备功能,MyBatis 中通过 SQL 的 LIMIT 关键字实现简单分页,适合小数据量场景。

  • 核心公式:SELECT * FROM table LIMIT #{startIndex}, #{pageSize}
  • startIndex:起始索引 = (当前页码 - 1) * 每页条数
  • pageSize:每页显示条数

二、代码实现

1. Mapper 接口
复制代码
public interface UserMapper {
    // 分页查询:startIndex 起始索引,pageSize 每页条数
    List<User> getUserByLimit(Map<String, Object> map);
}
2. Mapper XML

xml

复制代码
<mapper namespace="com.example.mapper.UserMapper">
    <select id="getUserByLimit" resultType="User" parameterType="map">
        SELECT * FROM user LIMIT #{startIndex}, #{pageSize}
    </select>
</mapper>
3. 测试代码
复制代码
@Test
public void testGetUserByLimit() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 分页参数:第 1 页,每页 2 条
    Map<String, Object> map = new HashMap<>();
    map.put("startIndex", 0);
    map.put("pageSize", 2);

    List<User> userList = mapper.getUserByLimit(map);
    userList.forEach(System.out::println);
    sqlSession.close();
}

三、分页逻辑封装

实际开发中,需封装分页工具类,计算起始索引:

复制代码
// 计算起始索引
int currentPage = 1; // 当前页码
int pageSize = 10;   // 每页条数
int startIndex = (currentPage - 1) * pageSize;

14、RowBounds 分页

一、核心概念

RowBounds 是 MyBatis 提供的内存分页 方式,无需编写 SQL 的 LIMIT,通过 Java 代码实现分页,底层仍使用 LIMIT

  • 优点:与数据库解耦,切换数据库无需修改 SQL
  • 缺点:内存分页,先查询所有数据再分页,大数据量下性能极低(不推荐生产使用)

二、代码实现

1. Mapper 接口(无需参数)
复制代码
public interface UserMapper {
    // 无需参数,RowBounds 会在运行时传入
    List<User> getUserByRowBounds();
}
2. Mapper XML

xml

复制代码
<select id="getUserByRowBounds" resultType="User">
    SELECT * FROM user
</select>
3. 测试代码
复制代码
@Test
public void testGetUserByRowBounds() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    // 1. 创建 RowBounds:offset 起始索引,limit 每页条数
    RowBounds rowBounds = new RowBounds(0, 2);

    // 2. 执行 SQL:传入 RowBounds
    List<User> userList = sqlSession.selectList("com.example.mapper.UserMapper.getUserByRowBounds", null, rowBounds);
    userList.forEach(System.out::println);
    sqlSession.close();
}

三、与 Limit 分页对比

方式 优点 缺点 适用场景
Limit 分页 性能高,数据库层面分页 与 SQL 耦合 生产环境推荐
RowBounds 解耦,无需修改 SQL 性能低,内存分页 简单测试、小数据量

15、使用注解开发(完整版)

MyBatis 提供了一套基于注解的开发方式,可以完全不写 XML 映射文件,直接在 Mapper 接口方法上写 SQL,适合简单 CRUD、快速开发场景。

15.1 核心常用注解

  • @Select:查询
  • @Insert:新增
  • @Update:修改
  • @Delete:删除
  • @Param:参数命名(多参数必用)
  • @Results@Result:替代 ResultMap 做字段映射
  • @One@Many:一对一、一对多关联查询

15.2 纯注解 CRUD 示例

1. 编写 Mapper 接口

复制代码
package com.example.mapper;

import com.example.pojo.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMapper {

    // 根据ID查询
    @Select("select * from user where id = #{id}")
    User getUserById(@Param("id") Integer id);

    // 查询全部
    @Select("select * from user")
    List<User> getUserList();

    // 新增用户
    @Insert("insert into user(username,password,email) " +
            "values(#{username},#{password},#{email})")
    int addUser(User user);

    // 修改用户
    @Update("update user set username=#{username},password=#{password},email=#{email} " +
            "where id=#{id}")
    int updateUser(User user);

    // 删除用户
    @Delete("delete from user where id=#{id}")
    int deleteUser(@Param("id") Integer id);
}

2. 在 mybatis-config.xml 中注册 Mapper

xml

复制代码
<mappers>
    <!-- 直接注册接口类,不需要xml -->
    <mapper class="com.example.mapper.UserMapper"/>
</mappers>

3. 测试

复制代码
@Test
public void testAnnotation(){
    SqlSession session = MyBatisUtils.getSqlSession(true);
    UserMapper mapper = session.getMapper(UserMapper.class);

    User user = mapper.getUserById(1);
    System.out.println(user);

    session.close();
}

15.3 字段映射 @Results / @Result

当数据库字段与实体类属性不一致时,使用注解映射:

复制代码
@Select("select * from user where id=#{id}")
@Results({
        @Result(id = true, column = "id", property = "id"),
        @Result(column = "user_name", property = "username"),
        @Result(column = "pwd", property = "password")
})
User getUserById(@Param("id") Integer id);

15.4 注解开发优缺点

优点:

  • 简单、快速,不用写 XML
  • 适合简单 SQL、微服务小型项目

缺点:

  • 复杂 SQL(嵌套、动态 SQL、多表关联)写起来非常乱
  • 不利于 SQL 统一管理
  • 企业主流依然是 XML + 接口 混合使用

16、MyBatis 核心执行流程(面试必考)

这一章是原理核心,面试 90% 会问,我给你整理成最清晰的完整版流程。

16.1 整体执行流程(一句话总结)

加载配置 → 构建工厂 → 创建会话 → 获取代理 Mapper → 执行 SQL → 结果映射 → 关闭资源

16.2 详细步骤(12 步完整版)

  1. 加载核心配置文件 读取 mybatis-config.xml,解析环境、数据源、mappers、settings 等。

  2. 加载映射文件 / 注解 解析所有 *Mapper.xml 或接口上的注解 SQL,把每一条 SQL 封装成 MappedStatement 对象。

  3. 构建 SqlSessionFactory 通过 SqlSessionFactoryBuilder 构建全局唯一的 SqlSessionFactory

  4. 创建 SqlSession openSession() 获得一次数据库会话,包含事务、连接、执行器。

  5. 创建 Executor 执行器MyBatis 会根据配置创建:

    • SimpleExecutor 简单执行器
    • ReuseExecutor 重用 Statement
    • BatchExecutor 批量执行
    • CachingExecutor 带缓存的执行器
  6. 通过动态代理获取 Mapper 接口实例 session.getMapper(UserMapper.class)→ JDK 动态代理生成代理对象→ 代理类会根据方法名找到对应的 MappedStatement

  7. 参数处理 把 Java 参数转换成 JDBC 可识别的参数,处理 #{} 预编译。

  8. 执行 SQL Executor 调用 JDBC 的 Statement/PreparedStatement 执行 SQL。

  9. 结果集封装(ResultSet) 查询结果通过 ResultSetHandler 转换成 Java 对象:

    • 字段映射
    • 驼峰转换
    • ResultMap 映射
    • 集合 / 对象封装
  10. 事务处理 增删改需要 commit(),查询自动结束。

  11. **缓存处理(一级 / 二级缓存)**如果开启缓存,会先查缓存,命中直接返回。

  12. 关闭资源关闭 SqlSession → 归还连接 → 释放资源。

16.3 极简流程图(适合写博客配图)

plaintext

复制代码
mybatis-config.xml
       ↓
SqlSessionFactoryBuilder
       ↓
SqlSessionFactory 【单例】
       ↓
SqlSession 【非线程安全】
       ↓
Executor(执行器)
       ↓
MapperProxy(代理对象)
       ↓
MappedStatement(SQL信息)
       ↓
JDBC 执行 SQL
       ↓
ResultSetHandler 结果映射
       ↓
返回 Java 对象

16.4 高频面试题(直接背)

  1. **SqlSession 为什么是非线程安全的?**因为持有数据库连接,会被多线程污染,必须一次请求一个。

  2. **MyBatis 的 Executor 有哪些?**Simple、Reuse、Batch、CachingExecutor。

  3. **Mapper 接口没有实现类为什么能调用?**JDK 动态代理生成代理对象。

  4. **#{} 和 区别?预编译,防注入;{}` 字符串拼接,有注入风险。

  5. **MyBatis 一级缓存、二级缓存是什么?**一级:SqlSession 级别,默认开启。二级:Mapper 级别,需手动开启。

17、注解增删改查

一、核心注解回顾

MyBatis 提供了一套纯注解的持久层开发方案,无需编写 XML 映射文件,直接在 Mapper 接口上通过注解定义 SQL,适合简单 CRUD 场景。核心注解如下:

注解 作用 对应标签
@Select 执行查询 SQL <select>
@Insert 执行新增 SQL <insert>
@Update 执行修改 SQL <update>
@Delete 执行删除 SQL <delete>
@Param 为参数命名,解决多参数绑定问题 -
@Results/@Result 自定义结果集映射,替代 ResultMap <resultMap>

二、完整注解 CRUD 实现

1. Mapper 接口代码
复制代码
package com.example.mapper;

import com.example.pojo.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMapper {

    // 1. 根据ID查询用户
    @Select("SELECT * FROM user WHERE id = #{id}")
    @Results({
            @Result(id = true, column = "id", property = "id"),
            @Result(column = "user_name", property = "userName"),
            @Result(column = "pwd", property = "password")
    })
    User getUserById(@Param("id") Integer id);

    // 2. 查询所有用户
    @Select("SELECT * FROM user")
    List<User> getUserList();

    // 3. 新增用户
    @Insert("INSERT INTO user(user_name, pwd, email) VALUES(#{userName}, #{password}, #{email})")
    // 开启自增主键回显(MySQL)
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int addUser(User user);

    // 4. 修改用户
    @Update("UPDATE user SET user_name=#{userName}, pwd=#{password}, email=#{email} WHERE id=#{id}")
    int updateUser(User user);

    // 5. 删除用户
    @Delete("DELETE FROM user WHERE id = #{id}")
    int deleteUser(@Param("id") Integer id);

    // 6. 多条件查询(@Param命名参数)
    @Select("SELECT * FROM user WHERE user_name LIKE #{username} AND email LIKE #{email}")
    List<User> getUserByCondition(@Param("username") String username, @Param("email") String email);
}
2. 核心配置说明
  • @Options注解 :用于配置 SQL 执行的额外属性,如useGeneratedKeys开启自增主键回显,keyProperty指定主键对应的实体类属性。
  • @Results注解:当数据库字段与实体类属性名不一致时,手动配置映射关系,替代 XML 中的 ResultMap。
  • @Param注解:当方法有多个参数时,必须使用该注解为每个参数命名,否则 MyBatis 无法正确绑定参数。
3. 注册 Mapper(mybatis-config.xml)

xml

复制代码
<mappers>
    <!-- 注解开发直接注册Mapper接口类 -->
    <mapper class="com.example.mapper.UserMapper"/>
</mappers>
4. 测试代码
复制代码
@Test
public void testAnnotationCRUD() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession(true); // 自动提交事务
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 新增用户
    User user = new User(null, "lisi", "123456", "lisi@example.com");
    int addRows = mapper.addUser(user);
    System.out.println("新增成功,用户ID:" + user.getId()); // 自增主键回显

    // 查询用户
    User queryUser = mapper.getUserById(user.getId());
    System.out.println("查询结果:" + queryUser);

    // 修改用户
    queryUser.setEmail("lisi_new@example.com");
    int updateRows = mapper.updateUser(queryUser);
    System.out.println("修改成功,影响行数:" + updateRows);

    // 删除用户
    int deleteRows = mapper.deleteUser(user.getId());
    System.out.println("删除成功,影响行数:" + deleteRows);

    sqlSession.close();
}

三、注解开发优缺点

优点 :代码简洁,无需维护 XML 文件,开发效率高,适合简单 CRUD 场景。❌ 缺点 :复杂 SQL(多表关联、动态 SQL)可读性差,难以维护,企业级项目主流仍采用XML + 接口的混合开发模式。


18、Lombok 的使用

一、Lombok 核心概念

Lombok 是一款 Java 代码简化工具,通过注解自动生成实体类的getter/settertoString构造方法等冗余代码,大幅减少样板代码,提升开发效率。

二、使用步骤

1. 导入 Maven 依赖

xml

复制代码
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>
2. IDEA 安装 Lombok 插件
  • 打开 IDEA → FileSettingsPlugins → 搜索Lombok → 安装并重启 IDEA。
  • 高版本 IDEA 已内置 Lombok 插件,无需手动安装。
3. 核心常用注解
注解 作用
@Data 自动生成getter/settertoStringequalshashCode、无参构造
@NoArgsConstructor 生成无参构造方法
@AllArgsConstructor 生成全参构造方法
@Getter/@Setter 单独生成getter/setter方法
@ToString 生成toString方法
@Builder 生成 Builder 建造者模式代码
@Slf4j 自动生成log日志对象(替代手动创建Logger
4. 实体类简化示例

传统实体类(冗余代码多)

复制代码
public class User {
    private Integer id;
    private String userName;
    private String password;
    private String email;

    public User() {}
    public User(Integer id, String userName, String password, String email) {
        this.id = id;
        this.userName = userName;
        this.password = password;
        this.email = email;
    }

    //  getter/setter、toString、equals、hashCode 省略(几十行冗余代码)
}

Lombok 简化后实体类

复制代码
package com.example.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

@Data // 自动生成getter/setter、toString等
@NoArgsConstructor // 无参构造
@AllArgsConstructor // 全参构造
public class User {
    private Integer id;
    private String userName;
    private String password;
    private String email;
}

三、核心优势

  • 减少冗余代码:实体类代码量减少 70% 以上,提升开发效率。
  • 代码可维护性高:修改属性时无需手动维护 getter/setter,避免遗漏。
  • 无侵入性:注解不影响业务逻辑,编译时自动生成字节码,运行时无依赖。

19、复杂查询环境搭建

一、环境准备(多表关联查询基础)

复杂查询核心是多表关联,我们以经典的「用户 - 订单 - 商品」模型为例,搭建多表环境:

1. 数据库表设计

sql

复制代码
-- 用户表
CREATE TABLE `user` (
    `id` INT PRIMARY KEY AUTO_INCREMENT,
    `user_name` VARCHAR(50) NOT NULL,
    `pwd` VARCHAR(50) NOT NULL,
    `email` VARCHAR(50)
);

-- 订单表(多对一:多个订单对应一个用户)
CREATE TABLE `orders` (
    `id` INT PRIMARY KEY AUTO_INCREMENT,
    `order_num` VARCHAR(50) NOT NULL,
    `user_id` INT NOT NULL,
    FOREIGN KEY (`user_id`) REFERENCES `user`(`id`)
);

-- 商品表(多对多:一个订单对应多个商品,一个商品对应多个订单)
CREATE TABLE `goods` (
    `id` INT PRIMARY KEY AUTO_INCREMENT,
    `goods_name` VARCHAR(50) NOT NULL,
    `price` DECIMAL(10,2) NOT NULL
);

-- 订单-商品中间表(多对多关联表)
CREATE TABLE `order_goods` (
    `order_id` INT NOT NULL,
    `goods_id` INT NOT NULL,
    PRIMARY KEY (`order_id`, `goods_id`),
    FOREIGN KEY (`order_id`) REFERENCES `orders`(`id`),
    FOREIGN KEY (`goods_id`) REFERENCES `goods`(`id`)
);
2. 实体类设计
复制代码
// User.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String userName;
    private String password;
    private String email;
    // 一对多:一个用户对应多个订单
    private List<Orders> orderList;
}

// Orders.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Orders {
    private Integer id;
    private String orderNum;
    // 多对一:多个订单对应一个用户
    private User user;
    // 多对多:一个订单对应多个商品
    private List<Goods> goodsList;
}

// Goods.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Goods {
    private Integer id;
    private String goodsName;
    private BigDecimal price;
}
3. 核心配置
  • 保持 MyBatis 核心配置文件mybatis-config.xml不变,开启驼峰命名自动映射:

xml

复制代码
<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <setting name="logImpl" value="LOG4J"/>
</settings>
  • 配置别名,简化 XML 中的全类名:

xml

复制代码
<typeAliases>
    <package name="com.example.pojo"/>
</typeAliases>

20、多对一的处理

一、多对一核心场景

多个订单(Orders)对应一个用户(User),即多对一关联,需要在查询订单时,关联查询对应用户信息。

二、两种实现方式

方式 1:嵌套结果查询(推荐,一次 SQL 查询)

通过association标签,在一条 SQL 中关联查询订单和用户信息,自动封装到实体类。

1. Mapper 接口
复制代码
public interface OrdersMapper {
    // 查询订单,关联查询对应用户
    List<Orders> getOrdersWithUser();
}
2. Mapper XML

xml

复制代码
<mapper namespace="com.example.mapper.OrdersMapper">
    <resultMap id="OrdersUserMap" type="Orders">
        <id property="id" column="id"/>
        <result property="orderNum" column="order_num"/>
        <!-- 多对一关联:association标签,javaType指定关联对象类型 -->
        <association property="user" javaType="User">
            <id property="id" column="user_id"/>
            <result property="userName" column="user_name"/>
            <result property="email" column="email"/>
        </association>
    </resultMap>

    <select id="getOrdersWithUser" resultMap="OrdersUserMap">
        SELECT o.*, u.user_name, u.email
        FROM orders o
        LEFT JOIN user u ON o.user_id = u.id
    </select>
</mapper>
方式 2:嵌套查询(分步查询,两次 SQL)

先查询订单,再根据订单的user_id关联查询用户信息,适合复杂场景。

1. Mapper 接口
复制代码
public interface OrdersMapper {
    List<Orders> getOrdersWithUserStep();
}

public interface UserMapper {
    User getUserById(@Param("id") Integer id);
}
2. Mapper XML

xml

复制代码
<!-- OrdersMapper.xml -->
<resultMap id="OrdersUserStepMap" type="Orders">
    <id property="id" column="id"/>
    <result property="orderNum" column="order_num"/>
    <!-- 多对一:select指定关联查询的SQL,column指定传递的参数 -->
    <association property="user" javaType="User"
                 select="com.example.mapper.UserMapper.getUserById"
                 column="user_id"/>
</resultMap>

<select id="getOrdersWithUserStep" resultMap="OrdersUserStepMap">
    SELECT * FROM orders
</select>

三、核心注意事项

  • association标签用于多对一 / 一对一 关联,javaType指定关联对象的类型。
  • 嵌套结果查询性能更高(一次 SQL),嵌套查询更灵活(分步查询,可复用 SQL)。
  • 关联查询必须使用ResultMap,无法使用resultType

21、一对多的处理

一、一对多核心场景

一个用户(User)对应多个订单(Orders),即一对多关联,需要在查询用户时,关联查询对应所有订单信息。

二、两种实现方式

方式 1:嵌套结果查询(推荐,一次 SQL 查询)

通过collection标签,在一条 SQL 中关联查询用户和订单信息,自动封装到实体类。

1. Mapper 接口
复制代码
public interface UserMapper {
    // 查询用户,关联查询对应所有订单
    List<User> getUsersWithOrders();
}
2. Mapper XML

xml

复制代码
<mapper namespace="com.example.mapper.UserMapper">
    <resultMap id="UserOrdersMap" type="User">
        <id property="id" column="id"/>
        <result property="userName" column="user_name"/>
        <result property="email" column="email"/>
        <!-- 一对多关联:collection标签,ofType指定集合元素类型 -->
        <collection property="orderList" ofType="Orders">
            <id property="id" column="order_id"/>
            <result property="orderNum" column="order_num"/>
        </collection>
    </resultMap>

    <select id="getUsersWithOrders" resultMap="UserOrdersMap">
        SELECT u.*, o.id AS order_id, o.order_num
        FROM user u
        LEFT JOIN orders o ON u.id = o.user_id
    </select>
</mapper>
方式 2:嵌套查询(分步查询,两次 SQL)

先查询用户,再根据用户 ID 关联查询所有订单信息。

1. Mapper 接口
复制代码
public interface UserMapper {
    List<User> getUsersWithOrdersStep();
}

public interface OrdersMapper {
    List<Orders> getOrdersByUserId(@Param("userId") Integer userId);
}
2. Mapper XML

xml

复制代码
<!-- UserMapper.xml -->
<resultMap id="UserOrdersStepMap" type="User">
    <id property="id" column="id"/>
    <result property="userName" column="user_name"/>
    <!-- 一对多:select指定关联查询的SQL,column指定传递的参数 -->
    <collection property="orderList" ofType="Orders"
                select="com.example.mapper.OrdersMapper.getOrdersByUserId"
                column="id"/>
</resultMap>

<select id="getUsersWithOrdersStep" resultMap="UserOrdersStepMap">
    SELECT * FROM user
</select>

<!-- OrdersMapper.xml -->
<select id="getOrdersByUserId" resultType="Orders">
    SELECT * FROM orders WHERE user_id = #{userId}
</select>

三、核心注意事项

  • collection标签用于一对多 关联,ofType指定集合中元素的类型(区别于associationjavaType)。
  • 一对多关联查询,实体类中必须定义List<Orders>类型的属性。
  • 嵌套结果查询需注意 SQL 别名,避免字段名冲突(如id需起别名order_id)。

22、动态 SQL 环境搭建

一、动态 SQL 核心概念

动态 SQL 是 MyBatis 最强大的特性之一,用于根据条件动态拼接 SQL 语句,解决传统 JDBC 中 SQL 拼接繁琐、易出错的问题,支持条件判断、循环、分支等逻辑。

二、环境搭建

1. 核心依赖

保持原有 MyBatis 依赖不变,无需额外引入,动态 SQL 是 MyBatis 原生支持的功能。

2. 核心配置

mybatis-config.xml中开启驼峰命名、日志,配置别名,确保 Mapper XML 正常加载。

3. 测试实体类与 Mapper

以用户多条件查询为例,搭建动态 SQL 测试环境:

复制代码
// UserMapper接口
public interface UserMapper {
    // 多条件动态查询
    List<User> getUserByCondition(User user);
}

23、动态 SQL 之 IF 语句

一、IF 语句核心作用

if标签是动态 SQL 最常用的标签,用于条件判断,根据参数是否为空,动态拼接 SQL 片段,实现多条件模糊查询。

二、代码实现

Mapper XML

xml

复制代码
<mapper namespace="com.example.mapper.UserMapper">
    <select id="getUserByCondition" resultType="User">
        SELECT * FROM user
        WHERE 1=1 <!-- 解决第一个条件为空时的语法错误 -->
        <if test="userName != null and userName != ''">
            AND user_name LIKE CONCAT('%', #{userName}, '%')
        </if>
        <if test="email != null and email != ''">
            AND email LIKE CONCAT('%', #{email}, '%')
        </if>
        <if test="id != null">
            AND id = #{id}
        </if>
    </select>
</mapper>

三、核心说明

  • test属性:条件判断表达式,支持 OGNL 语法,判断参数是否为空、是否满足条件。
  • WHERE 1=1:解决第一个条件为空时,SQL 出现WHERE AND的语法错误(后续可使用where标签替代)。
  • CONCAT('%', #{userName}, '%'):MySQL 中拼接模糊查询通配符,防 SQL 注入(推荐)。

四、测试代码

复制代码
@Test
public void testDynamicIf() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 条件1:仅查询用户名包含"admin"的用户
    User condition1 = new User();
    condition1.setUserName("admin");
    List<User> list1 = mapper.getUserByCondition(condition1);
    list1.forEach(System.out::println);

    // 条件2:查询用户名包含"admin"且邮箱包含"example"的用户
    User condition2 = new User();
    condition2.setUserName("admin");
    condition2.setEmail("example");
    List<User> list2 = mapper.getUserByCondition(condition2);
    list2.forEach(System.out::println);

    sqlSession.close();
}

24、动态 SQL 常用标签

一、核心常用标签汇总

标签 作用 替代语法
if 条件判断,动态拼接 SQL 传统 if-else 拼接
where 自动处理WHERE关键字,去除多余的AND/OR 手动写WHERE 1=1
set 自动处理SET关键字,去除多余的逗号 手动拼接 UPDATE 语句
choose/when/otherwise 多分支条件判断,相当于 Java 的if-else if-else 多层 if 嵌套
foreach 循环遍历集合,动态拼接 IN 语句 手动循环拼接
trim 自定义字符串截取,去除前缀 / 后缀 手动处理 SQL 语法
bind 自定义参数绑定,适配不同数据库 数据库函数拼接

二、各标签详细实现

1. where标签(替代WHERE 1=1

自动去除 SQL 中多余的AND/OR,无需手动写WHERE 1=1

xml

复制代码
<select id="getUserByCondition" resultType="User">
    SELECT * FROM user
    <where>
        <if test="userName != null and userName != ''">
            AND user_name LIKE CONCAT('%', #{userName}, '%')
        </if>
        <if test="email != null and email != ''">
            AND email LIKE CONCAT('%', #{email}, '%')
        </if>
    </where>
</select>
2. set标签(动态 UPDATE)

自动去除 UPDATE 语句中多余的逗号:

xml

复制代码
<update id="updateUserSelective">
    UPDATE user
    <set>
        <if test="userName != null and userName != ''">
            user_name = #{userName},
        </if>
        <if test="email != null and email != ''">
            email = #{email},
        </if>
        <if test="password != null and password != ''">
            pwd = #{password},
        </if>
    </set>
    WHERE id = #{id}
</update>
3. choose/when/otherwise(多分支判断)

相当于 Java 的if-else if-else,只会执行第一个满足条件的分支:

xml

复制代码
<select id="getUserByChoose" resultType="User">
    SELECT * FROM user
    <where>
        <choose>
            <when test="id != null">
                AND id = #{id}
            </when>
            <when test="userName != null and userName != ''">
                AND user_name = #{userName}
            </when>
            <otherwise>
                AND 1=1
            </otherwise>
        </choose>
    </where>
</select>
4. foreach标签(循环遍历,IN 查询)

用于遍历集合,动态拼接IN语句,适合批量查询、批量删除:

xml

复制代码
<!-- 批量查询:根据ID列表查询用户 -->
<select id="getUserByIdList" resultType="User">
    SELECT * FROM user
    WHERE id IN
    <foreach collection="idList" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

<!-- 批量删除 -->
<delete id="deleteUserByIdList">
    DELETE FROM user
    WHERE id IN
    <foreach collection="idList" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</delete>
  • collection:指定遍历的集合(List/Array/Map)
  • item:遍历的当前元素
  • open/close:拼接的前缀 / 后缀
  • separator:元素之间的分隔符
5. trim标签(自定义截取)

自定义 SQL 前缀 / 后缀的截取,灵活处理 SQL 语法:

xml

复制代码
<select id="getUserByCondition" resultType="User">
    SELECT * FROM user
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
        <if test="userName != null and userName != ''">
            AND user_name LIKE CONCAT('%', #{userName}, '%')
        </if>
        <if test="email != null and email != ''">
            AND email LIKE CONCAT('%', #{email}, '%')
        </if>
    </trim>
</select>
  • prefix:添加前缀
  • prefixOverrides:去除前缀(如多余的AND/OR
  • suffixOverrides:去除后缀(如多余的逗号)

25、动态 SQL 之 Foreach

一、核心作用

foreach 标签是动态 SQL 中处理集合循环 的最常用标签,主要用于批量删除、批量查询(IN 语句),可以自动遍历集合或数组,拼接 SQL 语句。

二、核心参数解析

参数 作用 示例
collection 指定要遍历的集合参数 list / array / map
item 循环中的当前元素变量名 item="id"
index 索引(遍历 List/Map 时) index="i"
open 拼接 SQL 的开头 通常为 (
close 拼接 SQL 的结尾 通常为 )
separator 元素之间的分隔符 通常为 ,

三、代码实现(批量查询与删除)

1. Mapper 接口
复制代码
package com.example.mapper;

import com.example.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

public interface UserMapper {
    // 批量查询:根据ID列表查询用户
    List<User> getUserByIdList(@Param("idList") List<Integer> idList);
    
    // 批量删除:根据ID列表删除
    int deleteUserByIdList(@Param("idList") List<Integer> idList);
    
    // 批量添加(可选)
    int addUserBatch(List<User> userList);
}
2. Mapper XML 配置

xml

复制代码
<mapper namespace="com.example.mapper.UserMapper">

    <!-- 1. 批量查询:IN 条件 -->
    <select id="getUserByIdList" resultType="User">
        SELECT * FROM user
        WHERE id IN
        <foreach collection="idList" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </select>

    <!-- 2. 批量删除:IN 条件 -->
    <delete id="deleteUserByIdList">
        DELETE FROM user
        WHERE id IN
        <foreach collection="idList" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>

    <!-- 3. 批量添加(性能较高) -->
    <insert id="addUserBatch">
        INSERT INTO user (user_name, pwd, email)
        VALUES
        <foreach collection="list" item="user" separator=",">
            (#{user.userName}, #{user.password}, #{user.email})
        </foreach>
    </insert>
</mapper>

四、测试代码

复制代码
@Test
public void testForeach() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 1. 批量查询
    List<Integer> ids = Arrays.asList(1, 2, 3);
    List<User> userList = mapper.getUserByIdList(ids);
    userList.forEach(System.out::println);

    // 2. 批量删除
    // int rows = mapper.deleteUserByIdList(ids);
    // System.out.println("删除行数:" + rows);

    sqlSession.close();
}

五、避坑指南

⚠️ collection 属性必须指定

  • 如果参数是 List,默认填 list
  • 如果参数是 Array,默认填 array
  • 如果使用 @Param("xxx") 注解,必须填 xxx

26、缓存简介

一、什么是 MyBatis 缓存?

MyBatis 缓存是指将查询结果存入内存(或磁盘) ,当执行相同的查询时,直接从缓存中获取结果,减少数据库访问,提高性能

二、缓存体系结构

MyBatis 缓存分为一级缓存二级缓存,层级关系如下:

  1. 一级缓存(本地缓存)SqlSession 级别,默认开启,无法关闭。
  2. 二级缓存(全局缓存)Mapper 级别,默认开启,但需手动配置。
  3. 第三方缓存 :如 EhcacheRedis,用于替代默认的二级缓存。

27、一级缓存

一、核心定义

一级缓存是SqlSession 会话级的缓存。它是 MyBatis 自动开启的,不需要任何配置。

  • 作用域 :同一个 SqlSession 内有效。
  • 存储结构PerSessionCache(Map 结构)。

二、工作机制

  1. 查询流程
    • 第一次查询:发送 SQL 到数据库,存入缓存
    • 第二次查询(同 SqlSession):直接从缓存取,不执行 SQL。
  2. 缓存失效场景
    • 执行 增删改 操作(insert/update/delete)会清空缓存。
    • 手动调用 sqlSession.clearCache() 清空。
    • SqlSession 关闭或提交后,缓存失效。

三、代码演示

复制代码
@Test
public void testFirstLevelCache() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 第一次查询
    User u1 = mapper.getUserById(1);
    System.out.println(u1);

    // 中间执行了一次增删改,缓存被清空
    // mapper.deleteUser(1); 
    // sqlSession.commit();

    // 第二次查询
    User u2 = mapper.getUserById(1);
    System.out.println(u2);

    // 关键:u1 == u2 ?
    System.out.println(u1 == u2); // true,说明是同一个对象(缓存命中)
    sqlSession.close();
}

四、总结

一级缓存是 MyBatis 底层机制,保证单线程下的数据一致性,通常不需要手动干预。


28、二级缓存

一、核心定义

二级缓存是Mapper 级别 的缓存(全局缓存)。它跨 SqlSession,多个会话可以共享缓存。

  • 作用域:整个应用内(基于 Mapper)。
  • 默认状态 :开启,但需要手动在 XML 中开启 <cache/>

二、使用步骤

1. 开启二级缓存(Mapper XML)

UserMapper.xml 头部添加:

xml

复制代码
<mapper namespace="com.example.mapper.UserMapper">
    <!-- 开启当前 Mapper 的二级缓存 -->
    <cache/>
    
    <!-- 或者配置属性:eviction="LRU" 缓存策略 -->
    <!-- <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/> -->
</mapper>
2. 实体类序列化(必须!)

MyBatis 要求缓存的实体类必须实现 Serializable 接口,因为二级缓存可能序列化到磁盘或网络传输。

复制代码
import lombok.Data;
import java.io.Serializable; // 必须导入

@Data
public class User implements Serializable { // 必须实现序列化
    private Integer id;
    private String userName;
    private String password;
    private String email;
}
3. 测试代码
复制代码
@Test
public void testSecondLevelCache() {
    // 会话1
    try (SqlSession session1 = MyBatisUtils.getSqlSession()) {
        UserMapper mapper1 = session1.getMapper(UserMapper.class);
        User u1 = mapper1.getUserById(1);
        System.out.println(u1);
        session1.close(); // 关闭会话,一级缓存会存入二级缓存
    }

    // 会话2
    try (SqlSession session2 = MyBatisUtils.getSqlSession()) {
        UserMapper mapper2 = session2.getMapper(UserMapper.class);
        User u2 = mapper2.getUserById(1); // 不会发SQL,走二级缓存
        System.out.println(u2);
        session2.close();
    }
}

三、核心配置属性

属性 说明
eviction 缓存回收策略,默认 LRU(最近最少使用)。
flushInterval 刷新间隔,单位毫秒。
size 缓存引用数量,默认 1024。
readOnly 是否只读。true 返回代理对象(性能高);false 返回克隆对象(安全)。

29、MyBatis 缓存原理

一、底层结构图

text

复制代码
SqlSession
   ↓
Executor (执行器)
   ↓
CachingExecutor (装饰器模式) -> 【负责二级缓存】
   ↓
BaseExecutor -> 【负责一级缓存】
   ↓
JDBC 执行

二、执行流程剖析

  1. 查询流程
    • 用户请求 -> SqlSession -> 获取 Mapper -> CachingExecutor
    • CachingExecutor 先查二级缓存(Mapper 级别)。
    • 二级缓存未命中 -> BaseExecutor一级缓存(SqlSession 级别)。
    • 一级缓存未命中 -> 执行 JDBC 查询 -> 结果存入一级缓存 -> 返回。
  2. 写入流程
    • 查询结果先写入一级缓存
    • 事务提交(commit)时,一级缓存数据写入二级缓存

三、为什么需要二级缓存?

一级缓存只能在一个会话内有效,无法共享数据。二级缓存跨会话,能大幅降低相同查询的压力。


30、自定义缓存 Ehcache

一、核心概念

MyBatis 默认的二级缓存是内存级的,重启应用会丢失。如果需要分布式缓存、持久化缓存 ,可以使用 EhcacheRedis

二、整合 Ehcache 步骤

1. 导入 Maven 依赖

xml

复制代码
<!-- MyBatis 与 Ehcache 整合包 -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.2</version>
</dependency>
<!-- Ehcache 核心包 -->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.9.2</version>
</dependency>
2. 配置 Mapper 使用 Ehcache

xml

复制代码
<mapper namespace="com.example.mapper.UserMapper">
    <!-- 指定使用 Ehcache 缓存 -->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
</mapper>
3. 配置 Ehcache 文件(ehcache.xml

resources 下创建 ehcache.xml,定义缓存策略:

xml

复制代码
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
         
    <!-- 磁盘缓存路径 -->
    <diskStore path="java.io.tmpdir/ehcache-mybatis"/>

    <!-- 默认缓存策略 -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiredThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>

    <!-- 自定义针对 User 的缓存策略 -->
    <cache name="com.example.mapper.UserMapper"
           maxElementsInMemory="100"
           eternal="false"
           timeToIdleSeconds="300"
           timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LFU"/>
</ehcache>

三、对比与总结

  • Ehcache:适合本地缓存,支持磁盘持久化,性能好。
  • Redis:适合分布式缓存,跨服务器,功能更强。

31、MyBatis 总结

一、整体知识图谱

这是一份 MyBatis 从入门到精通的完整路线,建议收藏:

  1. 基础篇:配置、SqlSession、CRUD、日志。
  2. 进阶篇:ResultMap、生命周期、插件。
  3. 高级篇:动态 SQL(IF、Where、Foreach)、多表关联(一对一、一对多)。
  4. 性能篇:缓存(一级 / 二级 / Ehcache)、分页插件。
  5. 整合篇:与 Spring、SpringBoot 整合。

二、核心技术点回顾

表格

模块 核心知识点
SQL 映射 #{}, ${}, parameterType, resultType, ResultMap
动态 SQL if, where, set, choose, foreach
关联查询 association (多对一), collection (一对多)
缓存 一级缓存(SqlSession),二级缓存(Mapper),Ehcache
注解 @Select, @Insert, @Param, @Results
工具 Lombok, PageHelper, Log4j

三、MyBatis 为何流行?

SQL 与代码分离 :维护方便,DBA 可优化。✅ 动态 SQL 强大 :适配各种业务场景。✅ 轻量 :小巧灵活,易于上手。❌ 与 Spring 对比:Spring 是 IOC/AOP 容器,MyBatis 是持久层框架。MyBatis 需与 Spring 配合使用。


32、聊聊 Spring 这东西(过渡与总结)

一、MyBatis 与 Spring 的关系

MyBatis 是持久层框架 ,负责操作数据库;Spring 是全能容器框架,负责管理 Bean、事务、AOP。

  • 整合目的
    1. 用 Spring 管理 SqlSessionFactoryMapper 接口。
    2. 用 Spring 的声明式事务(@Transactional)替代 MyBatis 手动事务。
    3. 整合 SpringMVC 完成完整的 Web 三层架构(SSM)。

二、SSM 架构流转图

text

复制代码
用户 (View: JSP/Vue)
   ↓
SpringMVC (Controller:接收请求,返回数据)
   ↓
Spring (Service:业务逻辑,事务管理)
   ↓
MyBatis (Mapper:操作数据库)
   ↓
MySQL

三、学习建议

  1. 先吃透 MyBatis:理解 ORM 原理、动态 SQL、缓存。
  2. 再学 Spring:理解 IOC(控制反转)、AOP(面向切面编程)。
  3. 最后学 SpringBoot:自动配置,大幅减少 XML,快速开发。

四、结尾寄语

MyBatis 是 Java 后端开发的基石。掌握了它,你就掌握了数据持久化的核心逻辑。接下来的学习路径建议:

  • 进阶:学习 Spring + SpringMVC + MyBatis (SSM) 整合。
  • 实战:做一个完整的项目(如电商、管理系统)。
  • 前沿:学习 MyBatis-Plus(MP),它是 MyBatis 的增强版,大幅提升开发效率。
相关推荐
96774 小时前
mybatis的作用+sql怎么写
java·开发语言·mybatis
那个失眠的夜1 天前
Mybatis延迟加载策略
xml·java·数据库·maven·mybatis
空太Jun1 天前
Spring Security 角色权限&资源权限配置 学习笔记
笔记·学习·spring·mybatis·security·springsecurity
身如柳絮随风扬1 天前
MyBatis 插件原理详解:从拦截器到动态代理,手写一个分页插件
java·mybatis
小江的记录本1 天前
【JEECG Boot】 JEECG Boot——数据字典管理 系统性知识体系全解析
java·前端·spring boot·后端·spring·spring cloud·mybatis
wuqingshun3141591 天前
说一下mybatis里面#{}和${}的区别
java·spring·mybatis
小江的记录本1 天前
【JEECG Boot】 《JEECG Boot 数据字典使用教程》(完整版)
java·前端·数据库·spring boot·后端·spring·mybatis
小江的记录本1 天前
【JEECG Boot】 JEECG Boot 数据字典管理——六大核心功能(内含:《JEECG Boot 数据字典开发速查清单》)
java·前端·数据库·spring boot·后端·spring·mybatis
小江的记录本1 天前
【JEECG Boot】 JEECG Boot——Online表单 系统性知识体系全解
java·前端·spring boot·后端·spring·低代码·mybatis