一、核心代码示例
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&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);
}
}
二、企业高频面试题
基础概念
-
MyBatis 是什么?它和 JDBC、Hibernate 的区别?
-
答:MyBatis 是半自动化 ORM 框架,简化 JDBC 操作,支持自定义 SQL;
- JDBC:手动写 SQL、处理连接、结果集,代码冗余;
- Hibernate:全自动化 ORM,无需写 SQL,适配多数据库,但复杂 SQL 优化困难;
- MyBatis:平衡灵活性和开发效率,支持自定义 SQL,适合复杂业务场景。
-
-
MyBatis 的核心组件有哪些?
-
答:
SqlSessionFactory:创建 SqlSession 的工厂,全局唯一;SqlSession:与数据库的会话,封装 CRUD 操作;Mapper 接口:映射 SQL 的接口,MyBatis 动态代理实现;Configuration:MyBatis 核心配置类,存储全局配置;Executor:执行器,处理 SQL 执行(Simple/Reuse/Batch)。
-
-
MyBatis 中 #{} 和 ${} 的区别?
-
答:
#{}:参数预编译,防止 SQL 注入,底层用 PreparedStatement;${}:字符串拼接,直接替换参数,有注入风险,适用于表名 / 列名动态替换(如 ORDER BY ${column})。
-
映射与 SQL
-
MyBatis 如何解决字段名与实体类属性名不一致的问题?
-
答:
- 方式 1:SQL 中使用别名(
SELECT user_name AS username FROM user); - 方式 2:使用
<resultMap>手动映射字段与属性; - 方式 3:开启全局配置
map-underscore-to-camel-case: true(下划线转驼峰)。
- 方式 1:SQL 中使用别名(
-
-
MyBatis 的动态 SQL 有哪些标签?
- 答:
<if>、<where>、<set>、<choose>/<when>/<otherwise>、<foreach>、<trim>等,用于动态拼接 SQL,适配多条件查询、批量操作等场景。
- 答:
-
MyBatis 如何实现分页?
-
答:
- 基础方式:SQL 中拼接 LIMIT #{offset}, #{size};
- 插件方式:使用 PageHelper 插件(企业主流),自动拦截 SQL 并添加分页条件;
- 手动分页:查询总数 + 分页查询结果。
-
-
MyBatis 中的一级缓存和二级缓存有什么区别?
-
答:
- 一级缓存:SqlSession 级别,默认开启,同一个 SqlSession 内查询相同 SQL 会缓存结果,关闭 SqlSession 缓存失效;
- 二级缓存:Mapper 级别,需手动开启(
<cache/>),多个 SqlSession 共享缓存,查询结果需实现 Serializable 接口; - 注意:二级缓存慎用,多表关联查询易导致数据不一致。
-
高级应用
-
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>
-
-
-
MyBatis 的插件原理是什么?如何自定义插件?
-
答:
-
原理:基于 JDK 动态代理,拦截四大对象(Executor、StatementHandler、ParameterHandler、ResultSetHandler)的方法;
-
自定义插件步骤:
- 实现
Interceptor接口,重写intercept()方法; - 添加
@Intercepts注解指定拦截的方法; - 配置插件到 MyBatis 核心配置中。
- 实现
-
-
-
MyBatis 中如何处理事务?
-
答:
- 原生 MyBatis:通过
SqlSession控制(openSession(false)手动提交,commit()/rollback()); - Spring 整合:使用 Spring 事务管理(
@Transactional注解),MyBatis 适配 Spring 事务管理器。
- 原生 MyBatis:通过
-
-
企业中如何优化 MyBatis 性能?
-
答:
- 合理使用缓存(一级缓存为主,二级缓存慎用);
- 避免 N+1 查询问题(使用关联查询、延迟加载);
- 批量操作使用
foreach+ Batch 执行器; - 关闭不必要的日志(如 DEBUG 级别);
- 使用 ResultMap 代替 ResultType,减少字段映射开销;
- 分页查询限制返回行数,避免全表扫描。
-
整合与实战
-
Spring Boot 整合 MyBatis 的核心步骤?
-
答:
- 引入 MyBatis Starter 依赖;
- 配置数据源和 MyBatis 核心参数(mapper 路径、别名等);
- 使用
@MapperScan扫描 Mapper 接口; - 编写 Mapper 接口和 XML(或注解);
- Service 层注入 Mapper 调用。
-
-
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
运行
iniPageHelper.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 或注解开发,结合分页、批量操作、关联查询等特性,提升开发效率和系统性能。