学习笔记: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 或注解开发,结合分页、批量操作、关联查询等特性,提升开发效率和系统性能。

相关推荐
前端小张同学3 小时前
餐饮小程序需要你们
java·前端·后端
王中阳Go4 小时前
都2026年了,PHP还纠结转Go还是Java呢?安利一个无缝迁移的框架~
java·后端·go
老华带你飞4 小时前
二手商城|基于springboot 二手商城系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
Tadas-Gao4 小时前
GraphQL:下一代API架构的设计哲学与实践创新
java·分布式·后端·微服务·架构·graphql
老华带你飞4 小时前
酒店预约|基于springboot 酒店预约系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
dob4 小时前
为什么你的if-else越写越乱?聊聊状态机
后端
古城小栈4 小时前
Spring Boot + 边缘 GenAI:智能座舱应用开发实战
java·spring boot·后端
开心就好20254 小时前
使用 Ipa Guard 应对 App Store 4.3 风险的一些实践
后端
想用offer打牌4 小时前
一站式了解数据库三大范式(库表设计基础)
数据库·后端·面试