以下是符合 MyBatis-Plus 最佳实践、遵循 企业级开发规范 的两个核心模板文件:
mapper.java.ftl(Mapper 接口)mapper.xml.ftl(Mapper XML 映射文件)
这两个模板均包含详尽的中文注释,适用于 MyBatis-Plus Generator 代码生成,可直接用于实际项目开发。
✅ 1. mapper.java.ftl(Mapper 接口模板)
package ${package.Mapper};
<#if entity??>
import ${package.Entity}.${entity};
</#if>
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* ${table.comment!} Mapper 接口
* </p>
*
* <p>
* 本接口继承 MyBatis-Plus 提供的 {@link BaseMapper},自动获得通用 CRUD 方法(如 insert、delete、update、select 等),
* 无需编写基础 SQL。若需自定义复杂查询(如多表关联、动态统计、分组聚合等),可在此接口中声明方法,
* 并在对应的 XML 文件中编写 SQL 实现。
* </p>
*
* <p>
* 【设计说明】
* - 使用 {@code @Mapper} 注解标识为 MyBatis Mapper 接口(Spring Boot 项目也可通过 {@code @MapperScan} 扫描,但显式标注更清晰)
* - 泛型指定实体类,确保类型安全
* - 所有自定义方法应遵循命名规范(如:selectActiveList、countByStatus)
* - 方法参数建议使用实体或 DTO,避免过多原始参数
* </p>
*
* @author ${author}
* @since ${date}
*/
@Mapper
public interface ${table.mapperName} extends BaseMapper<${entity}> {
/**
* 示例:根据状态查询启用的 ${table.comment!} 列表
*
* <p>
* 此方法展示了如何扩展自定义查询。MyBatis-Plus 的 BaseMapper 已覆盖 90% 的单表操作,
* 仅当需要复杂 SQL(如 JOIN、子查询、窗口函数等)时才需在此声明方法。
* </p>
*
* @param status 状态值(例如:1 表示启用)
* @return 启用状态的实体列表
*/
// List<${entity}> selectByStatus(@Param("status") Integer status);
/**
* 示例:批量逻辑删除(软删除)——通常不需要,因为 BaseMapper 的 delete 方法已支持逻辑删除
*
* <p>
* 若实体类使用 {@code @TableLogic} 注解标记逻辑删除字段,
* 调用 {@code deleteById} 或 {@code deleteByMap} 会自动转为 UPDATE 操作。
* </p>
*/
// int batchSoftDelete(@Param("ids") List<Long> ids);
}
✅ 2. mapper.xml.ftl(Mapper XML 映射文件模板)
<?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">
<!--
| ${table.comment!} Mapper XML 映射文件
|
| 【设计原则】
| 1. 仅当需要复杂 SQL(如多表 JOIN、动态 GROUP BY、自定义分页等)时才编写 XML
| 2. 简单单表 CRUD 应优先使用 MyBatis-Plus 的 LambdaQueryWrapper 或 QueryWrapper,避免冗余 XML
| 3. 所有 SQL 必须使用 #{param} 防止 SQL 注入,禁止使用 ${param}
| 4. 动态 SQL 使用 <if>、<choose> 等标签,保持可读性
| 5. 字段名建议使用实体类属性对应的数据库列名(可通过 @TableField 指定)
|
| 【注意事项】
| - namespace 必须与 Mapper 接口全限定名一致
| - id 必须与 Mapper 接口中方法名一致
| - resultType 指向实体类,MyBatis-Plus 会自动映射(需确保字段名匹配)
| - 若使用逻辑删除,WHERE 条件中无需手动添加 deleted = 0,MyBatis-Plus 全局插件会自动处理
|
| @author ${author}
| @since ${date}
-->
<mapper namespace="${package.Mapper}.${table.mapperName}">
<!--
示例:根据状态查询列表(取消注释后需在 Mapper 接口中声明对应方法)
注意:
- 使用 #{status} 而非 ${status},防止 SQL 注入
- 表名使用 ${table.name}(由代码生成器填充)
- 字段名建议与数据库实际列名一致(如 status 而非 statusField)
-->
<!--
<select id="selectByStatus" resultType="${package.Entity}.${entity}">
SELECT
<include refid="Base_Column_List" />
FROM ${table.name}
WHERE status = #{status}
AND deleted = 0 <!-- 若未启用全局逻辑删除插件,需手动添加;否则可省略 -->
</select>
-->
<!--
示例:复杂分页查询(带动态条件)
适用于:
- 多条件组合查询
- 需要手动控制分页逻辑(如 COUNT + LIST 分开写)
- MyBatis-Plus 分页插件无法满足的场景
注意:MyBatis-Plus 推荐使用 IPage + lambdaQuery() 实现分页,此处仅为展示 XML 写法
-->
<!--
<select id="selectPageByCondition" resultType="${package.Entity}.${entity}">
SELECT
<include refid="Base_Column_List" />
FROM ${table.name}
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="status != null">
AND status = #{status}
</if>
AND deleted = 0
</where>
ORDER BY create_time DESC
LIMIT #{page.offset}, #{page.size}
</select>
<select id="countByCondition" resultType="java.lang.Long">
SELECT COUNT(*)
FROM ${table.name}
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="status != null">
AND status = #{status}
</if>
AND deleted = 0
</where>
</select>
-->
<!--
【重要】基础字段列表,避免在每个 SELECT 中重复书写
- 字段顺序建议与数据库表定义一致
- 若字段较多,可考虑使用 MyBatis-Plus 的自动映射(无需显式列出)
-->
<sql id="Base_Column_List">
<#-- 遍历表字段,生成列名列表 -->
<#list table.fields as field>
<#if field_index == 0>${field.name}<#else>, ${field.name}</#if>
</#list>
</sql>
<!--
【扩展建议】以下为常见场景示例(按需启用)
1. 批量插入(使用 foreach)
2. 动态更新(仅更新非空字段)
3. 多表关联查询(LEFT JOIN)
示例:动态更新(MyBatis-Plus 的 updateById 已支持自动忽略 null,通常无需此写法)
-->
<!--
<update id="updateSelective">
UPDATE ${table.name}
<set>
<if test="name != null and name != ''">
name = #{name},
</if>
<if test="status != null">
status = #{status},
</if>
update_time = NOW()
</set>
WHERE id = #{id}
AND deleted = 0
</update>
-->
</mapper>
🔍 关键设计说明与最佳实践
1. 关于是否需要 XML 文件
- ✅ 推荐原则:能不用 XML 就不用 XML
- MyBatis-Plus 的
QueryWrapper/LambdaQueryWrapper可覆盖 95% 的单表操作 - 仅在以下场景使用 XML:
- 多表 JOIN 查询
- 复杂聚合(GROUP BY + HAVING + 子查询)
- 数据库特有函数(如 JSON_EXTRACT、窗口函数)
- 需要极致性能优化的手写 SQL
2. 字段安全与 SQL 注入防护
- 必须使用
#{}:MyBatis 会预编译,防止注入 - 禁止使用
${}:除非是动态表名/列名(极少见,且需严格校验)
3. 逻辑删除处理
- 若全局配置了 MyBatis-Plus 逻辑删除插件(
MybatisPlusInterceptor+TableLogic):- 所有 SELECT 自动追加
AND deleted = 0 - 所有 DELETE 自动转为
UPDATE ... SET deleted = 1 - XML 中无需手动处理
deleted字段
- 所有 SELECT 自动追加
4. 代码生成器变量说明
${table.name}:数据库表名(如user_info)${table.fields}:表字段列表,每个字段包含name(列名)、property(Java 属性名)等<#list table.fields as field>:FreeMarker 循环语法,用于生成字段列表
5. 性能与维护性建议
- 避免在 XML 中写业务逻辑(如状态判断),应放在 Service 层
- 复杂查询建议拆分为多个简单方法,而非一个“万能查询”
- 使用
<sql>标签复用字段列表,减少重复
📦 项目结构示例
src/main/resources
└── mapper
└── user
└── UserMapper.xml ← 按模块或实体分类存放
并在 application.yml 中配置:
mybatis-plus:
mapper-locations: classpath*:mapper/**/*Mapper.xml
configuration:
map-underscore-to-camel-case: true
✅ 这两份模板体现了 “约定优于配置” 和 “最小化 XML” 的现代 MyBatis-Plus 开发理念,既保留了 XML 的灵活性,又充分发挥了 MyBatis-Plus 的自动化优势,是企业级项目的理想选择。
771

被折叠的 条评论
为什么被折叠?



