一、什么是 MyBatis
MyBatis 是一个优秀的持久层框架,它通过 XML 或注解的方式将 SQL 映射到 Java 方法。MyBatis 提供了对 SQL 的灵活控制,避免了冗长的 JDBC 代码,使开发者可以专注于业务逻辑。
二、MyBatis 执行流程概述
MyBatis 的执行流程可以划分为以下几个步骤:
- 初始化:加载配置文件,创建
SqlSessionFactory
。 - 获取 SqlSession:从
SqlSessionFactory
获取SqlSession
,作为操作数据库的核心对象。 - 执行 SQL:调用映射文件中的方法(Mapper 方法)。
- 结果处理:将数据库查询结果映射到 Java 对象。
- 关闭资源:释放
SqlSession
,避免资源泄露。
三、MyBatis 执行流程详细分析
1. 初始化阶段
初始化步骤:
- 加载主配置文件(
mybatis-config.xml
)。 - 加载映射文件(
Mapper.xml
)。 - 构建
SqlSessionFactory
。
// 加载 MyBatis 配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
关键点:
- 配置文件中定义了环境信息(如数据源)、插件等。
SqlSessionFactory
是 MyBatis 的核心对象,负责创建SqlSession
。
2. 获取 SqlSession
获取 SqlSession:
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
// 使用 SqlSession 操作数据库
}
作用:
SqlSession
是操作数据库的核心对象,封装了 JDBC 连接。- 提供了对数据库操作的基本方法,如
selectOne
、insert
等。
3. Mapper 接口绑定
步骤:
- 加载
Mapper.xml
中定义的 SQL 语句。 - 通过动态代理生成 Mapper 接口的实现类。
代码示例:
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(1);
工作原理:
- Mapper 接口与
Mapper.xml
配置文件一一对应。 - MyBatis 动态代理机制会拦截接口方法调用,将其映射到对应的 SQL 语句。
4. SQL 执行
SQL 执行分为以下步骤:
- 参数解析:解析方法传入的参数,替换到 SQL 中。
- SQL 执行:通过 JDBC 执行 SQL。
- 结果映射:将查询结果转换为 Java 对象。
代码示例:
// 参数解析
Map<String, Object> params = new HashMap<>();
params.put("id", 1);
// 执行查询
User user = sqlSession.selectOne("com.example.mapper.UserMapper.getUserById", params);
底层调用:
- MyBatis 通过
PreparedStatement
执行 SQL。 - 自动处理参数占位符(如
?
)并绑定实际参数。
5. 结果处理
MyBatis 提供了多种结果处理方式:
- 单行映射:将查询结果映射为 Java 对象。
- 多行映射:将查询结果映射为对象列表。
- 嵌套映射:处理复杂的关联关系。
代码示例:
// 映射单行结果
User user = sqlSession.selectOne("com.example.mapper.UserMapper.getUserById", 1);
// 映射多行结果
List<User> users = sqlSession.selectList("com.example.mapper.UserMapper.getAllUsers");
工作原理:
- MyBatis 通过反射将数据库字段与 Java 属性匹配。
- 如果字段名与属性名不同,可以通过
resultMap
显式定义映射关系。
6. 关闭资源
MyBatis 使用的是 手动事务管理,因此需要显式关闭资源:
代码示例:
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
// 操作数据库
sqlSession.commit(); // 提交事务
} catch (Exception e) {
sqlSession.rollback(); // 回滚事务
} finally {
sqlSession.close(); // 释放资源
}
四、MyBatis 执行流程源码解析
1. SqlSessionFactoryBuilder
创建 SqlSessionFactory
的过程:
public SqlSessionFactory build(InputStream inputStream) {
Configuration config = new XMLConfigBuilder(inputStream).parse();
return new DefaultSqlSessionFactory(config);
}
XMLConfigBuilder
会解析 XML 配置文件并创建 Configuration
对象。
2. 动态代理生成 Mapper 实现
public <T> T getMapper(Class<T> type) {
return mapperRegistry.getMapper(type, this);
}
MapperRegistry
使用 JDK 动态代理生成 Mapper 接口的实现类。
3. SQL 执行
SqlSession
的 selectOne
方法:
public <T> T selectOne(String statement, Object parameter) {
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result");
}
return null;
}
五、完整的 MyBatis 使用示例
1. 数据表结构
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
email VARCHAR(50)
);
2. Mapper 接口
public interface UserMapper {
User getUserById(int id);
List<User> getAllUsers();
void insertUser(User user);
}
3. Mapper XML 文件
UserMapper.xml
:
<mapper namespace="com.example.mapper.UserMapper">
<select id="getUserById" parameterType="int" resultType="com.example.model.User">
SELECT * FROM users WHERE id = #{id}
</select>
<select id="getAllUsers" resultType="com.example.model.User">
SELECT * FROM users
</select>
<insert id="insertUser" parameterType="com.example.model.User">
INSERT INTO users (username, email) VALUES (#{username}, #{email})
</insert>
</mapper>
4. 配置文件
mybatis-config.xml
:
<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/testdb"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/example/mapper/UserMapper.xml"/>
</mappers>
</configuration>
5. 测试代码
public class MyBatisTest {
public static void main(String[] args) throws IOException {
// 加载 MyBatis 配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 插入数据
User newUser = new User();
newUser.setUsername("John");
newUser.setEmail("john@example.com");
userMapper.insertUser(newUser);
// 查询数据
User user = userMapper.getUserById(1);
System.out.println("User: " + user);
// 查询所有用户
List<User> users = userMapper.getAllUsers();
users.forEach(System.out::println);
}
}
}
六、总结
MyBatis 的执行流程围绕 SqlSession 展开,从配置初始化、Mapper 方法绑定到 SQL 执行和结果映射,每一步都有明确的职责和机制。MyBatis 提供了灵活的 SQL 控制,同时通过动态代理和映射机制简化了开发工作。
关键点总结:
- 核心对象:
SqlSessionFactory
、SqlSession
。 - 动态代理:将 Mapper 接口绑定到 SQL。
- 执行过程:参数解析、SQL 执行、结果映射。
- 源码理解:掌握 MyBatis 的核心源码有助于更高效地解决问题。