0%

mybatis基本配置及执行

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>
<!-- 1. 引入外部属性文件(解耦数据库配置,支持多环境切换) -->
<properties resource="db.properties">
<!-- 可选:默认值(外部文件未定义时生效) -->
<property name="jdbc.username" value="default_root"/>
</properties>

<!-- 2. 全局设置(控制MyBatis核心行为,必配优化项) -->
<settings>
<!-- 开启驼峰命名自动映射(数据库字段user_name → Java属性userName) -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启SQL日志打印(开发环境调试用,生产环境可关闭或改用SLF4J) -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 开启二级缓存(提升查询性能,需配合映射文件<cache>使用) -->
<setting name="cacheEnabled" value="true"/>
<!-- 关闭激进延迟加载(按需加载关联数据,避免冗余查询) -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

<!-- 3. 类型别名(简化映射文件中类名书写,避免全类名冗余) -->
<typeAliases>
<!-- 方式1:单个类指定别名(alias可自定义,不写则默认类名首字母小写) -->
<typeAlias type="com.zhanghe.study.mybatis.model.User" alias="User"/>
<!-- 方式2:批量扫描包(推荐!包下所有类默认别名为类名首字母小写,如User→user) -->
<package name="com.zhanghe.study.mybatis.model"/>
</typeAliases>

<!-- 4. 数据库环境配置(支持多环境,default指定默认环境) -->
<environments default="development">
<!-- 开发环境 -->
<environment id="development">
<!-- 事务管理器:JDBC(依赖数据库原生事务)/ MANAGED(交给容器如Spring管理) -->
<transactionManager type="JDBC"/>
<!-- 数据源:POOLED(连接池,生产推荐)/ UNPOOLED(无池化)/ JNDI(容器数据源) -->
<dataSource type="POOLED">
<!-- 引用外部properties文件中的配置(${key}) -->
<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>
<!-- 生产环境(可单独配置,切换时修改default值) -->
<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>

<!-- 5. 映射文件扫描(告诉MyBatis去哪里找SQL映射) -->
<mappers>
<!-- 方式1:单个映射文件(resource路径基于classpath,如src/main/resources/mapper/UserMapper.xml) -->
<mapper resource="mapper/UserMapper.xml"/>
<!-- 方式2:批量扫描Mapper接口(推荐!需满足「接口与映射文件同包同名」) -->
<!-- <package name="com.zhanghe.study.mybatis.mapper"/> -->
</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">
<!-- namespace必须与Mapper接口全类名一致(绑定接口) -->
<mapper namespace="com.zhanghe.study.mybatis.mapper.UserMapper">
<!-- 可选:开启当前Mapper的二级缓存(需全局settings中cacheEnabled=true) -->
<cache eviction="LRU" flushInterval="60000" size="1024" readOnly="true"/>

<!-- 结果映射(ResultMap):解决「表字段与实体属性名不一致」或「复杂关联查询」 -->
<!-- 若开启驼峰映射(mapUnderscoreToCamelCase=true),简单场景可省略ResultMap -->
<resultMap id="UserResultMap" type="User"> <!-- type用别名(全局配置中定义) -->
<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>

<!-- 1. 基础查询:根据ID查用户(使用ResultMap) -->
<select id="selectUser" parameterType="Integer" resultMap="UserResultMap">
SELECT user_id, user_name, user_age, create_time
FROM users
WHERE user_id = #{id} <!-- #{id}:预编译占位符,防SQL注入 -->
</select>

<!-- 2. 多参数查询:按姓名和年龄查用户(用@Param注解) -->
<select id="selectUserByNameAndAge" resultMap="UserResultMap">
SELECT * FROM users
WHERE user_name = #{name} AND user_age = #{age}
<!-- 接口方法需加@Param:User selectUserByNameAndAge(@Param("name") String name, @Param("age") Integer age) -->
</select>

<!-- 3. 插入:新增用户并返回自增主键 -->
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="userId">
<!-- useGeneratedKeys=true:开启自增主键返回;keyProperty:主键对应实体属性名 -->
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 {
// 根据ID查用户(对应映射文件selectUser)
User selectUser(Integer userId);

// 多参数查询(需@Param注解绑定参数名)
User selectUserByNameAndAge(@Param("name") String userName, @Param("age") Integer userAge);

// 新增用户(返回自增主键注入到User的userId属性)
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 {
// 1. 读取全局配置文件(Resources是MyBatis提供的工具类,简化classpath资源加载)
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
// 2. 通过SqlSessionFactoryBuilder构建SqlSessionFactory
// (SqlSessionFactoryBuilder用完即丢,无需长期持有)
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
// 方式1:自动提交事务(openSession(true))
SqlSession session = sqlSessionFactory.openSession(true);

// 方式2:手动提交事务(默认,适合多SQL操作需事务一致性场景)
SqlSession session = sqlSessionFactory.openSession();
try {
// 执行SQL
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(); // 关闭SqlSession,释放连接
}
}

工程化优化:工具类封装

为避免重复代码,推荐封装 MyBatisUtil 工具类,统一管理 SqlSessionFactorySqlSession

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 {
// 静态变量:确保SqlSessionFactory全局唯一
private static SqlSessionFactory 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);
}
}

// 获取SqlSession(自动提交事务,简化单SQL操作)
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);
}

// 关闭SqlSession
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)

  • 原因 1mybatis-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 → 实体属性 userNamenull

  • 原因:未开启全局设置 mapUnderscoreToCamelCase

  • 解决方案:在mybatis-config.xml的<settings>中添加:

    1
    <setting name="mapUnderscoreToCamelCase" value="true"/>

3. SQL 注入风险(使用 ${} 占位符)

  • 原因${} 是字符串直接拼接(如 ${id} → 替换为参数值,无预编译),#{} 是预编译占位符(推荐);
  • 解决方案:除动态表名 / 排序字段(如 ORDER BY ${field})外,全部使用 #{}

4. SqlSession 未关闭导致连接泄漏

  • 原因:未在 finally 块中关闭 SqlSession,异常时资源未释放;
  • 解决方案:使用工具类的 closeSqlSession 方法,在 finally 中强制关闭。

总结

MyBatis 的基础使用核心是 “配置 + 接口 + 映射”:

  1. 配置文件:全局配置管理数据库和框架参数,映射文件定义 SQL 和实体映射;
  2. 执行流程SqlSessionFactory(全局唯一)→ SqlSession(单次会话)→ Mapper 代理(执行 SQL);
  3. 最佳实践:封装工具类管理核心对象、开启驼峰映射简化配置、使用接口代理避免硬编码

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10