1. 为什么选择MyBatis作为首个ORM框架
刚接触Java持久层开发时,我面对Hibernate、JPA和MyBatis三大主流框架犹豫不决。最终选择MyBatis作为入门框架,主要基于三点考量:
首先,MyBatis的学习曲线最为平缓。相比Hibernate的全自动ORM机制,MyBatis采用半自动映射方式,开发者需要手动编写SQL但不必处理JDBC底层细节。这种"适度封装"的设计让新手既能理解数据库操作本质,又不会被繁琐的JDBC代码困扰。
其次,SQL可见性带来更好的调试体验。在开发初期,能看到完整SQL语句对排查问题至关重要。MyBatis的XML映射文件将SQL可视化,配合日志输出,可以清晰观察每个操作对应的实际SQL语句。
最后,MyBatis在国内企业应用广泛。根据2023年开发者调查报告,超过62%的Java项目采用MyBatis作为持久层框架,这意味着掌握MyBatis能获得更多实际工作机会。
提示:虽然MyBatis Plus提供了更多便捷功能,但建议初学者先掌握原生MyBatis核心概念,再过渡到增强版工具。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境准备与项目搭建
2.1 基础环境配置
我推荐使用以下环境组合开始MyBatis之旅:
- JDK 1.8+(目前最稳定的Java版本)
- Maven 3.6.3(管理项目依赖)
- IntelliJ IDEA(社区版即可)
- MySQL 5.7(或H2内存数据库)
创建Maven项目时,pom.xml需要添加的关键依赖:
xml复制<dependencies>
<!-- MyBatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- 日志框架 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
</dependencies>
2.2 数据库初始化
在MySQL中创建测试数据库和表:
sql复制CREATE DATABASE mybatis_demo;
USE mybatis_demo;
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `user` (`name`, `email`) VALUES
('张三', 'zhangsan@example.com'),
('李四', 'lisi@example.com');
3. 核心配置详解
3.1 mybatis-config.xml配置
在resources目录下创建MyBatis主配置文件:
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>
<settings>
<!-- 开启下划线到驼峰自动转换 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 打印SQL日志 -->
<setting name="logImpl" value="SLF4J"/>
</settings>
<typeAliases>
<package name="com.example.model"/>
</typeAliases>
<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/mybatis_demo?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
3.2 实体类与Mapper接口
创建用户实体类:
java复制package com.example.model;
import java.util.Date;
public class User {
private Integer id;
private String name;
private String email;
private Date createTime;
// 省略getter/setter和toString方法
}
定义Mapper接口:
java复制package com.example.mapper;
import com.example.model.User;
import java.util.List;
public interface UserMapper {
User selectById(Integer id);
List<User> selectAll();
int insert(User user);
int update(User user);
int delete(Integer id);
}
4. XML映射文件编写
4.1 基础CRUD映射
在resources/mapper目录下创建UserMapper.xml:
xml复制<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="email" column="email"/>
<result property="createTime" column="create_time"/>
</resultMap>
<select id="selectById" resultMap="userResultMap">
SELECT * FROM user WHERE id = #{id}
</select>
<select id="selectAll" resultMap="userResultMap">
SELECT * FROM user
</select>
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user(name, email)
VALUES(#{name}, #{email})
</insert>
<update id="update" parameterType="User">
UPDATE user SET
name = #{name},
email = #{email}
WHERE id = #{id}
</update>
<delete id="delete">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>
4.2 动态SQL示例
MyBatis强大的动态SQL功能:
xml复制<select id="selectByCondition" parameterType="map" resultMap="userResultMap">
SELECT * FROM user
<where>
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="email != null">
AND email = #{email}
</if>
<if test="startTime != null and endTime != null">
AND create_time BETWEEN #{startTime} AND #{endTime}
</if>
</where>
ORDER BY create_time DESC
</select>
5. 实际应用与测试
5.1 工具类封装
创建MyBatis工具类:
java复制package com.example.util;
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;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
throw new RuntimeException("初始化MyBatis失败", e);
}
}
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
5.2 测试用例编写
编写JUnit测试类:
java复制package com.example.test;
import com.example.mapper.UserMapper;
import com.example.model.User;
import com.example.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import java.util.List;
class MyBatisTest {
@Test
void testCRUD() {
try (SqlSession session = MyBatisUtil.getSqlSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
// 插入测试
User newUser = new User();
newUser.setName("王五");
newUser.setEmail("wangwu@example.com");
mapper.insert(newUser);
System.out.println("新增用户ID:" + newUser.getId());
// 查询测试
User user = mapper.selectById(1);
System.out.println("查询结果:" + user);
// 更新测试
user.setEmail("new_email@example.com");
mapper.update(user);
// 查询所有
List<User> users = mapper.selectAll();
users.forEach(System.out::println);
session.commit(); // 提交事务
}
}
}
6. 常见问题解决方案
6.1 SQL日志打印配置
在logback.xml中添加MyBatis日志配置:
xml复制<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.example.mapper" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
6.2 分页查询实现
使用RowBounds实现简单分页:
java复制@Test
void testPagination() {
try (SqlSession session = MyBatisUtil.getSqlSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
int pageNum = 2;
int pageSize = 10;
int offset = (pageNum - 1) * pageSize;
List<User> users = mapper.selectAll(new RowBounds(offset, pageSize));
users.forEach(System.out::println);
}
}
6.3 批量操作处理
批量插入示例:
java复制@Insert("<script>" +
"INSERT INTO user(name, email) VALUES " +
"<foreach collection='list' item='user' separator=','>" +
"(#{user.name}, #{user.email})" +
"</foreach>" +
"</script>")
void batchInsert(List<User> users);
7. 进阶技巧与最佳实践
7.1 结果集映射技巧
处理复杂结果集映射:
xml复制<resultMap id="detailedUserResultMap" type="User">
<constructor>
<idArg column="id" javaType="int"/>
<arg column="name" javaType="String"/>
</constructor>
<result property="email" column="email"/>
<association property="department" javaType="Department">
<id property="id" column="dept_id"/>
<result property="name" column="dept_name"/>
</association>
<collection property="roles" ofType="Role">
<id property="id" column="role_id"/>
<result property="name" column="role_name"/>
</collection>
</resultMap>
7.2 缓存配置优化
二级缓存配置示例:
xml复制<cache eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
7.3 插件开发示例
自定义分页插件:
java复制@Intercepts(@Signature(type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}))
public class PaginationInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler handler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = SystemMetaObject.forObject(handler);
// 获取分页参数
RowBounds rowBounds = (RowBounds) metaObject.getValue("delegate.rowBounds");
if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
return invocation.proceed();
}
// 修改原始SQL
String originalSql = (String) metaObject.getValue("delegate.boundSql.sql");
String limitSql = originalSql + " LIMIT " + rowBounds.getOffset() + "," + rowBounds.getLimit();
metaObject.setValue("delegate.boundSql.sql", limitSql);
// 重置RowBounds
metaObject.setValue("delegate.rowBounds", RowBounds.DEFAULT);
return invocation.proceed();
}
}
在实际项目中,我发现合理使用MyBatis的延迟加载特性能显著提升复杂查询性能。对于一对多关联查询,建议将fetchType设置为lazy,只有在真正访问关联对象时才触发查询。同时,要注意事务边界的控制,确保在Session关闭前完成所有延迟加载操作。
