学习笔记:Mybatis 示例代码,应用场景,面试题

一、核心代码示例

1. 基础环境搭建(Maven 依赖)

xml

xml 复制代码
<!-- 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>
<!-- Spring 整合 MyBatis(企业常用) -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.7</version>
</dependency>

2. 核心配置文件(mybatis-config.xml)

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">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false&amp;serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 映射器配置 -->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

3. 实体类(User.java)

java

运行

arduino 复制代码
public class User {
    private Long id;
    private String username;
    private Integer age;
    private String email;

    // 无参构造、全参构造、getter/setter、toString
    public User() {}
    public User(Long id, String username, Integer age, String email) {
        this.id = id;
        this.username = username;
        this.age = age;
        this.email = email;
    }
    // getter/setter 省略
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + ''' +
                ", age=" + age +
                ", email='" + email + ''' +
                '}';
    }
}

4. Mapper 接口(UserMapper.java)

java

运行

less 复制代码
public interface UserMapper {
    // 根据ID查询用户
    User selectUserById(Long id);
    // 新增用户
    int insertUser(User user);
    // 更新用户
    int updateUser(User user);
    // 删除用户
    int deleteUser(Long id);
    // 条件查询(动态SQL)
    List<User> selectUserByCondition(@Param("username") String username, @Param("age") Integer age);
}

