MyBatis 速查表
本文档提供 MyBatis 常用配置和语法的快速参考。
核心配置文件
完整配置模板
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"/>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="useGeneratedKeys" value="true"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="logImpl" value="SLF4J"/>
</settings>
<typeAliases>
<package name="com.example.entity"/>
</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}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.example.mapper"/>
</mappers>
</configuration>
常用设置项
| 设置项 | 说明 | 默认值 |
|---|---|---|
cacheEnabled | 开启二级缓存 | true |
lazyLoadingEnabled | 延迟加载 | false |
mapUnderscoreToCamelCase | 驼峰命名映射 | false |
useGeneratedKeys | 主键回填 | false |
defaultExecutorType | 执行器类型 | SIMPLE |
localCacheScope | 一级缓存作用域 | SESSION |
jdbcTypeForNull | 空值类型 | OTHER |
映射文件
基本结构
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<resultMap id="BaseResultMap" type="User">
<id column="id" property="id"/>
<result column="username" property="username"/>
</resultMap>
<sql id="Base_Column_List">
id, username, email, phone
</sql>
</mapper>
CRUD 语句
<!-- 查询 -->
<select id="selectById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
<!-- 插入 -->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (username, email) VALUES (#{username}, #{email})
</insert>
<!-- 更新 -->
<update id="update">
UPDATE user SET username = #{username} WHERE id = #{id}
</update>
<!-- 删除 -->
<delete id="deleteById">
DELETE FROM user WHERE id = #{id}
</delete>
动态 SQL
if 条件
<if test="username != null and username != ''">
AND username = #{username}
</if>
where 子句
<where>
<if test="username != null">
AND username = #{username}
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
set 子句
<set>
<if test="username != null">username = #{username},</if>
<if test="email != null">email = #{email},</if>
</set>
foreach 循环
<!-- IN 查询 -->
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
<!-- 批量插入 -->
<foreach collection="list" item="user" separator=",">
(#{user.username}, #{user.email})
</foreach>
choose 选择
<choose>
<when test="id != null">
AND id = #{id}
</when>
<when test="username != null">
AND username = #{username}
</when>
<otherwise>
AND status = 1
</otherwise>
</choose>
trim 自定义
<!-- 等价于 where -->
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<!-- 等价于 set -->
<trim prefix="SET" suffixOverrides=",">
...
</trim>
bind 变量绑定
<bind name="pattern" value="'%' + username + '%'" />
SELECT * FROM user WHERE username LIKE #{pattern}
结果映射
基本映射
<resultMap id="BaseResultMap" type="User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="create_time" property="createTime"/>
</resultMap>
一对一关联
<resultMap id="UserWithOrderMap" type="User">
<id column="id" property="id"/>
<association property="order" javaType="Order">
<id column="order_id" property="id"/>
<result column="order_no" property="orderNo"/>
</association>
</resultMap>
一对多关联
<resultMap id="UserWithOrdersMap" type="User">
<id column="id" property="id"/>
<collection property="orders" ofType="Order">
<id column="order_id" property="id"/>
<result column="order_no" property="orderNo"/>
</collection>
</resultMap>
嵌套查询
<!-- 一对一嵌套查询 -->
<association property="order"
column="id"
select="selectOrderByUserId"/>
<!-- 一对多嵌套查询 -->
<collection property="orders"
column="id"
ofType="Order"
select="selectOrdersByUserId"/>
继承映射
<resultMap id="DetailResultMap" type="User" extends="BaseResultMap">
<result column="phone" property="phone"/>
</resultMap>
注解开发
基本注解
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(Long id);
@Insert("INSERT INTO user (username) VALUES (#{username})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
@Update("UPDATE user SET username = #{username} WHERE id = #{id}")
int update(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
int deleteById(Long id);
结果映射注解
@Results(id = "userMap", value = {
@Result(column = "id", property = "id", id = true),
@Result(column = "username", property = "username")
})
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(Long id);
关联映射注解
@Results({
@Result(column = "id", property = "id"),
@Result(column = "id", property = "orders",
many = @Many(select = "selectOrdersByUserId"))
})
@Select("SELECT * FROM user WHERE id = #{id}")
User selectWithOrders(Long id);
动态 SQL 注解
@Select("<script>" +
"SELECT * FROM user" +
"<where>" +
" <if test='username != null'> AND username = #{username}</if>" +
"</where>" +
"</script>")
List<User> selectByCondition(UserQuery query);
@SelectProvider(type = UserSqlProvider.class, method = "selectByCondition")
List<User> selectByCondition(UserQuery query);
参数传递
参数占位符
| 占位符 | 说明 | 示例 |
|---|---|---|
#{} | 预编译参数(安全) | #{username} |
${} | 字符串替换(不安全) | ${tableName} |
参数类型
<!-- 单个参数 -->
WHERE id = #{id}
<!-- 多个参数(使用 @Param) -->
WHERE username = #{username} AND email = #{email}
<!-- 对象参数 -->
WHERE username = #{username} AND email = #{email}
<!-- 集合参数 -->
<foreach collection="list" item="item">
#{item}
</foreach>
<!-- 数组参数 -->
<foreach collection="array" item="item">
#{item}
</foreach>
@Param 注解
// 多个参数必须使用 @Param
@Select("SELECT * FROM user WHERE username = #{username} AND status = #{status}")
User selectByUsernameAndStatus(@Param("username") String username,
@Param("status") Integer status);
缓存配置
一级缓存
<settings>
<!-- SESSION 或 STATEMENT -->
<setting name="localCacheScope" value="SESSION"/>
</settings>
二级缓存
<!-- XML 方式 -->
<cache eviction="LRU" flushInterval="60000" size="1024"/>
<!-- 注解方式 -->
@CacheNamespace
public interface UserMapper { }
缓存属性
| 属性 | 说明 | 默认值 |
|---|---|---|
eviction | 回收策略 | LRU |
flushInterval | 刷新间隔 | 无 |
size | 缓存大小 | 1024 |
readOnly | 是否只读 | false |
缓存引用
<!-- 共享其他 Mapper 的缓存 -->
<cache-ref namespace="com.example.mapper.UserMapper"/>
分页查询
PageHelper 依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>6.1.0</version>
</dependency>
插件配置
<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
<property name="reasonable" value="true"/>
</plugin>
</plugins>
基本使用
// 设置分页参数
PageHelper.startPage(pageNum, pageSize);
// 执行查询(自动添加分页)
List<User> list = userMapper.selectAll();
// 封装分页信息
PageInfo<User> pageInfo = new PageInfo<>(list);
PageInfo 属性
| 属性 | 说明 |
|---|---|
list | 当前页数据 |
total | 总记录数 |
pages | 总页数 |
pageNum | 当前页码 |
pageSize | 每页记录数 |
hasNextPage | 是否有下一页 |
hasPreviousPage | 是否有上一页 |
配置参数
| 参数 | 说明 | 默认值 |
|---|---|---|
helperDialect | 数据库方言 | 自动检测 |
reasonable | 分页合理化 | false |
supportMethodsArguments | 支持参数传递 | false |
手动分页
<!-- MySQL 分页 -->
<select id="selectByPage" resultType="User">
SELECT * FROM user
ORDER BY id DESC
LIMIT #{pageSize} OFFSET #{offset}
</select>
<!-- 计算偏移量:offset = (pageNum - 1) * pageSize -->
存储过程调用
基本语法
<!-- 调用存储过程必须指定 statementType="CALLABLE" -->
<select id="callProcedure" statementType="CALLABLE">
{call procedure_name(#{param1}, #{param2})}
</select>
参数模式
| 模式 | 说明 | 用法 |
|---|---|---|
IN | 输入参数(默认) | #{param, mode=IN} |
OUT | 输出参数 | #{param, mode=OUT, jdbcType=INTEGER} |
INOUT | 输入输出参数 | #{param, mode=INOUT, jdbcType=INTEGER} |
OUT 参数示例
<select id="callGetUserCount" statementType="CALLABLE">
{call get_user_count(#{count, mode=OUT, jdbcType=INTEGER})}
</select>
// 使用 Map 接收输出参数
Map<String, Object> result = new HashMap<>();
mapper.callGetUserCount(result);
System.out.println(result.get("count"));
返回结果集
<!-- 存储过程返回结果集 -->
<select id="callGetUsers" statementType="CALLABLE" resultType="User">
{call get_active_users()}
</select>
多结果集
<select id="callGetUserWithOrders"
statementType="CALLABLE"
resultType="User"
resultSets="users,orders">
{call get_user_with_orders(#{userId})}
</select>
Oracle 游标
<select id="callGetUsersCursor"
statementType="CALLABLE"
resultMap="UserResultMap">
{call get_users_cursor(
#{cursor, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=UserResultMap}
)}
</select>
注解方式
@Select(value = "{call get_user_count(#{count, mode=OUT, jdbcType=INTEGER})}")
@Options(statementType = StatementType.CALLABLE)
void callGetUserCount(Map<String, Object> result);
重要提示
- OUT 参数必须指定
jdbcType - 存储过程调用使用
{call ...}语法 - Oracle 游标需要指定
javaType=ResultSet和resultMap
类型别名
内置别名
| 别名 | 类型 | 别名 | 类型 |
|---|---|---|---|
_byte | byte | byte | Byte |
_long | long | long | Long |
_short | short | short | Short |
_int | int | int | Integer |
_double | double | double | Double |
_float | float | float | Float |
_boolean | boolean | boolean | Boolean |
_char | char(3.5.10+) | char | Character(3.5.10+) |
string | String | date | Date |
bigdecimal | BigDecimal | biginteger | BigInteger |
map | Map | hashmap | HashMap |
list | List | arraylist | ArrayList |
collection | Collection | iterator | Iterator |
object | Object | object[] | Object[] |
自定义别名
<typeAliases>
<typeAlias type="com.example.entity.User" alias="User"/>
<!-- 或包扫描 -->
<package name="com.example.entity"/>
</typeAliases>
注解别名
@Alias("user")
public class User { }
JDBC 类型
| JDBC 类型 | Java 类型 |
|---|---|
BIT | Boolean |
TINYINT | Byte |
SMALLINT | Short |
INTEGER | Integer |
BIGINT | Long |
FLOAT | Float |
DOUBLE | Double |
CHAR | String |
VARCHAR | String |
DATE | Date |
TIMESTAMP | Date |
BLOB | byte[] |
CLOB | String |
常用工具代码
SqlSession 工具类
public class SqlSessionUtil {
private static final SqlSessionFactory factory;
static {
try (InputStream is = Resources.getResourceAsStream("mybatis-config.xml")) {
factory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static SqlSession getSession() {
return factory.openSession();
}
public static SqlSession getSession(boolean autoCommit) {
return factory.openSession(autoCommit);
}
}
基本使用流程
try (SqlSession session = SqlSessionUtil.getSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
// 查询
User user = mapper.selectById(1L);
// 插入
User newUser = new User();
newUser.setUsername("test");
mapper.insert(newUser);
// 提交事务
session.commit();
}
使用 try-with-resources
// 自动关闭 SqlSession
try (SqlSession session = SqlSessionUtil.getSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectById(1L);
session.commit();
}
XML 转义字符
| 字符 | 转义 | 说明 |
|---|---|---|
< | < | 小于 |
> | > | 大于 |
& | & | 和号 |
' | ' | 单引号 |
" | " | 双引号 |
<!-- 使用转义 -->
<if test="age < 18">...</if>
<!-- 使用 CDATA -->
<if test="age < 18"><![CDATA[ AND age < 18 ]]></if>
常见问题解决
1. 找不到映射文件
确保 pom.xml 中配置了资源过滤:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
2. 主键回填不生效
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (username) VALUES (#{username})
</insert>
3. 驼峰映射不生效
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
4. 延迟加载不生效
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
5. 二级缓存不生效
- 全局配置:
cacheEnabled = true - Mapper 配置:添加
<cache/>或@CacheNamespace - 实体类实现
Serializable接口
6. 时区问题
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?serverTimezone=Asia/Shanghai"/>
7. 编码问题
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?characterEncoding=utf8"/>
执行器类型
| 类型 | 说明 |
|---|---|
SIMPLE | 普通执行器,每次创建新 Statement |
REUSE | 重用 Statement |
BATCH | 批量执行,重用 Statement |
<settings>
<setting name="defaultExecutorType" value="SIMPLE"/>
</settings>
// 代码指定
SqlSession session = factory.openSession(ExecutorType.BATCH);
插件开发
插件拦截点
| 对象 | 方法 |
|---|---|
| Executor | update, query, flushStatements, commit, rollback, getTransaction, close, isClosed |
| ParameterHandler | getParameterObject, setParameters |
| ResultSetHandler | handleResultSets, handleOutputParameters |
| StatementHandler | prepare, parameterize, batch, update, query |
Interceptor 接口
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
default void setProperties(Properties properties) {}
}
注解配置
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)
})
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 前置处理
Object result = invocation.proceed();
// 后置处理
return result;
}
}
插件配置
<plugins>
<plugin interceptor="com.example.plugin.MyPlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
Spring Boot 配置插件
@Configuration
public class MyBatisConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> {
configuration.addInterceptor(new SqlTimePlugin());
};
}
}
常用场景
| 场景 | 拦截对象 | 拦截方法 |
|---|---|---|
| SQL 日志 | Executor | query, update |
| 执行时间统计 | Executor | query, update |
| 分页处理 | StatementHandler | prepare |
| 数据权限 | Executor | query |
Spring Boot 整合
Maven 依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
application.yml 配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_demo?serverTimezone=Asia/Shanghai
username: root
password: password
mybatis:
mapper-locations: classpath:mapper/**/*.xml
type-aliases-package: com.example.entity
configuration:
map-underscore-to-camel-case: true
cache-enabled: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
启动类配置
@SpringBootApplication
@MapperScan("com.example.mapper") // 扫描 Mapper 接口
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
常用配置属性
| 属性 | 说明 |
|---|---|
mybatis.mapper-locations | Mapper XML 文件位置 |
mybatis.type-aliases-package | 实体类别名包路径 |
mybatis.type-handlers-package | 类型处理器包路径 |
mybatis.executor-type | 执行器类型 |
mybatis.configuration.* | MyBatis 配置项 |
事务管理
@Service
public class UserService {
@Transactional // 声明式事务
public void save(User user) {
userMapper.insert(user);
}
@Transactional(rollbackFor = Exception.class) // 所有异常回滚
public void update(User user) {
userMapper.update(user);
}
@Transactional(readOnly = true) // 只读事务
public User findById(Long id) {
return userMapper.selectById(id);
}
}
版本兼容性
| Starter 版本 | Spring Boot | Java |
|---|---|---|
| 3.0 | 3.0+ | 17+ |
| 2.3 | 2.7 | 8+ |
PageHelper 分页整合
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
pagehelper:
helper-dialect: mysql
reasonable: true