1. 项目概述:20分钟掌握MyBatis核心用法
作为Java生态中最受欢迎的ORM框架之一,MyBatis凭借其灵活的SQL管理方式和简洁的API设计,成为企业级开发的标准配置。与Hibernate等全自动ORM框架不同,MyBatis采用半自动化模式,开发者既能享受对象关系映射的便利,又能精准控制SQL语句的执行细节。根据2024年最新统计,超过78%的中大型Java项目选择MyBatis作为持久层解决方案,特别是在需要复杂SQL优化的金融、电商领域表现尤为突出。
本教程专为具备Java基础但尚未系统学习MyBatis的开发者设计,通过精心设计的知识递进路径,帮助你在20分钟内快速建立MyBatis知识框架。我们将从环境搭建开始,逐步深入到CRUD操作、动态SQL编写等核心功能,最后分享实际项目中的性能优化技巧。不同于官方文档的全面铺陈,这里只聚焦最常用的20%功能,却能解决80%的实际开发问题。
2. 环境准备与基础配置
2.1 项目依赖配置
在Maven项目中引入MyBatis核心依赖(当前最新稳定版3.5.19):
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.19</version> </dependency>如果使用Spring Boot进行集成,推荐直接使用mybatis-spring-boot-starter:
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency>注意:MyBatis 3.5.x版本需要JDK 1.8+环境,如果项目中出现"源发行版17需要目标发行版17"的警告,请检查pom.xml中<maven.compiler.source>和<maven.compiler.target>的配置是否与本地JDK版本一致。
2.2 基础配置文件示例
在resources目录下创建mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <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"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers> </configuration>3. 核心操作快速上手
3.1 基础CRUD实现
首先定义实体类User.java:
public class User { private Long id; private String username; private String email; // 省略getter/setter }创建UserMapper接口:
public interface UserMapper { User selectUserById(Long id); List<User> selectAllUsers(); int insertUser(User user); int updateUser(User user); int deleteUser(Long id); }对应的UserMapper.xml映射文件:
<mapper namespace="com.example.mapper.UserMapper"> <select id="selectUserById" resultType="com.example.entity.User"> SELECT * FROM user WHERE id = #{id} </select> <insert id="insertUser" parameterType="com.example.entity.User" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user(username, email) VALUES(#{username}, #{email}) </insert> <!-- 其他CRUD操作类似 --> </mapper>3.2 动态SQL实战
MyBatis强大的动态SQL能力可以优雅地处理复杂查询条件:
<select id="selectUsersByCondition" resultType="User"> SELECT * FROM user <where> <if test="username != null"> AND username LIKE CONCAT('%', #{username}, '%') </if> <if test="email != null"> AND email = #{email} </if> <if test="ids != null and ids.size() > 0"> AND id IN <foreach item="id" collection="ids" open="(" separator="," close=")"> #{id} </foreach> </if> </where> ORDER BY id DESC </select>4. 高级特性与性能优化
4.1 关联查询处理
处理一对多关系时,可以使用collection标签:
<resultMap id="userWithOrdersMap" type="User"> <id property="id" column="user_id"/> <result property="username" column="username"/> <collection property="orders" ofType="Order"> <id property="id" column="order_id"/> <result property="orderNo" column="order_no"/> </collection> </resultMap> <select id="selectUserWithOrders" resultMap="userWithOrdersMap"> SELECT u.id as user_id, u.username, o.id as order_id, o.order_no FROM user u LEFT JOIN orders o ON u.id = o.user_id WHERE u.id = #{userId} </select>4.2 插件开发示例
实现一个简单的SQL执行时间统计插件:
@Intercepts({ @Signature(type= StatementHandler.class, method="query", args={Statement.class, ResultHandler.class}) }) public class SqlCostTimeInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { long start = System.currentTimeMillis(); try { return invocation.proceed(); } finally { long cost = System.currentTimeMillis() - start; StatementHandler handler = (StatementHandler)invocation.getTarget(); System.out.println("SQL执行耗时[" + cost + "ms]: " + handler.getBoundSql().getSql()); } } }在配置文件中注册插件:
<plugins> <plugin interceptor="com.example.plugin.SqlCostTimeInterceptor"/> </plugins>5. 生产环境最佳实践
5.1 批量操作优化
使用BatchExecutor提升批量插入性能:
try(SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) { UserMapper mapper = session.getMapper(UserMapper.class); for(int i=0; i<1000; i++) { User user = new User("user"+i, "user"+i+"@example.com"); mapper.insertUser(user); if(i % 200 == 0) { session.flushStatements(); // 分批提交 } } session.commit(); }5.2 常见问题排查
问题1:association/collection映射结果为空
- 检查数据库字段名与resultMap中的column是否完全一致(注意大小写)
- 确认关联查询SQL是否正确返回了所有需要的字段
- 使用MyBatis日志级别调整为DEBUG查看实际执行的SQL
问题2:返回List 等基础类型集合
<select id="selectAllIds" resultType="long"> SELECT id FROM user </select>问题3:内存溢出(OOM)处理
- 大数据量查询使用Cursor游标方式:
try(Cursor<User> cursor = userMapper.selectAllUsersWithCursor()) { cursor.forEach(user -> process(user)); }- 调整MyBatis的ExecutorType为REUSE
- 合理设置defaultFetchSize参数
6. 与MyBatis-Plus的选择建议
MyBatis-Plus在原生MyBatis基础上提供了更多开箱即用的功能:
| 特性 | MyBatis | MyBatis-Plus |
|---|---|---|
| 代码生成器 | 需插件 | 内置 |
| 通用Mapper | 无 | 支持 |
| 分页插件 | 需配置 | 内置 |
| 乐观锁 | 手动实现 | 注解支持 |
| 逻辑删除 | 手动实现 | 注解支持 |
对于新项目,特别是需要快速开发的场景,推荐使用MyBatis-Plus。但对于需要精细控制SQL或已有成熟MyBatis基础的项目,原生MyBatis仍是更灵活的选择。
在实际开发中,我发现合理使用MyBatis的缓存机制能显著提升性能。二级缓存虽然强大,但在分布式环境下需要特别小心,建议配合Redis等分布式缓存实现。对于查询频率高但更新少的配置类数据,可以设置flushInterval为1小时,既保证性能又不失数据时效性。