5. Mapper XML 文件(UserMapper.xml)

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">
    <!-- 结果映射(字段名与属性名不一致时使用) -->
    <resultMap id="UserResultMap" type="com.example.entity.User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="age" property="age"/>
        <result column="email" property="email"/>
    </resultMap>

    <!-- 查询单个 -->
    <select id="selectUserById" resultMap="UserResultMap">
        SELECT id, username, age, email FROM user WHERE id = #{id}
    </select>

    <!-- 新增 -->
    <insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO user (username, age, email) VALUES (#{username}, #{age}, #{email})
    </insert>

    <!-- 更新 -->
    <update id="updateUser">
        UPDATE user
        <set>
            <if test="username != null and username != ''">username = #{username},</if>
            <if test="age != null">age = #{age},</if>
            <if test="email != null and email != ''">email = #{email}</if>
        </set>
        WHERE id = #{id}
    </update>

    <!-- 删除 -->
    <delete id="deleteUser">
        DELETE FROM user WHERE id = #{id}
    </delete>

    <!-- 动态条件查询 -->
    <select id="selectUserByCondition" resultMap="UserResultMap">
        SELECT id, username, age, email FROM user
        <where>
            <if test="username != null and username != ''">AND username LIKE CONCAT('%', #{username}, '%')</if>
            <if test="age != null">AND age = #{age}</if>
        </where>
    </select>
</mapper>

6. 工具类(MyBatisUtil.java)

java

运行

java 复制代码
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;

public class MyBatisUtil {
    private static SqlSessionFactory sqlSessionFactory;

    // 初始化 SqlSessionFactory
    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

7. 测试类

java

运行

java 复制代码
import com.example.entity.User;
import com.example.mapper.UserMapper;
import org.apache.ibatis.session.SqlSession;
import java.util.List;

public class MyBatisTest {
    public static void main(String[] args) {
        // 1. 获取 SqlSession
        try (SqlSession session = MyBatisUtil.getSqlSession()) {
            UserMapper userMapper = session.getMapper(UserMapper.class);

            // 新增用户
            User user = new User(null, "zhangsan", 20, "zhangsan@163.com");
            userMapper.insertUser(user);
            System.out.println("新增用户ID:" + user.getId());

            // 查询单个用户
            User queryUser = userMapper.selectUserById(user.getId());
            System.out.println("查询到的用户:" + queryUser);

            // 更新用户
            queryUser.setAge(21);
            userMapper.updateUser(queryUser);
            System.out.println("更新后的用户:" + userMapper.selectUserById(user.getId()));

            // 条件查询
            List<User> userList = userMapper.selectUserByCondition("zhang", null);
            System.out.println("条件查询结果:" + userList);

            // 删除用户
            userMapper.deleteUser(user.getId());
            System.out.println("删除后查询:" + userMapper.selectUserById(user.getId()));
        }
    }
}

8. Spring Boot 整合 MyBatis(企业主流)

(1)application.yml 配置

yaml

yaml 复制代码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  mapper-locations: classpath:mapper/*.xml  # Mapper XML 路径
  type-aliases-package: com.example.entity  # 实体类别名包
  configuration:
    map-underscore-to-camel-case: true  # 开启下划线转驼峰

(2)启动类添加注解

java

运行

typescript 复制代码
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.mapper")  // 扫描 Mapper 接口
public class MyBatisApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyBatisApplication.class, args);
    }
}

(3)Service 层调用

java

运行

typescript 复制代码
import com.example.entity.User;
import com.example.mapper.UserMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;

@Service
public class UserService {
    @Resource
    private UserMapper userMapper;

    public User getUserById(Long id) {
        return userMapper.selectUserById(id);
    }

    public int addUser(User user) {
        return userMapper.insertUser(user);
    }

    public List<User> getUsersByCondition(String username, Integer age) {
        return userMapper.selectUserByCondition(username, age);
    }
}

二、企业高频面试题

基础概念

  1. MyBatis 是什么?它和 JDBC、Hibernate 的区别?

    • 答:MyBatis 是半自动化 ORM 框架,简化 JDBC 操作,支持自定义 SQL;

      • JDBC:手动写 SQL、处理连接、结果集,代码冗余;
      • Hibernate:全自动化 ORM,无需写 SQL,适配多数据库,但复杂 SQL 优化困难;
      • MyBatis:平衡灵活性和开发效率,支持自定义 SQL,适合复杂业务场景。
  2. MyBatis 的核心组件有哪些?

    • 答:

      • SqlSessionFactory:创建 SqlSession 的工厂,全局唯一;
      • SqlSession:与数据库的会话,封装 CRUD 操作;
      • Mapper 接口:映射 SQL 的接口,MyBatis 动态代理实现;
      • Configuration:MyBatis 核心配置类,存储全局配置;
      • Executor:执行器,处理 SQL 执行(Simple/Reuse/Batch)。
  3. MyBatis 中 #{} 和 ${} 的区别?

    • 答:

      • #{}:参数预编译,防止 SQL 注入,底层用 PreparedStatement;
      • ${}:字符串拼接,直接替换参数,有注入风险,适用于表名 / 列名动态替换(如 ORDER BY ${column})。

映射与 SQL

  1. MyBatis 如何解决字段名与实体类属性名不一致的问题?

    • 答:

      • 方式 1:SQL 中使用别名(SELECT user_name AS username FROM user);
      • 方式 2:使用 <resultMap> 手动映射字段与属性;
      • 方式 3:开启全局配置 map-underscore-to-camel-case: true(下划线转驼峰)。
  2. MyBatis 的动态 SQL 有哪些标签?

    • 答:<if><where><set><choose>/<when>/<otherwise><foreach><trim> 等,用于动态拼接 SQL,适配多条件查询、批量操作等场景。
  3. MyBatis 如何实现分页?

    • 答:

      • 基础方式:SQL 中拼接 LIMIT #{offset}, #{size};
      • 插件方式:使用 PageHelper 插件(企业主流),自动拦截 SQL 并添加分页条件;
      • 手动分页:查询总数 + 分页查询结果。
  4. MyBatis 中的一级缓存和二级缓存有什么区别?

    • 答:

      • 一级缓存:SqlSession 级别,默认开启,同一个 SqlSession 内查询相同 SQL 会缓存结果,关闭 SqlSession 缓存失效;
      • 二级缓存:Mapper 级别,需手动开启(<cache/>),多个 SqlSession 共享缓存,查询结果需实现 Serializable 接口;
      • 注意:二级缓存慎用,多表关联查询易导致数据不一致。

高级应用

  1. MyBatis 如何实现多表关联查询(一对一、一对多)?

    • 答:

      • 一对一:使用 <association> 标签,映射关联对象(如 User 关联 Order);

      • 一对多:使用 <collection> 标签,映射关联集合(如 User 关联多个 Order);

      • 示例:

        xml

        xml 复制代码
        <resultMap id="UserOrderMap" type="User">
            <id column="user_id" property="id"/>
            <result column="username" property="username"/>
            <!-- 一对多 -->
            <collection property="orders" ofType="Order">
                <id column="order_id" property="id"/>
                <result column="order_no" property="orderNo"/>
            </collection>
        </resultMap>
  2. MyBatis 的插件原理是什么?如何自定义插件?

    • 答:

      • 原理:基于 JDK 动态代理,拦截四大对象(Executor、StatementHandler、ParameterHandler、ResultSetHandler)的方法;

      • 自定义插件步骤:

        1. 实现 Interceptor 接口,重写 intercept() 方法;
        2. 添加 @Intercepts 注解指定拦截的方法;
        3. 配置插件到 MyBatis 核心配置中。
  3. MyBatis 中如何处理事务?

    • 答:

      • 原生 MyBatis:通过 SqlSession 控制(openSession(false) 手动提交,commit()/rollback());
      • Spring 整合:使用 Spring 事务管理(@Transactional 注解),MyBatis 适配 Spring 事务管理器。
  4. 企业中如何优化 MyBatis 性能?

    • 答:

      • 合理使用缓存(一级缓存为主,二级缓存慎用);
      • 避免 N+1 查询问题(使用关联查询、延迟加载);
      • 批量操作使用 foreach + Batch 执行器;
      • 关闭不必要的日志(如 DEBUG 级别);
      • 使用 ResultMap 代替 ResultType,减少字段映射开销;
      • 分页查询限制返回行数,避免全表扫描。

整合与实战

  1. Spring Boot 整合 MyBatis 的核心步骤?

    • 答:

      1. 引入 MyBatis Starter 依赖;
      2. 配置数据源和 MyBatis 核心参数(mapper 路径、别名等);
      3. 使用 @MapperScan 扫描 Mapper 接口;
      4. 编写 Mapper 接口和 XML(或注解);
      5. Service 层注入 Mapper 调用。
  2. MyBatis 注解开发和 XML 开发的优缺点?

    • 答:

      • 注解:简洁,适合简单 SQL,无需维护 XML;缺点:复杂 SQL、动态 SQL 可读性差,不易调试;
      • XML:适合复杂 SQL、动态 SQL,易维护、易调试;缺点:文件较多,配置稍繁琐;
      • 企业实践:简单 CRUD 用注解,复杂 SQL 用 XML。

三、企业应用场景

1. 核心业务开发(所有后端项目)

  • 场景:用户模块、订单模块、商品模块等基础 CRUD 操作;
  • 价值:替代原生 JDBC,减少重复代码,自定义 SQL 适配业务需求(如复杂条件查询、联表查询)。

2. 动态条件查询(电商 / 后台管理系统)

  • 场景:电商平台的商品筛选(价格区间、分类、销量排序)、后台管理系统的用户列表查询(多条件组合);
  • 实现 :使用 <if><where><choose> 等动态 SQL 标签,按需拼接查询条件。

3. 批量操作(数据导入 / 订单批量处理)

  • 场景:Excel 数据导入(批量新增用户 / 商品)、订单批量发货、批量删除无效数据;

  • 实现

    xml

    sql 复制代码
    <insert id="batchInsertUser">
        INSERT INTO user (username, age, email) VALUES
        <foreach collection="list" item="item" separator=",">
            (#{item.username}, #{item.age}, #{item.email})
        </foreach>
    </insert>

4. 多表关联查询(复杂业务场景)

  • 场景:电商订单详情(关联用户、商品、地址表)、财务报表(关联订单、支付、退款表);
  • 实现 :使用 <association>(一对一)、<collection>(一对多)实现关联映射,避免多次查询。

5. 分页查询(列表展示)

  • 场景:商品列表、订单列表、用户列表等分页展示;

  • 实现:结合 PageHelper 插件,一行代码实现分页:

    java

    运行

    ini 复制代码
    PageHelper.startPage(1, 10); // 第1页,每页10条
    List<User> userList = userMapper.selectUserByCondition(null, null);
    PageInfo<User> pageInfo = new PageInfo<>(userList);

6. 存储过程调用(金融 / 传统行业)

  • 场景:金融行业的计息计算、传统行业的报表统计(存储过程封装复杂逻辑);

  • 实现

    xml

    sql 复制代码
    <select id="callProcedure" statementType="CALLABLE">
        {CALL get_user_count(#{count, mode=OUT, jdbcType=INTEGER})}
    </select>

7. 读写分离(高并发场景)

  • 场景:电商秒杀、直播带货等高并发场景,读请求走从库,写请求走主库;
  • 实现:结合 MyCat/Sharding-JDBC 中间件,MyBatis 配置多数据源,通过插件动态切换数据源。

8. 自定义插件(通用功能扩展)

  • 场景:SQL 执行耗时监控、数据权限过滤(如按部门过滤数据)、防全表更新 / 删除;

  • 示例:自定义插件拦截 SQL,禁止不带 WHERE 条件的 UPDATE/DELETE:

    java

    运行

    less 复制代码
    @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
    public class SqlInterceptor implements Interceptor {
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
            BoundSql boundSql = statementHandler.getBoundSql();
            String sql = boundSql.getSql().trim().toUpperCase();
            if ((sql.startsWith("UPDATE") || sql.startsWith("DELETE")) && !sql.contains("WHERE")) {
                throw new RuntimeException("禁止执行不带WHERE条件的UPDATE/DELETE语句");
            }
            return invocation.proceed();
        }
    }

总结

MyBatis 是企业开发中最常用的持久层框架之一,核心优势是灵活的 SQL 控制低入侵性。掌握其核心语法(动态 SQL、结果映射)、缓存机制、性能优化技巧,以及与 Spring Boot 的整合,是应对企业面试和实际开发的关键。在企业场景中,需根据业务复杂度选择 XML 或注解开发,结合分页、批量操作、关联查询等特性,提升开发效率和系统性能。

相关推荐
嘉伟桑1 小时前
Python调用电价API返回JSON示例:分时电价、现货电价和字段解析
后端
武子康1 小时前
Shippy:确定性工具、会话级 Sandbox 与 Live-Data Eval(4 类收敛 + 7 步实现方案 + 6 类评测指标)
前端·人工智能·后端
我叫黑大帅1 小时前
我为什么单一消费者的场景下,要用 Redis List 当消息队列?
redis·后端·面试
AskHarries2 小时前
文件上传系统
后端
止语Lab3 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活3 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困3 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn3 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
用户298698530143 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel
用户77283104908403 小时前
krono-job:零侵入、单二进制交付的分布式任务调度平台(开源)
后端