MyBatis 基础配置与执行流程全解析(从入门到实践)
MyBatis 作为 Java 生态中轻量级且灵活的持久层框架,其核心是通过配置文件解耦 SQL 与业务代码,同时简化 JDBC 冗余操作。配置细节优化、执行流程原理、工程化实践及常见问题排查,帮助你彻底掌握 MyBatis 的基础使用与核心逻辑。
配置文件:从基础到优化
MyBatis 的配置体系分为「全局配置文件(mybatis-config.xml
)」和「映射文件(XxxMapper.xml
)」,两者分工明确 —— 全局配置负责框架级参数,映射文件负责业务 SQL 与实体映射。
全局配置文件(mybatis-config.xml
)深度解读
(1)全局配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| <?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> <properties resource="db.properties"> <property name="jdbc.username" value="default_root"/> </properties>
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="logImpl" value="STDOUT_LOGGING"/> <setting name="cacheEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> </settings>
<typeAliases> <typeAlias type="com.zhanghe.study.mybatis.model.User" alias="User"/> <package name="com.zhanghe.study.mybatis.model"/> </typeAliases>
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="poolMaximumActiveConnections" value="20"/> <property name="poolMaximumIdleConnections" value="5"/> </dataSource> </environment> <environment id="production"> <transactionManager type="MANAGED"/> <dataSource type="POOLED"> <property name="driver" value="${prod.jdbc.driver}"/> <property name="url" value="${prod.jdbc.url}"/> <property name="username" value="${prod.jdbc.username}"/> <property name="password" value="${prod.jdbc.password}"/> </dataSource> </environment> </environments>
<mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers> </configuration>
|
(2)外部属性文件(db.properties
)
将数据库配置单独抽取到 src/main/resources/db.properties
,便于维护和环境切换:
1 2 3 4 5 6 7 8 9 10 11
| jdbc.driver=com.mysql.cj.jdbc.Driver # MySQL 8.0+需用cj驱动,5.x用com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&characterEncoding=utf8 jdbc.username=root jdbc.password=123456
prod.jdbc.driver=com.mysql.cj.jdbc.Driver prod.jdbc.url=jdbc:mysql://192.168.1.100:3306/prod_db?useSSL=true&serverTimezone=UTC prod.jdbc.username=prod_user prod.jdbc.password=prod_123456
|
映射文件(UserMapper.xml
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <?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.zhanghe.study.mybatis.mapper.UserMapper"> <cache eviction="LRU" flushInterval="60000" size="1024" readOnly="true"/>
<resultMap id="UserResultMap" type="User"> <id column="user_id" property="userId"/> <result column="user_name" property="userName"/> <result column="user_age" property="userAge"/> <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/> </resultMap>
<select id="selectUser" parameterType="Integer" resultMap="UserResultMap"> SELECT user_id, user_name, user_age, create_time FROM users WHERE user_id = #{id} </select>
<select id="selectUserByNameAndAge" resultMap="UserResultMap"> SELECT * FROM users WHERE user_name = #{name} AND user_age = #{age} </select>
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="userId"> INSERT INTO users (user_name, user_age, create_time) VALUES (#{userName}, #{userAge}, #{createTime}) </insert> </mapper>
|
对应的 Mapper 接口(UserMapper.java
)
接口方法需与映射文件的 id
、参数类型、返回值类型完全匹配:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.zhanghe.study.mybatis.mapper;
import com.zhanghe.study.mybatis.model.User; import org.apache.ibatis.annotations.Param;
public interface UserMapper { User selectUser(Integer userId); User selectUserByNameAndAge(@Param("name") String userName, @Param("age") Integer userAge); int insertUser(User user); }
|
执行流程:从配置加载到 SQL 执行
MyBatis 的执行流程可拆解为「配置加载→核心对象创建→SQL 执行→资源释放」四步,每一步都有明确的职责和生命周期管理要求。
核心流程拆解
(1)步骤 1:加载配置,创建 SqlSessionFactory
SqlSessionFactory
是 MyBatis 的 “工厂”,负责创建 SqlSession
,其生命周期是全局唯一(应用启动时创建一次,避免重复创建导致连接泄漏)。
1 2 3 4 5 6 7 8 9 10 11
| public static SqlSessionFactory createFactory() { try { InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); return new SqlSessionFactoryBuilder().build(is); } catch (IOException e) { throw new RuntimeException("MyBatis配置文件加载失败!", e); } }
|
关键原理:
SqlSessionFactoryBuilder
会解析 mybatis-config.xml
和所有 Mapper.xml
,将配置信息封装到 Configuration
对象中,最终基于 Configuration
创建 SqlSessionFactory
(默认实现是 DefaultSqlSessionFactory
)。
(2)步骤 2:创建 SqlSession
SqlSession
相当于 JDBC 的 Connection
,代表一次数据库会话,生命周期是单次业务请求(线程不安全,需及时关闭)。
1 2 3 4 5 6 7 8 9 10 11 12 13
| SqlSession session = sqlSessionFactory.openSession(true);
SqlSession session = sqlSessionFactory.openSession(); try { session.commit(); } catch (Exception e) { session.rollback(); } finally { session.close(); }
|
核心注意事项:
SqlSession
线程不安全,不可跨线程共享;
- 必须在
finally
块中关闭 SqlSession
,避免数据库连接泄漏;
- 手动提交模式下,未调用
commit()
会导致事务未提交,数据不生效。
(3)步骤 3:执行 SQL(新旧版本对比)
MyBatis 提供两种执行 SQL 的方式,新版本的「Mapper 接口代理」是主流(类型安全、可读性强)。
执行方式 |
代码示例 |
优缺点对比 |
老版本(SqlSession 直接调用) |
User user = session.selectOne("com.zhanghe.study.mybatis.mapper.UserMapper.selectUser", 2); |
优点:无需定义接口; 缺点:字符串硬编码(易写错)、无类型校验、可读性差。 |
新版本(Mapper 接口代理) |
UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUser(2); |
优点:类型安全(编译期校验)、代码简洁、可维护性强; 缺点:需定义接口。 |
原理补充:
MyBatis 会为 Mapper 接口动态生成代理对象(基于 JDK 动态代理),调用接口方法时,代理对象会解析 namespace + methodName
找到对应的 SQL,执行后返回结果。
(4)步骤 4:资源释放
无论 SQL 执行成功或失败,都需关闭 SqlSession
,释放数据库连接(归还到连接池):
1 2 3 4 5 6 7 8 9 10 11
| SqlSession session = null; try { session = sqlSessionFactory.openSession(true); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUser(2); System.out.println(user); } finally { if (session != null) { session.close(); } }
|
工程化优化:工具类封装
为避免重复代码,推荐封装 MyBatisUtil
工具类,统一管理 SqlSessionFactory
和 SqlSession
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package com.zhanghe.study.mybatis.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 is = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); } catch (IOException e) { throw new ExceptionInInitializerError("MyBatis初始化失败!", e); } }
public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(true); }
public static void closeSqlSession(SqlSession session) { if (session != null) { session.close(); } } }
|
工具类使用示例:
1 2 3 4 5 6 7 8 9 10
| SqlSession session = null; try { session = MyBatisUtil.getSqlSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUser(2); System.out.println(user); } finally { MyBatisUtil.closeSqlSession(session); }
|
常见问题与解决方案
1. 映射文件找不到(Invalid bound statement (not found)
)
- 原因 1:
mybatis-config.xml
的 <mappers>
配置路径错误(如 resource
路径不是 classpath 下的路径);
- 原因 2:Mapper 接口与映射文件不同包同名(如接口在
com.mapper
,映射文件需在 resources/com/mapper
下且名为 UserMapper.xml
);
- 解决方案:使用
<package name="com.zhanghe.study.mybatis.mapper"/>
批量扫描 Mapper 接口,确保接口与映射文件同包同名。
2. 驼峰命名不生效(数据库字段 user_name
→ 实体属性 userName
为 null
)
3. SQL 注入风险(使用 ${}
占位符)
- 原因:
${}
是字符串直接拼接(如 ${id}
→ 替换为参数值,无预编译),#{}
是预编译占位符(推荐);
- 解决方案:除动态表名 / 排序字段(如
ORDER BY ${field}
)外,全部使用 #{}
。
4. SqlSession
未关闭导致连接泄漏
- 原因:未在
finally
块中关闭 SqlSession
,异常时资源未释放;
- 解决方案:使用工具类的
closeSqlSession
方法,在 finally
中强制关闭。
总结
MyBatis 的基础使用核心是 “配置 + 接口 + 映射”:
- 配置文件:全局配置管理数据库和框架参数,映射文件定义 SQL 和实体映射;
- 执行流程:
SqlSessionFactory
(全局唯一)→ SqlSession
(单次会话)→ Mapper 代理(执行 SQL);
- 最佳实践:封装工具类管理核心对象、开启驼峰映射简化配置、使用接口代理避免硬编码
v1.3.10