Java MyBatis
MyBatis 使用规则文档
一、架构概述
1.1 技术栈
- MyBatis 中间件:
ins-framework-mybatis:6.2.1-SNAPSHOT - Spring Boot: 2.7.18
- MyBatis Spring Boot Starter: 自动配置(通过中间件引入)
- 数据库: PostgreSQL(默认)/ MySQL / H2 / Oracle 等
1.2 架构层次
┌─────────────────────────────────────────┐
│ Controller 层 (Web) │
│ - 接收 HTTP 请求 │
│ - 参数验证 │
│ - 调用 Service 层 │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ Service 层 (业务逻辑) │
│ - 业务逻辑处理 │
│ - 事务管理 │
│ - PO/VO 转换 │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ DAO 层 (数据访问) │
│ - 继承 MybatisBaseDao<T, I> │
│ - 定义数据访问接口 │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ Mapper XML (SQL 映射) │
│ - 定义 SQL 语句 │
│ - 结果映射 │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ MyBatis 中间件 (ins-framework) │
│ - MybatisBaseDao 基础接口 │
│ - 分页拦截器 (OffsetLimitInterceptor) │
│ - Page/PageParam/Order 类 │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ 数据库 (PostgreSQL) │
└─────────────────────────────────────────┘
1.3 核心组件
- MybatisBaseDao<T, I>: 基础 DAO 接口,提供标准 CRUD 方法
- OffsetLimitInterceptor: 分页拦截器,自动处理分页 SQL
- PageParam: 分页参数类,包含页码、每页数量、排序信息
- Page: 分页结果类,包含数据列表和分页信息
- Order: 排序类,用于定义排序规则
二、依赖配置
2.1 Maven 依赖
在 pom.xml 中配置:
<!-- MyBatis Framework 中间件 -->
<dependency>
<groupId>ins.framework</groupId>
<artifactId>ins-framework-mybatis</artifactId>
<version>6.2.1-SNAPSHOT</version>
<exclusions>
<!-- 排除 logback,使用 log4j2 -->
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</exclusion>
</exclusions>
</dependency>
2.2 应用配置
在 application.properties 中配置:
# MyBatis 配置
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=pdfc.monitor.domain.sys.po
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.log-impl=org.apache.ibatis.logging.slf4j.Slf4jImpl
配置说明:
mapper-locations: Mapper XML 文件位置type-aliases-package: PO 类的包路径(用于简化 XML 中的类型引用)map-underscore-to-camel-case: 自动映射下划线命名到驼峰命名log-impl: MyBatis SQL 日志实现
2.3 MyBatis 配置类
创建 MonitorMybatisConfig.java 配置类:
@Configuration
@MapperScan("pdfc.monitor.domain.sys.dao")
@Slf4j
public class MonitorMybatisConfig {
@Autowired
private DataSource dataSource;
/**
* 创建分页拦截器 Bean
* 自动检测数据库类型并配置对应的分页方言
*/
@Bean
public OffsetLimitInterceptor offsetLimitInterceptor() throws SQLException {
OffsetLimitInterceptor interceptor = new OffsetLimitInterceptor();
// 根据数据库类型自动配置方言
// 支持: PostgreSQL, MySQL, Oracle, H2, DB2, SQL Server 等
// ...
return interceptor;
}
/**
* 注册分页拦截器到 MyBatis 配置
*/
@Bean
public ConfigurationCustomizer mybatisConfigurationCustomizer(OffsetLimitInterceptor interceptor) {
return configuration -> {
configuration.addInterceptor(interceptor);
log.info("MyBatis pagination interceptor configured successfully");
};
}
}
三、DAO 层开发规则
3.1 DAO 接口定义
规则 1: DAO 接口必须继承 MybatisBaseDao<T, I>
@Mapper
public interface SysUserDao extends MybatisBaseDao<SysUser, Integer> {
// T: 实体类型 (PO)
// I: 主键类型
}
规则 2: 使用 @Mapper 注解标记接口
规则 3: 如需自定义参数名,重写方法并添加 @Param 注解
@Mapper
public interface SysUserDao extends MybatisBaseDao<SysUser, Integer> {
/**
* 重写方法以支持参数绑定
* 在 XML 中可以使用 #{idList} 引用参数
*/
@Override
List<SysUser> selectBatchByPrimaryKeys(@Param("idList") List<Integer> idList);
/**
* 分页查询(重写以支持参数绑定)
*/
@Override
ins.framework.mybatis.Page<SysUser> selectPage(
@Param("pageParam") PageParam pageParam,
@Param("entity") SysUser entity
);
}
3.2 MybatisBaseDao 提供的方法
| 方法名 | 说明 | 参数 | 返回值 |
|---|---|---|---|
insert |
插入记录(全字段) | T entity |
int |
insertSelective |
插入记录(选择性字段) | T entity |
int |
deleteByPrimaryKey |
根据主键删除 | I id |
int |
deleteBatchByPrimaryKeys |
批量删除 | List<I> idList |
int |
updateByPrimaryKey |
根据主键更新(全字段) | T entity |
int |
updateSelectiveByPrimaryKey |
根据主键更新(选择性字段) | T entity |
int |
selectByPrimaryKey |
根据主键查询 | I id |
T |
selectBatchByPrimaryKeys |
批量查询 | List<I> idList |
List<T> |
selectPage |
分页查询 | PageParam pageParam, T entity |
Page<T> |
四、Mapper XML 编写规则
4.1 文件位置和命名
- 位置:
src/main/resources/mapper/ - 命名:
{Entity}Mapper.xml,如SysUserMapper.xml - namespace: 必须与 DAO 接口的完整类名一致
<mapper namespace="pdfc.monitor.domain.sys.dao.SysUserDao">
<!-- SQL 映射 -->
</mapper>
4.2 结果映射 (ResultMap)
规则: 定义 BaseResultMap 用于结果映射
<resultMap id="BaseResultMap" type="pdfc.monitor.domain.sys.po.SysUser">
<id column="id" property="id" jdbcType="INTEGER"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<result column="work" property="work" jdbcType="VARCHAR"/>
<result column="age" property="age" jdbcType="INTEGER"/>
<result column="version" property="version" jdbcType="INTEGER"/>
<result column="insert_time_for_his" property="insertTimeForHis" jdbcType="TIMESTAMP"/>
<result column="operate_time_for_his" property="operateTimeForHis" jdbcType="TIMESTAMP"/>
</resultMap>
说明:
<id>: 主键字段映射<result>: 普通字段映射column: 数据库列名(下划线命名)property: Java 属性名(驼峰命名)jdbcType: JDBC 类型
4.3 SQL 片段复用
规则: 使用 <sql> 标签定义可复用的 SQL 片段
<sql id="Base_Column_List">
id, name, work, age, version, insert_time_for_his, operate_time_for_his
</sql>
<!-- 在查询中使用 -->
<select id="selectByPrimaryKey" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM sys_user
WHERE id = #{id,jdbcType=INTEGER}
</select>
4.4 CRUD 操作 SQL 编写
4.4.1 插入操作
全字段插入:
<insert id="insert" parameterType="pdfc.monitor.domain.sys.po.SysUser"
useGeneratedKeys="true" keyProperty="id" keyColumn="id">
INSERT INTO sys_user (
name, work, age, version,
insert_time_for_his, operate_time_for_his
) VALUES (
#{name,jdbcType=VARCHAR},
#{work,jdbcType=VARCHAR},
#{age,jdbcType=INTEGER},
#{version,jdbcType=INTEGER},
#{insertTimeForHis,jdbcType=TIMESTAMP},
#{operateTimeForHis,jdbcType=TIMESTAMP}
)
</insert>
选择性插入:
<insert id="insertSelective" parameterType="pdfc.monitor.domain.sys.po.SysUser"
useGeneratedKeys="true" keyProperty="id" keyColumn="id">
INSERT INTO sys_user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="name != null">
name,
</if>
<if test="work != null">
work,
</if>
<!-- 其他字段 -->
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="work != null">
#{work,jdbcType=VARCHAR},
</if>
<!-- 其他字段 -->
</trim>
</insert>
4.4.2 删除操作
单条删除:
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
DELETE FROM sys_user WHERE id = #{id,jdbcType=INTEGER}
</delete>
批量删除:
<delete id="deleteBatchByPrimaryKeys" parameterType="java.util.List">
DELETE FROM sys_user WHERE id IN
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id,jdbcType=INTEGER}
</foreach>
</delete>
注意: 如果没有 @Param 注解,集合参数默认名为 list 或 collection
4.4.3 更新操作
全字段更新:
<update id="updateByPrimaryKey" parameterType="pdfc.monitor.domain.sys.po.SysUser">
UPDATE sys_user
SET name = #{name,jdbcType=VARCHAR},
work = #{work,jdbcType=VARCHAR},
age = #{age,jdbcType=INTEGER},
version = #{version,jdbcType=INTEGER},
operate_time_for_his = #{operateTimeForHis,jdbcType=TIMESTAMP}
WHERE id = #{id,jdbcType=INTEGER}
</update>
选择性更新:
<update id="updateSelectiveByPrimaryKey" parameterType="pdfc.monitor.domain.sys.po.SysUser">
UPDATE sys_user
<set>
<if test="name != null">
name = #{name,jdbcType=VARCHAR},
</if>
<if test="work != null">
work = #{work,jdbcType=VARCHAR},
</if>
<!-- 其他字段 -->
</set>
WHERE id = #{id,jdbcType=INTEGER}
</update>
4.4.4 查询操作
单条查询:
<select id="selectByPrimaryKey" parameterType="java.lang.Integer"
resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM sys_user
WHERE id = #{id,jdbcType=INTEGER}
</select>
批量查询:
<select id="selectBatchByPrimaryKeys" parameterType="java.util.List"
resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM sys_user
WHERE id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id,jdbcType=INTEGER}
</foreach>
</select>
注意: 如果 DAO 方法使用了 @Param("idList"),XML 中应使用 collection="idList"
4.5 分页查询 SQL 编写
重要规则: 分页查询的 SQL 不需要手动添加 LIMIT 和 OFFSET,分页拦截器会自动处理
<!-- 分页查询(分页拦截器会自动添加 LIMIT 和 OFFSET) -->
<select id="selectPage" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM sys_user
<where>
<if test="entity != null">
<if test="entity.id != null">
AND id = #{entity.id,jdbcType=INTEGER}
</if>
<if test="entity.name != null and entity.name != ''">
AND name LIKE '%' || #{entity.name,jdbcType=VARCHAR} || '%'
</if>
<!-- 其他条件 -->
</if>
</where>
ORDER BY insert_time_for_his DESC
</select>
说明:
- 参数使用
entity.xxx访问实体属性 - 不要添加
LIMIT和OFFSET子句 - 排序可以在 SQL 中定义,也可以通过
PageParam动态添加
五、Service 层使用规则
5.1 基础 CRUD 操作
@Service
public class SysUserServiceImpl implements SysUserService {
@Autowired
private SysUserDao sysUserDao;
@Override
public int insert(SysUserVo entity) {
// VO 转 PO
SysUser po = BeanConverter.voToPo(entity);
// 调用 DAO
return sysUserDao.insert(po);
}
@Override
public SysUserVo selectByPrimaryKey(Integer id) {
SysUser po = sysUserDao.selectByPrimaryKey(id);
// PO 转 VO
return BeanConverter.poToVo(po);
}
// 其他 CRUD 方法类似...
}
5.2 分页查询使用
@Override
public Page<SysUserVo> getPage(Integer pageIndex, Integer pageSize) {
// 1. 设置默认值
if (pageIndex == null || pageIndex < 1) {
pageIndex = 1;
}
if (pageSize == null || pageSize < 1) {
pageSize = 10;
}
// 2. 创建分页参数
PageParam pageParam = new PageParam(pageIndex, pageSize);
// 3. 添加排序(可选)
Order order = Order.create("insert_time_for_his", "desc", null);
pageParam.getOrders().add(order);
// 4. 创建查询条件
SysUser condition = new SysUser();
// 设置查询条件...
// 5. 执行分页查询
Page<SysUser> poPage = sysUserDao.selectPage(pageParam, condition);
// 6. 转换为 VO 列表
List<SysUserVo> voList = BeanConverter.poListToVoList(poPage);
// 7. 创建返回的 Page 对象(保留分页信息)
Page<SysUserVo> voPage = new Page<>(voList, poPage.getPaginator());
return voPage;
}
关键点:
PageParam构造函数:PageParam(int page, int limit)Order.create(): 创建排序对象,参数为(property, direction, orderExpr)Page<T>构造函数:Page(Collection<E> c, Paginator p)保留分页信息- 通过
page.getTotalCount()获取总记录数
5.3 批量操作使用
@Override
public List<SysUserVo> selectBatchByPrimaryKeys(List<SysUserVo> idList) {
// 1. 提取 ID 列表
List<Integer> ids = new ArrayList<>();
for (SysUserVo vo : idList) {
if (vo != null && vo.getId() != null) {
ids.add(vo.getId());
}
}
// 2. 批量查询
List<SysUser> poList = sysUserDao.selectBatchByPrimaryKeys(ids);
// 3. 转换为 VO 列表
return BeanConverter.poListToVoList(poList);
}
六、参数绑定规则
6.1 单个参数
规则: 单个参数可以直接使用,无需 @Param 注解
// DAO 接口
SysUser selectByPrimaryKey(Integer id);
// XML
<select id="selectByPrimaryKey" parameterType="java.lang.Integer">
SELECT * FROM sys_user WHERE id = #{id}
</select>
6.2 多个参数
规则: 多个参数必须使用 @Param 注解
// DAO 接口
Long selectPageCount(
@Param("pageParam") PageParam pageParam,
@Param("condition") SysUser condition
);
// XML
<select id="selectPageCount" resultType="java.lang.Long">
SELECT COUNT(1) FROM sys_user
<where>
<if test="condition.id != null">
AND id = #{condition.id}
</if>
</where>
</select>
6.3 集合参数
规则 1: 如果没有 @Param 注解,默认参数名为 list 或 collection
// DAO 接口(无 @Param)
int deleteBatchByPrimaryKeys(List<Integer> idList);
// XML(使用 list)
<foreach collection="list" item="id">
#{id}
</foreach>
规则 2: 如果有 @Param 注解,使用注解中的名称
// DAO 接口(有 @Param)
List<SysUser> selectBatchByPrimaryKeys(@Param("idList") List<Integer> idList);
// XML(使用 idList)
<foreach collection="idList" item="id">
#{id}
</foreach>
6.4 对象属性访问
规则: 使用点号访问对象属性
<!-- 访问实体属性 -->
<if test="entity.name != null">
AND name = #{entity.name}
</if>
<!-- 访问嵌套对象属性 -->
<if test="entity.user.name != null">
AND user_name = #{entity.user.name}
</if>
七、分页功能详解
7.1 PageParam 类
PageParam pageParam = new PageParam(pageIndex, pageSize);
// 添加排序
Order order1 = Order.create("name", "asc", null);
Order order2 = Order.create("age", "desc", null);
pageParam.getOrders().add(order1);
pageParam.getOrders().add(order2);
Order.create() 参数说明:
property: 排序字段名(数据库列名)direction: 排序方向(“asc” 或 “desc”)orderExpr: 排序表达式(可选,用于特殊排序需求,如 Oracle 的 NLSSORT)
7.2 Page 类
Page<SysUser> page = sysUserDao.selectPage(pageParam, condition);
// 获取数据列表
List<SysUser> list = page; // Page 继承自 ArrayList
// 获取分页信息
int totalCount = page.getTotalCount(); // 总记录数
int pageNo = page.getPageNo(); // 当前页码
int pageSize = page.getPageSize(); // 每页数量
Paginator paginator = page.getPaginator(); // 分页器对象
7.3 分页拦截器工作原理
- 拦截查询: 拦截
selectPage方法的 SQL 执行 - 解析 PageParam: 从方法参数中提取分页信息
- 生成总数查询: 自动生成
SELECT COUNT(*)查询 - 修改原 SQL: 在原 SQL 基础上添加
LIMIT和OFFSET - 执行查询: 执行修改后的 SQL 和总数查询
- 封装结果: 将结果封装到
Page<T>对象中
支持的数据库方言:
- PostgreSQL
- MySQL
- Oracle
- H2
- DB2
- SQL Server
- HSQL
- Sybase
- Informix
八、最佳实践
8.1 DAO 层
- 统一继承 MybatisBaseDao: 所有 DAO 接口都应继承
MybatisBaseDao<T, I> - 合理使用 @Param: 多参数或需要自定义参数名时使用
- 避免重复定义: 基础 CRUD 方法已在
MybatisBaseDao中定义,无需重复
8.2 Mapper XML
- 统一 ResultMap: 使用
BaseResultMap统一结果映射 - SQL 片段复用: 使用
<sql>和<include>复用 SQL 片段 - 动态 SQL: 合理使用
<if>,<where>,<set>,<trim>等标签 - 分页 SQL: 不要手动添加
LIMIT和OFFSET - 参数类型: 明确指定
jdbcType避免类型转换问题
8.3 Service 层
- PO/VO 分离: Service 层处理 PO 和 VO 的转换
- 事务管理: 在 Service 方法上使用
@Transactional - 异常处理: 统一异常处理,不要将 SQL 异常直接抛出
- 参数验证: 在 Service 层进行业务参数验证
8.4 性能优化
- 批量操作: 使用批量方法减少数据库交互
- 分页查询: 合理设置每页数量,避免一次查询过多数据
- 索引优化: 为常用查询字段添加数据库索引
- SQL 优化: 避免
SELECT *,只查询需要的字段
九、常见问题
9.1 参数绑定错误
问题: Parameter 'xxx' not found. Available parameters are [collection, list]
原因: 集合参数没有使用 @Param 注解,但 XML 中使用了自定义参数名
解决:
- 方法 1: 在 DAO 方法上添加
@Param("xxx")注解 - 方法 2: 在 XML 中使用
collection="list"或collection="collection"
9.2 分页不生效
问题: 分页查询返回所有数据,没有分页效果
原因:
- 分页拦截器未正确配置
- 数据库方言配置错误
PageParam参数传递错误
解决:
- 检查
MonitorMybatisConfig配置 - 确认数据库类型检测正确
- 确认
selectPage方法参数正确
9.3 结果映射错误
问题: 查询结果字段为 null
原因:
ResultMap配置错误- 数据库列名与 Java 属性名不匹配
- 未启用
map-underscore-to-camel-case
解决:
- 检查
ResultMap配置 - 确认列名和属性名映射正确
- 启用自动映射:
mybatis.configuration.map-underscore-to-camel-case=true
9.4 主键回填失败
问题: 插入后实体对象的 ID 仍为 null
原因: insert 标签缺少 useGeneratedKeys 配置
解决:
<insert id="insert" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
<!-- SQL -->
</insert>
十、总结
10.1 核心要点
- 使用中间件: 通过
ins-framework-mybatis中间件简化开发 - 继承基础接口: DAO 接口继承
MybatisBaseDao获得基础功能 - 分页自动化: 使用分页拦截器自动处理分页 SQL
- 参数绑定: 合理使用
@Param注解进行参数绑定 - XML 映射: 遵循 XML 编写规范,注意参数名和集合处理
10.2 开发流程
- 创建 PO 类(持久化对象)
- 创建 DAO 接口,继承
MybatisBaseDao<PO, ID> - 创建 Mapper XML 文件,实现 SQL 映射
- 在 Service 层调用 DAO 方法
- 处理 PO/VO 转换和业务逻辑
10.3 参考示例
完整示例请参考项目中的:
- DAO 接口:
src/main/java/pdfc/monitor/domain/sys/dao/SysUserDao.java - Mapper XML:
src/main/resources/mapper/SysUserMapper.xml - Service 实现:
src/main/java/pdfc/monitor/service/impl/SysUserServiceImpl.java - 配置类:
src/main/java/pdfc/monitor/config/MonitorMybatisConfig.java
文档版本: 1.0
最后更新: 2025-11-17
维护者: Allen
更多推荐

所有评论(0)