跳到主要内容

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=ResultSetresultMap

类型别名

内置别名

别名类型别名类型
_bytebytebyteByte
_longlonglongLong
_shortshortshortShort
_intintintInteger
_doubledoubledoubleDouble
_floatfloatfloatFloat
_booleanbooleanbooleanBoolean
_charchar(3.5.10+)charCharacter(3.5.10+)
stringStringdateDate
bigdecimalBigDecimalbigintegerBigInteger
mapMaphashmapHashMap
listListarraylistArrayList
collectionCollectioniteratorIterator
objectObjectobject[]Object[]

自定义别名

<typeAliases>
<typeAlias type="com.example.entity.User" alias="User"/>
<!-- 或包扫描 -->
<package name="com.example.entity"/>
</typeAliases>

注解别名

@Alias("user")
public class User { }

JDBC 类型

JDBC 类型Java 类型
BITBoolean
TINYINTByte
SMALLINTShort
INTEGERInteger
BIGINTLong
FLOATFloat
DOUBLEDouble
CHARString
VARCHARString
DATEDate
TIMESTAMPDate
BLOBbyte[]
CLOBString

常用工具代码

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 转义字符

字符转义说明
<&lt;小于
>&gt;大于
&&amp;和号
'&apos;单引号
"&quot;双引号
<!-- 使用转义 -->
<if test="age &lt; 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);

插件开发

插件拦截点

对象方法
Executorupdate, query, flushStatements, commit, rollback, getTransaction, close, isClosed
ParameterHandlergetParameterObject, setParameters
ResultSetHandlerhandleResultSets, handleOutputParameters
StatementHandlerprepare, 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 日志Executorquery, update
执行时间统计Executorquery, update
分页处理StatementHandlerprepare
数据权限Executorquery

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-locationsMapper 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 BootJava
3.03.0+17+
2.32.78+

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