分页插件与性能优化
分页插件配置
简要描述:MybatisPlus分页插件是基于物理分页实现的高性能分页解决方案,支持多种数据库的分页语法,能够自动识别数据库类型并生成对应的分页SQL。
核心概念:
- 物理分页:直接在SQL层面进行分页,性能优异
- 自动识别:自动识别数据库类型,生成对应分页语法
- 插件机制:基于Mybatis拦截器实现
- 多数据库支持:支持MySQL、PostgreSQL、Oracle、SQL Server等
- 性能优化:支持count查询优化、分页参数合理性检查
基础配置
java
/**
* MybatisPlus配置类
*/
@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {
/**
* 分页插件配置
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
// 设置数据库类型(可选,会自动识别)
paginationInterceptor.setDbType(DbType.MYSQL);
// 设置最大单页限制数量,默认500条,-1不受限制
paginationInterceptor.setMaxLimit(1000L);
// 溢出总页数后是否进行处理(默认不处理)
paginationInterceptor.setOverflow(false);
// 生成countSql优化掉join现象
paginationInterceptor.setOptimizeJoin(true);
interceptor.addInnerInterceptor(paginationInterceptor);
return interceptor;
}
}
高级配置
java
/**
* 高级分页配置
*/
@Configuration
public class AdvancedPaginationConfig {
/**
* 自定义分页插件配置
*/
@Bean
public MybatisPlusInterceptor advancedMybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor() {
@Override
public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
// 自定义分页逻辑,可以在这里添加权限检查等
IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
if (page != null && page.getSize() > 1000) {
throw new RuntimeException("分页大小不能超过1000条");
}
return super.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
}
};
// 数据库类型
paginationInterceptor.setDbType(DbType.MYSQL);
// 最大单页限制
paginationInterceptor.setMaxLimit(1000L);
// 溢出处理
paginationInterceptor.setOverflow(true);
// count查询优化
paginationInterceptor.setOptimizeJoin(true);
interceptor.addInnerInterceptor(paginationInterceptor);
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 阻断插件(防止全表更新与删除)
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
/**
* 自定义分页方言(如果需要支持特殊数据库)
*/
@Bean
public IDialect customDialect() {
return new IDialect() {
@Override
public boolean willDoCount(IPage<?> page, List<MappedStatement> countMs, BoundSql countSql, Object parameter) {
// 自定义count查询逻辑
return page.searchCount();
}
@Override
public String buildPaginationSql(String originalSql, long offset, long limit) {
// 自定义分页SQL构建
return originalSql + " LIMIT " + offset + ", " + limit;
}
};
}
}
多数据源分页配置
java
/**
* 多数据源分页配置
*/
@Configuration
public class MultiDataSourcePaginationConfig {
/**
* 主数据源分页配置
*/
@Bean("masterPaginationInterceptor")
public MybatisPlusInterceptor masterPaginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
paginationInterceptor.setDbType(DbType.MYSQL);
paginationInterceptor.setMaxLimit(1000L);
paginationInterceptor.setOptimizeJoin(true);
interceptor.addInnerInterceptor(paginationInterceptor);
return interceptor;
}
/**
* 从数据源分页配置
*/
@Bean("slavePaginationInterceptor")
public MybatisPlusInterceptor slavePaginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
paginationInterceptor.setDbType(DbType.MYSQL);
paginationInterceptor.setMaxLimit(2000L); // 从库可以设置更大的限制
paginationInterceptor.setOptimizeJoin(true);
interceptor.addInnerInterceptor(paginationInterceptor);
return interceptor;
}
}
分页参数配置
yaml
# application.yml
mybatis-plus:
configuration:
# 分页相关配置
map-underscore-to-camel-case: true
cache-enabled: false
call-setters-on-nulls: true
jdbc-type-for-null: 'null'
global-config:
db-config:
# 逻辑删除配置
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
# 分页配置
banner: false
# 分页插件配置
pagination:
# 默认分页大小
default-page-size: 10
# 最大分页大小
max-page-size: 1000
# 是否启用分页合理化
reasonable: true
# 是否支持接口参数来传递分页参数
support-methods-arguments: true
# 分页参数名称
page-size-zero: false
自定义分页处理器
java
/**
* 自定义分页处理器
*/
@Component
public class CustomPaginationHandler {
/**
* 分页参数预处理
*/
public <T> IPage<T> preprocessPage(IPage<T> page) {
// 参数合理性检查
if (page.getCurrent() <= 0) {
page.setCurrent(1);
}
if (page.getSize() <= 0) {
page.setSize(10);
}
// 最大分页限制
if (page.getSize() > 1000) {
page.setSize(1000);
}
return page;
}
/**
* 分页结果后处理
*/
public <T> IPage<T> postprocessPage(IPage<T> page) {
// 计算总页数
long pages = page.getTotal() / page.getSize();
if (page.getTotal() % page.getSize() != 0) {
pages++;
}
page.setPages(pages);
// 处理溢出情况
if (page.getCurrent() > pages && pages > 0) {
page.setCurrent(pages);
}
return page;
}
/**
* 创建分页对象
*/
public <T> Page<T> createPage(Integer current, Integer size) {
current = current != null && current > 0 ? current : 1;
size = size != null && size > 0 ? size : 10;
size = Math.min(size, 1000); // 限制最大分页大小
return new Page<>(current, size);
}
/**
* 创建分页对象(带排序)
*/
public <T> Page<T> createPage(Integer current, Integer size, String orderBy, String orderDirection) {
Page<T> page = createPage(current, size);
if (StringUtils.isNotBlank(orderBy)) {
boolean isAsc = "asc".equalsIgnoreCase(orderDirection);
// 安全的排序字段检查
if (isValidOrderField(orderBy)) {
OrderItem orderItem = new OrderItem();
orderItem.setColumn(orderBy);
orderItem.setAsc(isAsc);
page.addOrder(orderItem);
}
}
return page;
}
/**
* 验证排序字段是否安全
*/
private boolean isValidOrderField(String field) {
// 白名单验证,防止SQL注入
Set<String> allowedFields = Set.of(
"id", "name", "age", "email", "phone",
"create_time", "update_time", "status"
);
return allowedFields.contains(field.toLowerCase());
}
}
分页工具类
java
/**
* 分页工具类
*/
@UtilityClass
public class PageUtils {
/**
* 构建分页对象
*/
public static <T> Page<T> buildPage(PageRequest pageRequest) {
Integer current = pageRequest.getCurrent();
Integer size = pageRequest.getSize();
current = current != null && current > 0 ? current : 1;
size = size != null && size > 0 ? size : 10;
size = Math.min(size, 1000);
Page<T> page = new Page<>(current, size);
// 处理排序
if (CollectionUtils.isNotEmpty(pageRequest.getOrders())) {
for (PageRequest.Order order : pageRequest.getOrders()) {
if (isValidOrderField(order.getField())) {
page.addOrder(new OrderItem(order.getField(), "asc".equalsIgnoreCase(order.getDirection())));
}
}
}
return page;
}
/**
* 转换分页结果
*/
public static <T, R> IPage<R> convertPage(IPage<T> sourcePage, Function<T, R> converter) {
Page<R> targetPage = new Page<>(sourcePage.getCurrent(), sourcePage.getSize(), sourcePage.getTotal());
targetPage.setPages(sourcePage.getPages());
targetPage.setSearchCount(sourcePage.searchCount());
List<R> records = sourcePage.getRecords().stream()
.map(converter)
.collect(Collectors.toList());
targetPage.setRecords(records);
return targetPage;
}
/**
* 批量转换分页结果
*/
public static <T, R> IPage<R> convertPageBatch(IPage<T> sourcePage, Function<List<T>, List<R>> batchConverter) {
Page<R> targetPage = new Page<>(sourcePage.getCurrent(), sourcePage.getSize(), sourcePage.getTotal());
targetPage.setPages(sourcePage.getPages());
targetPage.setSearchCount(sourcePage.searchCount());
List<R> records = batchConverter.apply(sourcePage.getRecords());
targetPage.setRecords(records);
return targetPage;
}
/**
* 创建空分页结果
*/
public static <T> IPage<T> emptyPage(long current, long size) {
return new Page<>(current, size, 0);
}
/**
* 验证排序字段
*/
private static boolean isValidOrderField(String field) {
if (StringUtils.isBlank(field)) {
return false;
}
// 检查是否包含危险字符
String dangerousChars = "';--/**/union|select|insert|update|delete|drop|create|alter|exec|execute";
String lowerField = field.toLowerCase();
for (String dangerous : dangerousChars.split("\\|")) {
if (lowerField.contains(dangerous)) {
return false;
}
}
// 只允许字母、数字、下划线
return field.matches("^[a-zA-Z_][a-zA-Z0-9_]*$");
}
/**
* 计算偏移量
*/
public static long calculateOffset(long current, long size) {
return (current - 1) * size;
}
/**
* 计算总页数
*/
public static long calculatePages(long total, long size) {
if (size == 0) {
return 0;
}
return (total + size - 1) / size;
}
}
/**
* 分页请求DTO
*/
@Data
public class PageRequest {
private Integer current = 1;
private Integer size = 10;
private List<Order> orders;
@Data
public static class Order {
private String field;
private String direction = "asc";
}
}
Page分页对象
简要描述:Page是MybatisPlus提供的分页对象,封装了分页查询的所有参数和结果,支持链式调用和灵活的分页配置。
核心概念:
- 分页参数:当前页、每页大小、总记录数、总页数
- 排序支持:支持多字段排序和动态排序
- 优化查询:支持禁用count查询提升性能
- 结果封装:自动计算分页相关信息
- 类型安全:泛型支持,类型安全
基础使用
java
@Service
public class UserService extends ServiceImpl<UserMapper, User> {
/**
* 基础分页查询
*/
public IPage<User> basicPageQuery(Integer current, Integer size) {
// 创建分页对象
Page<User> page = new Page<>(current, size);
// 执行分页查询
return this.page(page);
}
/**
* 带条件的分页查询
*/
public IPage<User> pageQueryWithCondition(Integer current, Integer size, String name, Integer status) {
Page<User> page = new Page<>(current, size);
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(name), User::getName, name)
.eq(status != null, User::getStatus, status)
.eq(User::getDeleted, 0);
return this.page(page, wrapper);
}
/**
* 带排序的分页查询
*/
public IPage<User> pageQueryWithOrder(Integer current, Integer size, String orderBy, String orderDirection) {
Page<User> page = new Page<>(current, size);
// 添加排序
if (StringUtils.isNotBlank(orderBy)) {
boolean isAsc = "asc".equalsIgnoreCase(orderDirection);
page.addOrder(new OrderItem(orderBy, isAsc));
}
return this.page(page);
}
/**
* 多字段排序分页查询
*/
public IPage<User> pageQueryWithMultiOrder(Integer current, Integer size, List<OrderField> orderFields) {
Page<User> page = new Page<>(current, size);
// 添加多个排序字段
if (CollectionUtils.isNotEmpty(orderFields)) {
for (OrderField orderField : orderFields) {
boolean isAsc = "asc".equalsIgnoreCase(orderField.getDirection());
page.addOrder(new OrderItem(orderField.getField(), isAsc));
}
}
return this.page(page);
}
}
Page对象详解
java
/**
* Page对象使用示例
*/
@Service
public class PageExampleService {
/**
* Page对象属性详解
*/
public void pagePropertiesExample() {
Page<User> page = new Page<>();
// 基础属性设置
page.setCurrent(1); // 当前页,从1开始
page.setSize(10); // 每页大小
page.setTotal(100); // 总记录数(通常由框架自动设置)
page.setPages(10); // 总页数(通常由框架自动计算)
// 优化设置
page.setSearchCount(true); // 是否查询总记录数,默认true
page.setOptimizeCountSql(true); // 是否优化count查询,默认true
page.setMaxLimit(1000L); // 单页最大限制
// 排序设置
page.addOrder(OrderItem.asc("name")); // 升序
page.addOrder(OrderItem.desc("create_time")); // 降序
// 获取分页信息
long current = page.getCurrent(); // 当前页
long size = page.getSize(); // 每页大小
long total = page.getTotal(); // 总记录数
long pages = page.getPages(); // 总页数
List<User> records = page.getRecords(); // 当前页数据
// 分页计算
boolean hasNext = page.hasNext(); // 是否有下一页
boolean hasPrevious = page.hasPrevious(); // 是否有上一页
long offset = page.offset(); // 偏移量
}
/**
* 禁用count查询(提升性能)
*/
public IPage<User> pageWithoutCount(Integer current, Integer size) {
Page<User> page = new Page<>(current, size);
// 禁用count查询,适用于不需要总数的场景
page.setSearchCount(false);
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime);
return userMapper.selectPage(page, wrapper);
}
/**
* 自定义count查询
*/
public IPage<User> pageWithCustomCount(Integer current, Integer size, String keyword) {
Page<User> page = new Page<>(current, size);
// 构建查询条件
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getDeleted, 0);
if (StringUtils.isNotBlank(keyword)) {
wrapper.and(w -> w.like(User::getName, keyword)
.or().like(User::getEmail, keyword));
}
// 先执行count查询
Long total = userMapper.selectCount(wrapper);
page.setTotal(total);
if (total > 0) {
// 有数据才执行分页查询
page.setSearchCount(false); // 禁用自动count
List<User> records = userMapper.selectList(wrapper.last(
"LIMIT " + page.offset() + ", " + page.getSize()
));
page.setRecords(records);
}
return page;
}
/**
* 流式分页查询(大数据量)
*/
public void streamPageQuery(Consumer<List<User>> processor) {
int pageSize = 1000;
int current = 1;
while (true) {
Page<User> page = new Page<>(current, pageSize);
page.setSearchCount(false); // 禁用count查询
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getDeleted, 0)
.orderByAsc(User::getId); // 确保排序稳定
IPage<User> result = userMapper.selectPage(page, wrapper);
if (CollectionUtils.isEmpty(result.getRecords())) {
break; // 没有更多数据
}
// 处理当前页数据
processor.accept(result.getRecords());
if (result.getRecords().size() < pageSize) {
break; // 最后一页
}
current++;
}
}
}
分页结果处理
java
/**
* 分页结果处理示例
*/
@Service
public class PageResultService {
/**
* 分页结果转换
*/
public IPage<UserVO> convertPageResult(IPage<User> userPage) {
// 方式1:使用工具类转换
return PageUtils.convertPage(userPage, this::convertToVO);
}
/**
* 批量转换分页结果(性能更好)
*/
public IPage<UserVO> batchConvertPageResult(IPage<User> userPage) {
return PageUtils.convertPageBatch(userPage, users -> {
// 批量查询关联数据
List<Long> deptIds = users.stream()
.map(User::getDeptId)
.distinct()
.collect(Collectors.toList());
Map<Long, Dept> deptMap = deptService.listByIds(deptIds)
.stream()
.collect(Collectors.toMap(Dept::getId, Function.identity()));
// 批量转换
return users.stream()
.map(user -> {
UserVO vo = convertToVO(user);
vo.setDeptName(Optional.ofNullable(deptMap.get(user.getDeptId()))
.map(Dept::getName)
.orElse(""));
return vo;
})
.collect(Collectors.toList());
});
}
/**
* 分页结果包装
*/
public PageResult<UserVO> wrapPageResult(IPage<User> userPage) {
IPage<UserVO> voPage = convertPageResult(userPage);
return PageResult.<UserVO>builder()
.current(voPage.getCurrent())
.size(voPage.getSize())
.total(voPage.getTotal())
.pages(voPage.getPages())
.records(voPage.getRecords())
.hasNext(voPage.hasNext())
.hasPrevious(voPage.hasPrevious())
.build();
}
/**
* 实体转VO
*/
private UserVO convertToVO(User user) {
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
// 其他转换逻辑
return vo;
}
}
/**
* 自定义分页结果包装类
*/
@Data
@Builder
public class PageResult<T> {
private Long current; // 当前页
private Long size; // 每页大小
private Long total; // 总记录数
private Long pages; // 总页数
private List<T> records; // 当前页数据
private Boolean hasNext; // 是否有下一页
private Boolean hasPrevious; // 是否有上一页
public static <T> PageResultBuilder<T> builder() {
return new PageResultBuilder<>();
}
}
自定义分页实现
简要描述:当MybatisPlus内置的分页功能无法满足复杂业务需求时,可以通过自定义分页实现来扩展分页功能,支持复杂查询、多表关联、聚合统计等场景。
核心概念:
- 自定义SQL:编写复杂的分页查询SQL
- 手动分页:手动计算偏移量和限制条件
- 结果映射:自定义结果集映射
- 性能优化:针对特定场景的性能优化
- 扩展功能:支持复杂的业务逻辑
自定义Mapper分页
java
/**
* 自定义分页Mapper
*/
@Mapper
public interface CustomUserMapper extends BaseMapper<User> {
/**
* 自定义分页查询(注解方式)
*/
@Select("SELECT u.*, d.name as dept_name " +
"FROM user u LEFT JOIN dept d ON u.dept_id = d.id " +
"WHERE u.deleted = 0 " +
"${ew.customSqlSegment}")
IPage<UserWithDeptVO> selectUserWithDeptPage(IPage<UserWithDeptVO> page, @Param(Constants.WRAPPER) Wrapper<User> wrapper);
/**
* 自定义count查询
*/
@Select("SELECT COUNT(*) FROM user u " +
"LEFT JOIN dept d ON u.dept_id = d.id " +
"WHERE u.deleted = 0 " +
"${ew.customSqlSegment}")
Long selectUserWithDeptCount(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
/**
* 复杂统计分页查询
*/
@Select("SELECT u.dept_id, d.name as dept_name, " +
"COUNT(*) as user_count, " +
"AVG(u.age) as avg_age, " +
"SUM(u.salary) as total_salary " +
"FROM user u " +
"LEFT JOIN dept d ON u.dept_id = d.id " +
"WHERE u.deleted = 0 " +
"${ew.customSqlSegment} " +
"GROUP BY u.dept_id, d.name")
IPage<DeptStatVO> selectDeptStatPage(IPage<DeptStatVO> page, @Param(Constants.WRAPPER) Wrapper<User> wrapper);
/**
* XML方式自定义分页查询
*/
IPage<UserDetailVO> selectUserDetailPage(IPage<UserDetailVO> page, @Param("query") UserQueryDTO query);
/**
* 手动分页查询
*/
List<UserWithDeptVO> selectUserWithDeptList(@Param("query") UserQueryDTO query,
@Param("offset") Long offset,
@Param("limit") Long limit);
/**
* 手动count查询
*/
Long selectUserWithDeptListCount(@Param("query") UserQueryDTO query);
}
XML自定义分页
xml
<!-- CustomUserMapper.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">
<mapper namespace="com.example.mapper.CustomUserMapper">
<!-- 结果映射 -->
<resultMap id="UserDetailVOMap" type="com.example.vo.UserDetailVO">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="email" property="email"/>
<result column="phone" property="phone"/>
<result column="age" property="age"/>
<result column="salary" property="salary"/>
<result column="status" property="status"/>
<result column="create_time" property="createTime"/>
<result column="dept_name" property="deptName"/>
<result column="role_names" property="roleNames"/>
<result column="permission_count" property="permissionCount"/>
</resultMap>
<!-- 复杂分页查询 -->
<select id="selectUserDetailPage" resultMap="UserDetailVOMap">
SELECT
u.id,
u.name,
u.email,
u.phone,
u.age,
u.salary,
u.status,
u.create_time,
d.name as dept_name,
GROUP_CONCAT(r.name) as role_names,
COUNT(DISTINCT p.id) as permission_count
FROM user u
LEFT JOIN dept d ON u.dept_id = d.id
LEFT JOIN user_role ur ON u.id = ur.user_id
LEFT JOIN role r ON ur.role_id = r.id
LEFT JOIN role_permission rp ON r.id = rp.role_id
LEFT JOIN permission p ON rp.permission_id = p.id
<where>
u.deleted = 0
<if test="query.name != null and query.name != ''">
AND u.name LIKE CONCAT('%', #{query.name}, '%')
</if>
<if test="query.email != null and query.email != ''">
AND u.email LIKE CONCAT('%', #{query.email}, '%')
</if>
<if test="query.status != null">
AND u.status = #{query.status}
</if>
<if test="query.deptIds != null and query.deptIds.size() > 0">
AND u.dept_id IN
<foreach collection="query.deptIds" item="deptId" open="(" separator="," close=")">
#{deptId}
</foreach>
</if>
<if test="query.minAge != null">
AND u.age >= #{query.minAge}
</if>
<if test="query.maxAge != null">
AND u.age <= #{query.maxAge}
</if>
<if test="query.createTimeStart != null">
AND u.create_time >= #{query.createTimeStart}
</if>
<if test="query.createTimeEnd != null">
AND u.create_time <= #{query.createTimeEnd}
</if>
</where>
GROUP BY u.id, u.name, u.email, u.phone, u.age, u.salary, u.status, u.create_time, d.name
<if test="query.orderBy != null and query.orderBy != ''">
ORDER BY
<choose>
<when test="query.orderBy == 'name'">
u.name
</when>
<when test="query.orderBy == 'age'">
u.age
</when>
<when test="query.orderBy == 'salary'">
u.salary
</when>
<when test="query.orderBy == 'createTime'">
u.create_time
</when>
<otherwise>
u.create_time
</otherwise>
</choose>
<if test="query.orderDirection != null and query.orderDirection == 'asc'">
ASC
</if>
<if test="query.orderDirection == null or query.orderDirection != 'asc'">
DESC
</if>
</if>
</select>
<!-- 手动分页查询 -->
<select id="selectUserWithDeptList" resultType="com.example.vo.UserWithDeptVO">
SELECT
u.id,
u.name,
u.email,
u.phone,
u.age,
u.status,
u.create_time,
d.name as deptName
FROM user u
LEFT JOIN dept d ON u.dept_id = d.id
<where>
u.deleted = 0
<if test="query.name != null and query.name != ''">
AND u.name LIKE CONCAT('%', #{query.name}, '%')
</if>
<if test="query.status != null">
AND u.status = #{query.status}
</if>
<if test="query.deptIds != null and query.deptIds.size() > 0">
AND u.dept_id IN
<foreach collection="query.deptIds" item="deptId" open="(" separator="," close=")">
#{deptId}
</foreach>
</if>
</where>
ORDER BY u.create_time DESC
LIMIT #{offset}, #{limit}
</select>
<!-- 手动count查询 -->
<select id="selectUserWithDeptListCount" resultType="java.lang.Long">
SELECT COUNT(*)
FROM user u
LEFT JOIN dept d ON u.dept_id = d.id
<where>
u.deleted = 0
<if test="query.name != null and query.name != ''">
AND u.name LIKE CONCAT('%', #{query.name}, '%')
</if>
<if test="query.status != null">
AND u.status = #{query.status}
</if>
<if test="query.deptIds != null and query.deptIds.size() > 0">
AND u.dept_id IN
<foreach collection="query.deptIds" item="deptId" open="(" separator="," close=")">
#{deptId}
</foreach>
</if>
</where>
</select>
</mapper>
自定义分页Service
java
/**
* 自定义分页Service
*/
@Service
public class CustomPageService {
@Autowired
private CustomUserMapper customUserMapper;
/**
* 复杂关联查询分页
*/
public IPage<UserWithDeptVO> pageUserWithDept(Integer current, Integer size, UserQueryDTO query) {
Page<UserWithDeptVO> page = new Page<>(current, size);
// 构建查询条件
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(query.getName()), User::getName, query.getName())
.eq(query.getStatus() != null, User::getStatus, query.getStatus())
.in(CollectionUtils.isNotEmpty(query.getDeptIds()), User::getDeptId, query.getDeptIds());
return customUserMapper.selectUserWithDeptPage(page, wrapper);
}
/**
* 手动分页实现
*/
public IPage<UserWithDeptVO> manualPageUserWithDept(Integer current, Integer size, UserQueryDTO query) {
// 计算偏移量
long offset = (current - 1) * size;
// 查询总数
Long total = customUserMapper.selectUserWithDeptListCount(query);
// 查询数据
List<UserWithDeptVO> records = Collections.emptyList();
if (total > 0) {
records = customUserMapper.selectUserWithDeptList(query, offset, (long) size);
}
// 构建分页结果
Page<UserWithDeptVO> page = new Page<>(current, size, total);
page.setRecords(records);
return page;
}
/**
* 分步查询分页(先查主表,再查关联数据)
*/
public IPage<UserDetailVO> stepPageUserDetail(Integer current, Integer size, UserQueryDTO query) {
// 第一步:分页查询用户基础信息
Page<User> userPage = new Page<>(current, size);
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(query.getName()), User::getName, query.getName())
.eq(query.getStatus() != null, User::getStatus, query.getStatus())
.eq(User::getDeleted, 0);
IPage<User> userResult = userService.page(userPage, wrapper);
// 第二步:批量查询关联数据
List<UserDetailVO> detailList = Collections.emptyList();
if (CollectionUtils.isNotEmpty(userResult.getRecords())) {
List<Long> userIds = userResult.getRecords().stream()
.map(User::getId)
.collect(Collectors.toList());
// 批量查询部门信息
List<Long> deptIds = userResult.getRecords().stream()
.map(User::getDeptId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
Map<Long, Dept> deptMap = Collections.emptyMap();
if (CollectionUtils.isNotEmpty(deptIds)) {
deptMap = deptService.listByIds(deptIds).stream()
.collect(Collectors.toMap(Dept::getId, Function.identity()));
}
// 批量查询角色信息
Map<Long, List<Role>> userRoleMap = roleService.getUserRoleMap(userIds);
// 组装结果
final Map<Long, Dept> finalDeptMap = deptMap;
detailList = userResult.getRecords().stream()
.map(user -> {
UserDetailVO detail = new UserDetailVO();
BeanUtils.copyProperties(user, detail);
// 设置部门信息
Optional.ofNullable(finalDeptMap.get(user.getDeptId()))
.ifPresent(dept -> detail.setDeptName(dept.getName()));
// 设置角色信息
List<Role> roles = userRoleMap.getOrDefault(user.getId(), Collections.emptyList());
detail.setRoleNames(roles.stream()
.map(Role::getName)
.collect(Collectors.joining(",")));
return detail;
})
.collect(Collectors.toList());
}
// 构建分页结果
Page<UserDetailVO> resultPage = new Page<>(userResult.getCurrent(), userResult.getSize(), userResult.getTotal());
resultPage.setRecords(detailList);
return resultPage;
}
/**
* 游标分页(适用于大数据量场景)
*/
public CursorPageResult<User> cursorPageQuery(Long lastId, Integer size) {
size = Math.min(size, 1000); // 限制最大查询数量
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getDeleted, 0)
.gt(lastId != null, User::getId, lastId)
.orderByAsc(User::getId)
.last("LIMIT " + (size + 1)); // 多查一条用于判断是否有下一页
List<User> records = userService.list(wrapper);
boolean hasNext = records.size() > size;
if (hasNext) {
records = records.subList(0, size); // 移除多查的一条
}
Long nextCursor = null;
if (CollectionUtils.isNotEmpty(records)) {
nextCursor = records.get(records.size() - 1).getId();
}
return CursorPageResult.<User>builder()
.records(records)
.hasNext(hasNext)
.nextCursor(nextCursor)
.size(size)
.build();
}
}
/**
* 游标分页结果
*/
@Data
@Builder
public class CursorPageResult<T> {
private List<T> records; // 当前页数据
private Boolean hasNext; // 是否有下一页
private Long nextCursor; // 下一页游标
private Integer size; // 页大小
}
/**
* 用户详情VO
*/
@Data
public class UserDetailVO {
private Long id;
private String name;
private String email;
private String phone;
private Integer age;
private BigDecimal salary;
private Integer status;
private LocalDateTime createTime;
private String deptName;
private String roleNames;
private Integer permissionCount;
}
/**
* 用户部门VO
*/
@Data
public class UserWithDeptVO {
private Long id;
private String name;
private String email;
private String phone;
private Integer age;
private Integer status;
private LocalDateTime createTime;
private String deptName;
}
/**
* 部门统计VO
*/
@Data
public class DeptStatVO {
private Long deptId;
private String deptName;
private Long userCount;
private Double avgAge;
private BigDecimal totalSalary;
}
分页性能优化
简要描述:分页查询是系统中最常见的操作之一,合理的性能优化可以显著提升系统响应速度和用户体验,特别是在大数据量场景下。
核心概念:
- 索引优化:合理使用索引提升查询效率
- Count优化:减少或优化count查询
- 深分页优化:解决深分页性能问题
- 缓存策略:合理使用缓存减少数据库压力
- SQL优化:优化查询语句结构
索引优化策略
java
/**
* 分页查询索引优化
*/
@Service
public class PageOptimizationService {
/**
* 基于索引的分页查询优化
*/
public IPage<User> optimizedPageQuery(Integer current, Integer size, UserQueryDTO query) {
Page<User> page = new Page<>(current, size);
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// 优化1:使用复合索引 (status, create_time, id)
wrapper.eq(User::getStatus, query.getStatus())
.ge(query.getCreateTimeStart() != null, User::getCreateTime, query.getCreateTimeStart())
.le(query.getCreateTimeEnd() != null, User::getCreateTime, query.getCreateTimeEnd())
.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime, User::getId); // 确保排序字段有索引
return userService.page(page, wrapper);
}
/**
* 覆盖索引优化(只查询索引字段)
*/
public IPage<UserSimpleVO> coveringIndexQuery(Integer current, Integer size, Integer status) {
Page<UserSimpleVO> page = new Page<>(current, size);
// 只查询索引覆盖的字段,避免回表
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "name", "status", "create_time") // 这些字段在索引中
.eq("status", status)
.eq("deleted", 0)
.orderByDesc("create_time");
IPage<User> result = userService.page(new Page<>(current, size), wrapper);
// 转换为VO
List<UserSimpleVO> voList = result.getRecords().stream()
.map(user -> {
UserSimpleVO vo = new UserSimpleVO();
vo.setId(user.getId());
vo.setName(user.getName());
vo.setStatus(user.getStatus());
vo.setCreateTime(user.getCreateTime());
return vo;
})
.collect(Collectors.toList());
Page<UserSimpleVO> voPage = new Page<>(result.getCurrent(), result.getSize(), result.getTotal());
voPage.setRecords(voList);
return voPage;
}
/**
* 强制使用索引
*/
public IPage<User> forceIndexQuery(Integer current, Integer size, String indexName) {
Page<User> page = new Page<>(current, size);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("deleted", 0)
.orderByDesc("create_time")
.last("FORCE INDEX (" + indexName + ")"); // 强制使用指定索引
return userService.page(page, wrapper);
}
}
/**
* 用户简单VO(用于覆盖索引查询)
*/
@Data
public class UserSimpleVO {
private Long id;
private String name;
private Integer status;
private LocalDateTime createTime;
}
Count查询优化
java
/**
* Count查询优化策略
*/
@Service
public class CountOptimizationService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 禁用count查询(适用于不需要总数的场景)
*/
public IPage<User> pageWithoutCount(Integer current, Integer size) {
Page<User> page = new Page<>(current, size);
page.setSearchCount(false); // 禁用count查询
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime);
return userService.page(page, wrapper);
}
/**
* 缓存count结果
*/
public IPage<User> pageWithCachedCount(Integer current, Integer size, UserQueryDTO query) {
String countKey = "user:count:" + DigestUtils.md5Hex(JSON.toJSONString(query));
// 尝试从缓存获取count
Long cachedCount = (Long) redisTemplate.opsForValue().get(countKey);
Page<User> page = new Page<>(current, size);
if (cachedCount != null) {
// 使用缓存的count,禁用自动count查询
page.setTotal(cachedCount);
page.setSearchCount(false);
} else {
// 启用count查询
page.setSearchCount(true);
}
LambdaQueryWrapper<User> wrapper = buildQueryWrapper(query);
IPage<User> result = userService.page(page, wrapper);
// 缓存count结果(5分钟)
if (cachedCount == null) {
redisTemplate.opsForValue().set(countKey, result.getTotal(), 5, TimeUnit.MINUTES);
}
return result;
}
/**
* 估算count(适用于大数据量场景)
*/
public IPage<User> pageWithEstimatedCount(Integer current, Integer size, UserQueryDTO query) {
Page<User> page = new Page<>(current, size);
// 对于大数据量,使用估算count
if (current <= 10) { // 前10页使用精确count
page.setSearchCount(true);
} else { // 后续页使用估算count
page.setSearchCount(false);
// 估算总数(基于前10页的平均值)
long estimatedTotal = estimateTotal(query);
page.setTotal(estimatedTotal);
}
LambdaQueryWrapper<User> wrapper = buildQueryWrapper(query);
return userService.page(page, wrapper);
}
/**
* 分段count查询
*/
public IPage<User> pageWithSegmentedCount(Integer current, Integer size, UserQueryDTO query) {
Page<User> page = new Page<>(current, size);
// 只在第一页查询count
if (current == 1) {
page.setSearchCount(true);
} else {
page.setSearchCount(false);
// 从缓存或session中获取总数
Long totalFromCache = getTotalFromCache(query);
if (totalFromCache != null) {
page.setTotal(totalFromCache);
}
}
LambdaQueryWrapper<User> wrapper = buildQueryWrapper(query);
IPage<User> result = userService.page(page, wrapper);
// 缓存总数
if (current == 1) {
cacheTotalCount(query, result.getTotal());
}
return result;
}
/**
* 构建查询条件
*/
private LambdaQueryWrapper<User> buildQueryWrapper(UserQueryDTO query) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(query.getName()), User::getName, query.getName())
.eq(query.getStatus() != null, User::getStatus, query.getStatus())
.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime);
return wrapper;
}
/**
* 估算总数
*/
private long estimateTotal(UserQueryDTO query) {
// 基于统计信息或采样估算
// 这里简化处理,实际可以基于数据库统计信息
return 100000L; // 示例值
}
/**
* 从缓存获取总数
*/
private Long getTotalFromCache(UserQueryDTO query) {
String key = "user:total:" + DigestUtils.md5Hex(JSON.toJSONString(query));
return (Long) redisTemplate.opsForValue().get(key);
}
/**
* 缓存总数
*/
private void cacheTotalCount(UserQueryDTO query, Long total) {
String key = "user:total:" + DigestUtils.md5Hex(JSON.toJSONString(query));
redisTemplate.opsForValue().set(key, total, 10, TimeUnit.MINUTES);
}
}
深分页优化
java
/**
* 深分页优化策略
*/
@Service
public class DeepPageOptimizationService {
/**
* 游标分页(推荐用于深分页)
*/
public CursorPageResult<User> cursorBasedPagination(Long lastId, Integer size) {
size = Math.min(size, 100); // 限制页大小
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getDeleted, 0)
.gt(lastId != null, User::getId, lastId)
.orderByAsc(User::getId)
.last("LIMIT " + (size + 1)); // 多查一条判断是否有下一页
List<User> records = userService.list(wrapper);
boolean hasNext = records.size() > size;
if (hasNext) {
records = records.subList(0, size);
}
Long nextCursor = null;
if (!records.isEmpty()) {
nextCursor = records.get(records.size() - 1).getId();
}
return CursorPageResult.<User>builder()
.records(records)
.hasNext(hasNext)
.nextCursor(nextCursor)
.size(size)
.build();
}
/**
* 子查询优化深分页
*/
public IPage<User> subQueryOptimizedPage(Integer current, Integer size) {
// 限制深分页
if (current > 1000) {
throw new BusinessException("分页深度不能超过1000页");
}
Page<User> page = new Page<>(current, size);
// 使用子查询先获取ID,再关联查询完整数据
String subQuerySql =
"SELECT u.* FROM user u " +
"INNER JOIN (" +
" SELECT id FROM user " +
" WHERE deleted = 0 " +
" ORDER BY create_time DESC " +
" LIMIT " + page.offset() + ", " + page.getSize() +
") t ON u.id = t.id " +
"ORDER BY u.create_time DESC";
// 执行自定义SQL
List<User> records = userMapper.selectByCustomSql(subQuerySql);
// 单独查询总数
LambdaQueryWrapper<User> countWrapper = new LambdaQueryWrapper<>();
countWrapper.eq(User::getDeleted, 0);
Long total = userService.count(countWrapper);
page.setRecords(records);
page.setTotal(total);
page.setSearchCount(false);
return page;
}
/**
* 延迟关联优化
*/
public IPage<UserVO> deferredJoinPage(Integer current, Integer size, UserQueryDTO query) {
Page<User> page = new Page<>(current, size);
// 第一步:只查询主表ID和排序字段
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "create_time")
.eq("deleted", 0)
.like(StringUtils.isNotBlank(query.getName()), "name", query.getName())
.orderByDesc("create_time");
IPage<User> idResult = userService.page(page, wrapper);
// 第二步:根据ID批量查询完整数据
List<UserVO> voList = Collections.emptyList();
if (!idResult.getRecords().isEmpty()) {
List<Long> ids = idResult.getRecords().stream()
.map(User::getId)
.collect(Collectors.toList());
// 批量查询完整用户信息
List<User> users = userService.listByIds(ids);
// 保持原有排序
Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, Function.identity()));
voList = ids.stream()
.map(userMap::get)
.filter(Objects::nonNull)
.map(this::convertToVO)
.collect(Collectors.toList());
}
Page<UserVO> voPage = new Page<>(idResult.getCurrent(), idResult.getSize(), idResult.getTotal());
voPage.setRecords(voList);
return voPage;
}
/**
* 分片查询(超大数据量)
*/
public void shardedPageQuery(UserQueryDTO query, Consumer<List<User>> processor) {
int shardSize = 10000; // 每片大小
int pageSize = 1000; // 每页大小
// 获取数据范围
DateRange dateRange = getDateRange(query);
List<DateRange> shards = splitDateRange(dateRange, shardSize);
for (DateRange shard : shards) {
int current = 1;
while (true) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getDeleted, 0)
.ge(User::getCreateTime, shard.getStart())
.lt(User::getCreateTime, shard.getEnd())
.orderByAsc(User::getId)
.last("LIMIT " + ((current - 1) * pageSize) + ", " + pageSize);
List<User> records = userService.list(wrapper);
if (records.isEmpty()) {
break;
}
processor.accept(records);
if (records.size() < pageSize) {
break;
}
current++;
}
}
}
private UserVO convertToVO(User user) {
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
return vo;
}
private DateRange getDateRange(UserQueryDTO query) {
// 获取查询的日期范围
return new DateRange(query.getCreateTimeStart(), query.getCreateTimeEnd());
}
private List<DateRange> splitDateRange(DateRange range, int shardSize) {
// 将日期范围分片
List<DateRange> shards = new ArrayList<>();
// 实现分片逻辑
return shards;
}
}
/**
* 日期范围
*/
@Data
@AllArgsConstructor
public class DateRange {
private LocalDateTime start;
private LocalDateTime end;
}
缓存优化策略
java
/**
* 分页缓存优化
*/
@Service
public class PageCacheOptimizationService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 分页结果缓存
*/
@Cacheable(value = "userPage", key = "#current + ':' + #size + ':' + #query.hashCode()")
public IPage<UserVO> cachedPageQuery(Integer current, Integer size, UserQueryDTO query) {
Page<User> page = new Page<>(current, size);
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(query.getName()), User::getName, query.getName())
.eq(query.getStatus() != null, User::getStatus, query.getStatus())
.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime);
IPage<User> result = userService.page(page, wrapper);
// 转换为VO
List<UserVO> voList = result.getRecords().stream()
.map(this::convertToVO)
.collect(Collectors.toList());
Page<UserVO> voPage = new Page<>(result.getCurrent(), result.getSize(), result.getTotal());
voPage.setRecords(voList);
return voPage;
}
/**
* 热点数据预加载
*/
@PostConstruct
public void preloadHotData() {
// 预加载前几页热点数据
CompletableFuture.runAsync(() -> {
try {
UserQueryDTO query = new UserQueryDTO();
query.setStatus(1); // 活跃用户
// 预加载前5页
for (int i = 1; i <= 5; i++) {
cachedPageQuery(i, 20, query);
Thread.sleep(100); // 避免对数据库造成压力
}
} catch (Exception e) {
log.error("预加载热点数据失败", e);
}
});
}
/**
* 分层缓存策略
*/
public IPage<UserVO> tieredCachePageQuery(Integer current, Integer size, UserQueryDTO query) {
String cacheKey = buildCacheKey(current, size, query);
// L1缓存:本地缓存(Caffeine)
IPage<UserVO> result = localCache.get(cacheKey);
if (result != null) {
return result;
}
// L2缓存:Redis缓存
result = (IPage<UserVO>) redisTemplate.opsForValue().get(cacheKey);
if (result != null) {
// 回写到本地缓存
localCache.put(cacheKey, result);
return result;
}
// L3:数据库查询
result = doPageQuery(current, size, query);
// 写入缓存
localCache.put(cacheKey, result);
redisTemplate.opsForValue().set(cacheKey, result, 5, TimeUnit.MINUTES);
return result;
}
/**
* 缓存失效策略
*/
@CacheEvict(value = "userPage", allEntries = true)
public void evictUserPageCache() {
// 当用户数据发生变化时,清除相关缓存
}
/**
* 智能缓存更新
*/
public void smartCacheUpdate(User user) {
// 只清除受影响的缓存页
String pattern = "userPage:*";
Set<String> keys = redisTemplate.keys(pattern);
if (keys != null) {
for (String key : keys) {
// 判断是否需要清除该缓存
if (shouldEvictCache(key, user)) {
redisTemplate.delete(key);
}
}
}
}
private boolean shouldEvictCache(String cacheKey, User user) {
// 根据缓存key和用户变更情况判断是否需要清除缓存
// 实现具体的判断逻辑
return true;
}
private String buildCacheKey(Integer current, Integer size, UserQueryDTO query) {
return String.format("userPage:%d:%d:%s", current, size, DigestUtils.md5Hex(JSON.toJSONString(query)));
}
private IPage<UserVO> doPageQuery(Integer current, Integer size, UserQueryDTO query) {
// 实际的数据库查询逻辑
return cachedPageQuery(current, size, query);
}
private UserVO convertToVO(User user) {
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
return vo;
}
@Autowired
private Cache<String, IPage<UserVO>> localCache; // 本地缓存
}
分页插件源码分析
简要描述:深入理解MybatisPlus分页插件的实现原理,有助于更好地使用分页功能和进行性能调优。
核心概念:
- 拦截器机制:基于Mybatis拦截器实现
- SQL解析:解析和重写SQL语句
- 方言支持:支持多种数据库方言
- 插件链:支持多个插件协同工作
- 扩展机制:支持自定义扩展
分页插件核心组件
java
/**
* 分页插件核心类分析
*/
public class PaginationInnerInterceptor implements InnerInterceptor {
/**
* 数据库方言
*/
private IDialect dialect;
/**
* 溢出总页数后是否进行处理
*/
protected boolean overflow = false;
/**
* 单页分页条数限制
*/
protected Long maxLimit;
/**
* 数据库类型
*/
private DbType dbType;
/**
* 查询前处理
*/
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
if (page == null) {
return;
}
// 处理参数
handleParameter(ms, boundSql, page);
// 处理分页SQL
handlePageSql(executor, ms, boundSql, page);
}
/**
* 处理分页参数
*/
protected void handleParameter(MappedStatement ms, BoundSql boundSql, IPage<?> page) {
// 检查分页参数合法性
if (page.getSize() < 0) {
throw new MybatisPlusException("分页大小不能小于0");
}
// 检查单页限制
if (maxLimit != null && page.getSize() > maxLimit) {
throw new MybatisPlusException("分页大小超过限制: " + maxLimit);
}
// 处理溢出
if (overflow && page.getCurrent() > page.getPages()) {
page.setCurrent(1);
}
}
/**
* 处理分页SQL
*/
protected void handlePageSql(Executor executor, MappedStatement ms, BoundSql boundSql, IPage<?> page) throws SQLException {
// 1. 处理count查询
if (page.isSearchCount()) {
Long total = executeCountSql(executor, ms, boundSql, page);
page.setTotal(total);
// 如果总数为0,直接返回
if (total == 0) {
return;
}
}
// 2. 构建分页SQL
String originalSql = boundSql.getSql();
String pageSql = buildPageSql(originalSql, page);
// 3. 替换原始SQL
PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
mpBoundSql.sql(pageSql);
}
/**
* 执行count查询
*/
protected Long executeCountSql(Executor executor, MappedStatement ms, BoundSql boundSql, IPage<?> page) throws SQLException {
// 构建count SQL
String countSql = buildCountSql(boundSql.getSql(), page);
// 创建count查询的MappedStatement
MappedStatement countMs = buildCountMappedStatement(ms, countSql);
// 执行count查询
List<Object> result = executor.query(countMs, ms.getParameterMap(),
RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER,
new BoundSql(countMs.getConfiguration(), countSql,
boundSql.getParameterMappings(),
boundSql.getParameterObject()));
return result.isEmpty() ? 0L : Long.parseLong(result.get(0).toString());
}
/**
* 构建count SQL
*/
protected String buildCountSql(String originalSql, IPage<?> page) {
// 使用JSqlParser解析SQL
try {
Select select = (Select) CCJSqlParserUtil.parse(originalSql);
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
// 移除ORDER BY子句(count查询不需要排序)
plainSelect.setOrderByElements(null);
// 检查是否有GROUP BY或DISTINCT
if (hasGroupBy(plainSelect) || hasDistinct(plainSelect)) {
// 使用子查询方式
return "SELECT COUNT(*) FROM (" + select.toString() + ") tmp_count";
} else {
// 直接替换SELECT子句
plainSelect.setSelectItems(Collections.singletonList(new SelectExpressionItem(new Column("COUNT(*)"), null)));
return select.toString();
}
} catch (JSQLParserException e) {
// 解析失败,使用简单包装
return "SELECT COUNT(*) FROM (" + originalSql + ") tmp_count";
}
}
/**
* 构建分页SQL
*/
protected String buildPageSql(String originalSql, IPage<?> page) {
// 根据数据库方言构建分页SQL
return dialect.buildPaginationSql(originalSql, page.offset(), page.getSize());
}
/**
* 检查是否有GROUP BY
*/
private boolean hasGroupBy(PlainSelect plainSelect) {
return plainSelect.getGroupBy() != null && !plainSelect.getGroupBy().getGroupByExpressions().isEmpty();
}
/**
* 检查是否有DISTINCT
*/
private boolean hasDistinct(PlainSelect plainSelect) {
return plainSelect.getDistinct() != null;
}
/**
* 构建count查询的MappedStatement
*/
private MappedStatement buildCountMappedStatement(MappedStatement ms, String countSql) {
MappedStatement.Builder builder = new MappedStatement.Builder(
ms.getConfiguration(),
ms.getId() + "_COUNT",
ms.getSqlSource(),
SqlCommandType.SELECT
);
builder.resource(ms.getResource())
.fetchSize(ms.getFetchSize())
.timeout(ms.getTimeout())
.statementType(ms.getStatementType())
.keyGenerator(ms.getKeyGenerator())
.keyProperty(StringUtils.join(ms.getKeyProperties(), ","))
.keyColumn(StringUtils.join(ms.getKeyColumns(), ","))
.databaseId(ms.getDatabaseId())
.lang(ms.getLang())
.resultOrdered(ms.isResultOrdered())
.resultSets(StringUtils.join(ms.getResultSets(), ","))
.resultMaps(Collections.singletonList(
new ResultMap.Builder(ms.getConfiguration(), "count", Long.class, new ArrayList<>()).build()
))
.flushCacheRequired(ms.isFlushCacheRequired())
.useCache(ms.isUseCache())
.cache(ms.getCache());
return builder.build();
}
}
数据库方言实现
java
/**
* MySQL方言实现
*/
public class MySqlDialect implements IDialect {
@Override
public String buildPaginationSql(String originalSql, long offset, long limit) {
StringBuilder sql = new StringBuilder(originalSql.length() + 20);
sql.append(originalSql);
if (offset == 0) {
sql.append(" LIMIT ").append(limit);
} else {
sql.append(" LIMIT ").append(offset).append(", ").append(limit);
}
return sql.toString();
}
@Override
public DbType getDbType() {
return DbType.MYSQL;
}
}
/**
* Oracle方言实现
*/
public class OracleDialect implements IDialect {
@Override
public String buildPaginationSql(String originalSql, long offset, long limit) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM (")
.append("SELECT TMP_PAGE.*, ROWNUM ROW_ID FROM (")
.append(originalSql)
.append(") TMP_PAGE WHERE ROWNUM <= ")
.append(offset + limit)
.append(") WHERE ROW_ID > ")
.append(offset);
return sql.toString();
}
@Override
public DbType getDbType() {
return DbType.ORACLE;
}
}
/**
* PostgreSQL方言实现
*/
public class PostgreSqlDialect implements IDialect {
@Override
public String buildPaginationSql(String originalSql, long offset, long limit) {
StringBuilder sql = new StringBuilder(originalSql.length() + 20);
sql.append(originalSql);
sql.append(" LIMIT ").append(limit);
if (offset > 0) {
sql.append(" OFFSET ").append(offset);
}
return sql.toString();
}
@Override
public DbType getDbType() {
return DbType.POSTGRE_SQL;
}
}
自定义分页插件扩展
java
/**
* 自定义分页插件扩展
*/
@Component
public class CustomPaginationInterceptor implements InnerInterceptor {
private static final Logger log = LoggerFactory.getLogger(CustomPaginationInterceptor.class);
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
if (page == null) {
return;
}
// 自定义逻辑:记录分页查询日志
logPageQuery(ms, page, boundSql);
// 自定义逻辑:分页参数校验
validatePageParameters(page);
// 自定义逻辑:性能监控
monitorPageQuery(ms, page);
}
/**
* 记录分页查询日志
*/
private void logPageQuery(MappedStatement ms, IPage<?> page, BoundSql boundSql) {
if (log.isDebugEnabled()) {
log.debug("分页查询 - Mapper: {}, 当前页: {}, 页大小: {}, SQL: {}",
ms.getId(), page.getCurrent(), page.getSize(), boundSql.getSql());
}
}
/**
* 分页参数校验
*/
private void validatePageParameters(IPage<?> page) {
// 检查页码
if (page.getCurrent() < 1) {
throw new IllegalArgumentException("页码不能小于1");
}
// 检查页大小
if (page.getSize() < 1) {
throw new IllegalArgumentException("页大小不能小于1");
}
if (page.getSize() > 1000) {
throw new IllegalArgumentException("页大小不能超过1000");
}
// 检查深分页
if (page.getCurrent() > 10000) {
log.warn("检测到深分页查询,当前页: {}, 建议使用游标分页", page.getCurrent());
}
}
/**
* 性能监控
*/
private void monitorPageQuery(MappedStatement ms, IPage<?> page) {
// 记录查询指标
String metricName = "page.query." + ms.getId().replace(".", "_");
// 记录分页深度
Metrics.gauge(metricName + ".depth", page.getCurrent());
// 记录页大小
Metrics.gauge(metricName + ".size", page.getSize());
// 计数器
Metrics.counter(metricName + ".count").increment();
}
}
/**
* 分页插件配置
*/
@Configuration
public class PaginationConfig {
/**
* 配置分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加自定义分页插件
interceptor.addInnerInterceptor(new CustomPaginationInterceptor());
// 添加分页插件
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
paginationInterceptor.setDbType(DbType.MYSQL);
paginationInterceptor.setMaxLimit(1000L); // 单页最大1000条
paginationInterceptor.setOverflow(false); // 禁止溢出
interceptor.addInnerInterceptor(paginationInterceptor);
return interceptor;
}
/**
* 自定义方言(如果需要)
*/
@Bean
public IDialect customDialect() {
return new CustomMySqlDialect();
}
}
/**
* 自定义MySQL方言
*/
public class CustomMySqlDialect extends MySqlDialect {
@Override
public String buildPaginationSql(String originalSql, long offset, long limit) {
// 添加自定义逻辑,比如SQL优化提示
String optimizedSql = addSqlHints(originalSql);
return super.buildPaginationSql(optimizedSql, offset, limit);
}
private String addSqlHints(String sql) {
// 添加MySQL查询提示
if (sql.toUpperCase().startsWith("SELECT")) {
return sql.replaceFirst("(?i)SELECT", "SELECT /*+ USE_INDEX */");
}
return sql;
}
}
分页插件调试工具
java
/**
* 分页插件调试工具
*/
@Component
public class PaginationDebugger {
private static final Logger log = LoggerFactory.getLogger(PaginationDebugger.class);
/**
* 分析分页SQL性能
*/
public void analyzePaginationPerformance(String sql, IPage<?> page) {
log.info("=== 分页SQL性能分析 ===");
log.info("原始SQL: {}", sql);
log.info("分页参数: 当前页={}, 页大小={}, 偏移量={}",
page.getCurrent(), page.getSize(), page.offset());
// 分析SQL复杂度
analyzeSqlComplexity(sql);
// 检查索引使用
checkIndexUsage(sql);
// 估算查询成本
estimateQueryCost(sql, page);
}
/**
* 分析SQL复杂度
*/
private void analyzeSqlComplexity(String sql) {
int joinCount = StringUtils.countMatches(sql.toUpperCase(), "JOIN");
int subQueryCount = StringUtils.countMatches(sql, "(");
int functionCount = StringUtils.countMatches(sql.toUpperCase(), "COUNT(") +
StringUtils.countMatches(sql.toUpperCase(), "SUM(") +
StringUtils.countMatches(sql.toUpperCase(), "AVG(");
log.info("SQL复杂度分析: JOIN数量={}, 子查询数量={}, 函数数量={}",
joinCount, subQueryCount, functionCount);
if (joinCount > 3) {
log.warn("检测到多表关联({}个JOIN),建议优化查询", joinCount);
}
if (subQueryCount > 2) {
log.warn("检测到复杂子查询,建议考虑分步查询");
}
}
/**
* 检查索引使用
*/
private void checkIndexUsage(String sql) {
// 检查WHERE子句中的字段
if (sql.toUpperCase().contains("WHERE")) {
log.info("检测到WHERE条件,请确保相关字段有索引");
}
// 检查ORDER BY字段
if (sql.toUpperCase().contains("ORDER BY")) {
log.info("检测到ORDER BY子句,请确保排序字段有索引");
}
// 检查GROUP BY字段
if (sql.toUpperCase().contains("GROUP BY")) {
log.info("检测到GROUP BY子句,请确保分组字段有索引");
}
}
/**
* 估算查询成本
*/
private void estimateQueryCost(String sql, IPage<?> page) {
// 简单的成本估算
int baseCost = 1;
if (sql.toUpperCase().contains("JOIN")) {
baseCost *= 2;
}
if (sql.toUpperCase().contains("GROUP BY")) {
baseCost *= 3;
}
if (page.offset() > 10000) {
baseCost *= 5; // 深分页成本很高
}
log.info("估算查询成本: {} (1=低, 5=高)", Math.min(baseCost, 5));
if (baseCost > 3) {
log.warn("查询成本较高,建议优化");
}
}
/**
* 生成分页优化建议
*/
public List<String> generateOptimizationSuggestions(String sql, IPage<?> page) {
List<String> suggestions = new ArrayList<>();
// 深分页建议
if (page.offset() > 10000) {
suggestions.add("使用游标分页替代深分页");
suggestions.add("考虑使用子查询优化深分页");
}
// 大页面建议
if (page.getSize() > 100) {
suggestions.add("减少单页数据量,提升用户体验");
}
// SQL优化建议
if (sql.toUpperCase().contains("SELECT *")) {
suggestions.add("避免使用SELECT *,只查询需要的字段");
}
if (StringUtils.countMatches(sql.toUpperCase(), "JOIN") > 2) {
suggestions.add("考虑分步查询减少复杂关联");
}
return suggestions;
}
}
代码生成器
简要描述:MybatisPlus代码生成器是一个强大的工具,能够根据数据库表结构自动生成Entity、Mapper、Service、Controller等代码,大大提升开发效率。
核心概念:
- 模板引擎:支持Velocity、Freemarker、Beetl等模板引擎
- 策略配置:灵活的代码生成策略
- 自定义模板:支持自定义代码模板
- 包结构配置:自动生成规范的包结构
- 字段映射:数据库字段到Java属性的映射
代码生成器配置
简要描述:通过配置代码生成器的各种参数,可以生成符合项目规范的代码结构。
核心概念:
- 全局配置:输出目录、作者信息、文件覆盖等
- 数据源配置:数据库连接信息
- 包配置:各层代码的包路径
- 策略配置:命名策略、字段策略等
- 模板配置:自定义模板路径
基础配置示例
java
/**
* 代码生成器基础配置
*/
@Component
public class CodeGeneratorConfig {
/**
* 基础代码生成器配置
*/
public void generateCode() {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("mybatis-plus");
gc.setOpen(false); // 是否打开输出目录
gc.setFileOverride(true); // 是否覆盖已有文件
gc.setServiceName("%sService"); // service 命名方式
gc.setServiceImplName("%sServiceImpl"); // service impl 命名方式
gc.setMapperName("%sMapper"); // mapper 命名方式
gc.setXmlName("%sMapper"); // mapper xml 命名方式
gc.setControllerName("%sController"); // controller 命名方式
gc.setIdType(IdType.ASSIGN_ID); // 主键策略
gc.setDateType(DateType.ONLY_DATE); // 日期类型
gc.setSwagger2(true); // 实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("password");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("system"); // 模块名
pc.setParent("com.example"); // 父包名
pc.setEntity("entity"); // Entity包名
pc.setMapper("mapper"); // Mapper包名
pc.setService("service"); // Service包名
pc.setServiceImpl("service.impl"); // ServiceImpl包名
pc.setController("controller"); // Controller包名
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// 自定义属性注入
Map<String, Object> map = new HashMap<>();
map.put("date", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
this.setMap(map);
}
};
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null); // 配置自定义输出模板,指定自定义模板路径
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel); // 数据库表映射到实体的命名策略
strategy.setColumnNaming(NamingStrategy.underline_to_camel); // 数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // 是否为lombok模型
strategy.setRestControllerStyle(true); // 生成 @RestController 控制器
strategy.setInclude("user", "role", "permission"); // 需要包含的表名
strategy.setControllerMappingHyphenStyle(true); // 驼峰转连字符
strategy.setTablePrefix(pc.getModuleName() + "_"); // 表前缀
mpg.setStrategy(strategy);
// 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
高级配置示例
java
/**
* 高级代码生成器配置
*/
@Component
public class AdvancedCodeGenerator {
/**
* 高级配置代码生成
*/
public void generateAdvancedCode() {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = buildGlobalConfig();
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = buildDataSourceConfig();
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = buildPackageConfig();
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = buildInjectionConfig(pc);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = buildTemplateConfig();
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = buildStrategyConfig();
mpg.setStrategy(strategy);
// 选择模板引擎
mpg.setTemplateEngine(new VelocityTemplateEngine());
mpg.execute();
}
/**
* 构建全局配置
*/
private GlobalConfig buildGlobalConfig() {
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java")
.setAuthor("Code Generator")
.setOpen(false)
.setFileOverride(true)
.setActiveRecord(false) // 不需要ActiveRecord特性的请改为false
.setEnableCache(false) // XML 二级缓存
.setBaseResultMap(true) // XML ResultMap
.setBaseColumnList(true) // XML columList
.setDateType(DateType.ONLY_DATE)
.setSwagger2(true); // 实体属性 Swagger2 注解
// 自定义文件命名,注意 %s 会自动填充表实体属性!
gc.setEntityName("%sEntity")
.setMapperName("%sMapper")
.setXmlName("%sMapper")
.setServiceName("%sService")
.setServiceImplName("%sServiceImpl")
.setControllerName("%sController");
return gc;
}
/**
* 构建数据源配置
*/
private DataSourceConfig buildDataSourceConfig() {
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8")
.setDriverName("com.mysql.cj.jdbc.Driver")
.setUsername("root")
.setPassword("password")
.setDbType(DbType.MYSQL);
return dsc;
}
/**
* 构建包配置
*/
private PackageConfig buildPackageConfig() {
PackageConfig pc = new PackageConfig();
pc.setModuleName("system")
.setParent("com.example.mybatisplus")
.setEntity("entity")
.setMapper("mapper")
.setService("service")
.setServiceImpl("service.impl")
.setController("controller")
.setXml("mapper.xml");
return pc;
}
/**
* 构建自定义配置
*/
private InjectionConfig buildInjectionConfig(PackageConfig pc) {
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
Map<String, Object> map = new HashMap<>();
map.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
map.put("author", "Code Generator");
map.put("version", "1.0.0");
map.put("projectName", "MyBatis Plus Demo");
this.setMap(map);
}
};
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
String projectPath = System.getProperty("user.dir");
// 自定义Mapper XML生成
focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
// 自定义DTO生成
focList.add(new FileOutConfig("/templates/dto.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/java/com/example/mybatisplus/dto/"
+ tableInfo.getEntityName() + "DTO.java";
}
});
// 自定义VO生成
focList.add(new FileOutConfig("/templates/vo.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/java/com/example/mybatisplus/vo/"
+ tableInfo.getEntityName() + "VO.java";
}
});
cfg.setFileOutConfigList(focList);
return cfg;
}
/**
* 构建模板配置
*/
private TemplateConfig buildTemplateConfig() {
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
templateConfig.setEntity("/templates/entity.java")
.setMapper("/templates/mapper.java")
.setService("/templates/service.java")
.setServiceImpl("/templates/serviceImpl.java")
.setController("/templates/controller.java")
.setXml(null); // 指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
return templateConfig;
}
/**
* 构建策略配置
*/
private StrategyConfig buildStrategyConfig() {
StrategyConfig strategy = new StrategyConfig();
// 数据库表配置
strategy.setNaming(NamingStrategy.underline_to_camel) // 数据库表映射到实体的命名策略
.setColumnNaming(NamingStrategy.underline_to_camel) // 数据库表字段映射到实体的命名策略
.setInclude("sys_user", "sys_role", "sys_permission") // 需要包含的表名,允许正则表达式
.setExclude("test_table") // 需要排除的表名,允许正则表达式
.setTablePrefix("sys_") // 表前缀
.setFieldPrefix("is_", "has_"); // 字段前缀
// 实体类配置
strategy.setEntityLombokModel(true) // 是否为lombok模型
.setEntityTableFieldAnnotationEnable(true) // 是否生成实体时,生成字段注解
.setEntityColumnConstant(true) // 是否生成字段常量
.setEntityBuilderModel(false) // 是否为构建者模型
.setEntitySerialVersionUID(true) // 是否生成serialVersionUID
.setChainModel(false) // 是否为链式模型
.setEntityBooleanColumnRemoveIsPrefix(true); // Boolean类型字段是否移除is前缀
// Controller配置
strategy.setRestControllerStyle(true) // 生成 @RestController 控制器
.setControllerMappingHyphenStyle(true); // 驼峰转连字符
// 填充字段配置
List<TableFill> tableFillList = new ArrayList<>();
tableFillList.add(new TableFill("create_time", FieldFill.INSERT));
tableFillList.add(new TableFill("update_time", FieldFill.INSERT_UPDATE));
tableFillList.add(new TableFill("create_by", FieldFill.INSERT));
tableFillList.add(new TableFill("update_by", FieldFill.INSERT_UPDATE));
strategy.setTableFillList(tableFillList);
// 乐观锁字段配置
strategy.setVersionFieldName("version");
// 逻辑删除字段配置
strategy.setLogicDeleteFieldName("deleted");
return strategy;
}
}
自定义模板配置
java
/**
* 自定义模板配置
*/
@Component
public class CustomTemplateGenerator {
/**
* 使用自定义模板生成代码
*/
public void generateWithCustomTemplate() {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java")
.setAuthor("Custom Generator")
.setOpen(false)
.setFileOverride(true)
.setSwagger2(true);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8")
.setDriverName("com.mysql.cj.jdbc.Driver")
.setUsername("root")
.setPassword("password");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.example.custom")
.setEntity("domain")
.setMapper("repository")
.setService("application")
.setServiceImpl("application.impl")
.setController("interfaces.web");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
Map<String, Object> map = new HashMap<>();
map.put("basePackage", "com.example.custom");
map.put("apiVersion", "v1");
map.put("moduleName", "user");
this.setMap(map);
}
};
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 生成Repository接口
focList.add(new FileOutConfig("/templates/repository.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/java/com/example/custom/repository/"
+ tableInfo.getEntityName() + "Repository.java";
}
});
// 生成Repository实现类
focList.add(new FileOutConfig("/templates/repositoryImpl.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/java/com/example/custom/repository/impl/"
+ tableInfo.getEntityName() + "RepositoryImpl.java";
}
});
// 生成Application Service
focList.add(new FileOutConfig("/templates/applicationService.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/java/com/example/custom/application/"
+ tableInfo.getEntityName() + "ApplicationService.java";
}
});
// 生成Facade
focList.add(new FileOutConfig("/templates/facade.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/java/com/example/custom/interfaces/facade/"
+ tableInfo.getEntityName() + "Facade.java";
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setEntity("/templates/domain.java")
.setMapper("/templates/mapper.java")
.setService(null) // 不生成默认Service
.setServiceImpl(null) // 不生成默认ServiceImpl
.setController(null) // 不生成默认Controller
.setXml("/templates/mapper.xml");
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel)
.setColumnNaming(NamingStrategy.underline_to_camel)
.setEntityLombokModel(true)
.setEntityTableFieldAnnotationEnable(true)
.setInclude("user", "role")
.setTablePrefix("t_");
mpg.setStrategy(strategy);
// 选择模板引擎
mpg.setTemplateEngine(new VelocityTemplateEngine());
mpg.execute();
}
}
批量生成配置
java
/**
* 批量代码生成器
*/
@Component
public class BatchCodeGenerator {
/**
* 批量生成多个模块的代码
*/
public void batchGenerate() {
// 定义模块配置
List<ModuleConfig> modules = Arrays.asList(
new ModuleConfig("user", "用户模块", Arrays.asList("sys_user", "sys_user_role")),
new ModuleConfig("role", "角色模块", Arrays.asList("sys_role", "sys_role_permission")),
new ModuleConfig("permission", "权限模块", Arrays.asList("sys_permission", "sys_menu"))
);
// 为每个模块生成代码
for (ModuleConfig module : modules) {
generateModuleCode(module);
}
}
/**
* 生成单个模块代码
*/
private void generateModuleCode(ModuleConfig moduleConfig) {
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java")
.setAuthor("Batch Generator")
.setOpen(false)
.setFileOverride(true)
.setSwagger2(true)
.setServiceName("%sService")
.setServiceImplName("%sServiceImpl")
.setMapperName("%sMapper")
.setControllerName("%sController");
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8")
.setDriverName("com.mysql.cj.jdbc.Driver")
.setUsername("root")
.setPassword("password");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(moduleConfig.getModuleName())
.setParent("com.example.modules")
.setEntity("entity")
.setMapper("mapper")
.setService("service")
.setServiceImpl("service.impl")
.setController("controller");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
Map<String, Object> map = new HashMap<>();
map.put("moduleName", moduleConfig.getModuleName());
map.put("moduleComment", moduleConfig.getComment());
map.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
this.setMap(map);
}
};
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 生成模块配置文件
focList.add(new FileOutConfig("/templates/moduleConfig.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/java/com/example/modules/" + moduleConfig.getModuleName()
+ "/config/" + StringUtils.capitalize(moduleConfig.getModuleName()) + "Config.java";
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel)
.setColumnNaming(NamingStrategy.underline_to_camel)
.setEntityLombokModel(true)
.setRestControllerStyle(true)
.setInclude(moduleConfig.getTables().toArray(new String[0]))
.setTablePrefix("sys_")
.setControllerMappingHyphenStyle(true);
mpg.setStrategy(strategy);
// 模板引擎
mpg.setTemplateEngine(new VelocityTemplateEngine());
mpg.execute();
System.out.println("模块 [" + moduleConfig.getModuleName() + "] 代码生成完成");
}
/**
* 模块配置
*/
@Data
@AllArgsConstructor
public static class ModuleConfig {
private String moduleName;
private String comment;
private List<String> tables;
}
}
模板定制
简要描述:MybatisPlus代码生成器支持自定义模板,可以根据项目需求定制生成的代码格式和内容。
核心概念:
- 模板引擎:支持Velocity、Freemarker、Beetl等模板引擎
- 模板变量:内置丰富的模板变量
- 自定义模板:可以完全自定义代码模板
- 模板继承:支持模板继承和复用
- 条件渲染:支持条件判断和循环渲染
Velocity模板示例
Entity模板定制
velocity
##(set $tableName = $table.name)
##(set $entityName = $table.entityName)
##(set $entityPath = $table.entityPath)
package ${package.Entity};
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${swagger2})
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
#end
#if(${entityLombokModel})
import lombok.Data;
import lombok.EqualsAndHashCode;
#if(${chainModel})
import lombok.experimental.Accessors;
#end
#end
/**
* <p>
* $!{table.comment}
* </p>
*
* @author ${author}
* @since ${date}
*/
#if(${entityLombokModel})
@Data
#if(${superEntityClass})
@EqualsAndHashCode(callSuper = true)
#else
@EqualsAndHashCode(callSuper = false)
#end
#if(${chainModel})
@Accessors(chain = true)
#end
#end
#if(${table.convert})
@TableName("${table.name}")
#end
#if(${swagger2})
@ApiModel(value="${entity}对象", description="$!{table.comment}")
#end
#if(${superEntityClass})
public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {
#elseif(${activeRecord})
public class ${entity} extends Model<${entity}> {
#else
public class ${entity} implements Serializable {
#end
#if(${entitySerialVersionUID})
private static final long serialVersionUID = 1L;
#end
## ---------- BEGIN 字段循环遍历 ----------
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
#if("$!field.comment" != "")
#if(${swagger2})
@ApiModelProperty(value = "${field.comment}")
#else
/**
* ${field.comment}
*/
#end
#end
#if(${field.keyFlag})
## 主键
#if(${field.keyIdentityFlag})
@TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
#elseif(!$null.isNull(${idType}) && "$!idType" != "")
@TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
#elseif(${field.convert})
@TableId("${field.annotationColumnName}")
#end
## 普通字段
#elseif(${field.fill})
## ----- 存在字段填充设置 -----
#if(${field.convert})
@TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
#else
@TableField(fill = FieldFill.${field.fill})
#end
#elseif(${field.convert})
@TableField("${field.annotationColumnName}")
#end
## 乐观锁注解
#if(${versionFieldName}==${field.name})
@Version
#end
## 逻辑删除注解
#if(${logicDeleteFieldName}==${field.name})
@TableLogic
#end
private ${field.propertyType} ${field.propertyName};
#end
## ---------- END 字段循环遍历 ----------
#if(!${entityLombokModel})
#foreach($field in ${table.fields})
#if(${field.propertyType.equals("boolean")})
#set($getprefix="is")
#else
#set($getprefix="get")
#end
public ${field.propertyType} ${getprefix}${field.capitalName}() {
return ${field.propertyName};
}
#if(${chainModel})
public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
#else
public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
#end
this.${field.propertyName} = ${field.propertyName};
#if(${chainModel})
return this;
#end
}
#end
#end
#if(${entityColumnConstant})
#foreach($field in ${table.fields})
public static final String ${field.name.toUpperCase()} = "${field.name}";
#end
#end
#if(${activeRecord})
@Override
protected Serializable pkVal() {
#if(${keyPropertyName})
return this.${keyPropertyName};
#else
return null;
#end
}
#end
#if(!${entityLombokModel})
@Override
public String toString() {
return "${entity}{" +
#foreach($field in ${table.fields})
#if($!{foreach.index}==0)
"${field.propertyName}=" + ${field.propertyName} +
#else
", ${field.propertyName}=" + ${field.propertyName} +
#end
#end
"}";
}
#end
}
Mapper模板定制
velocity
package ${package.Mapper};
import ${package.Entity}.${entity};
import ${superMapperClassPackage};
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* <p>
* $!{table.comment} Mapper 接口
* </p>
*
* @author ${author}
* @since ${date}
*/
#if(${kotlin})
interface ${table.mapperName} : ${superMapperClass}<${entity}>
#else
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {
/**
* 自定义分页查询
* @param page 分页参数
* @param queryParam 查询参数
* @return 分页结果
*/
IPage<${entity}> selectCustomPage(IPage<${entity}> page, @Param("query") ${entity}QueryDTO queryParam);
/**
* 根据条件统计数量
* @param queryParam 查询参数
* @return 统计结果
*/
Long countByCondition(@Param("query") ${entity}QueryDTO queryParam);
/**
* 批量插入
* @param entityList 实体列表
* @return 影响行数
*/
int insertBatch(@Param("list") List<${entity}> entityList);
/**
* 根据条件查询列表
* @param queryParam 查询参数
* @return 结果列表
*/
List<${entity}> selectByCondition(@Param("query") ${entity}QueryDTO queryParam);
/**
* 根据ID列表查询
* @param ids ID列表
* @return 结果列表
*/
List<${entity}> selectByIds(@Param("ids") List<Long> ids);
/**
* 软删除
* @param id 主键ID
* @param updateBy 更新人
* @return 影响行数
*/
int softDeleteById(@Param("id") Long id, @Param("updateBy") String updateBy);
/**
* 批量软删除
* @param ids ID列表
* @param updateBy 更新人
* @return 影响行数
*/
int softDeleteByIds(@Param("ids") List<Long> ids, @Param("updateBy") String updateBy);
}
#end
Service模板定制
velocity
package ${package.Service};
import ${package.Entity}.${entity};
import ${superServiceClassPackage};
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
/**
* <p>
* $!{table.comment} 服务类
* </p>
*
* @author ${author}
* @since ${date}
*/
#if(${kotlin})
interface ${table.serviceName} : ${superServiceClass}<${entity}>
#else
public interface ${table.serviceName} extends ${superServiceClass}<${entity}> {
/**
* 分页查询
* @param page 分页参数
* @param queryDTO 查询条件
* @return 分页结果
*/
IPage<${entity}VO> selectPage(Page<${entity}> page, ${entity}QueryDTO queryDTO);
/**
* 根据条件查询列表
* @param queryDTO 查询条件
* @return 结果列表
*/
List<${entity}VO> selectList(${entity}QueryDTO queryDTO);
/**
* 根据ID查询详情
* @param id 主键ID
* @return 详情信息
*/
${entity}VO selectById(Long id);
/**
* 新增
* @param createDTO 创建DTO
* @return 是否成功
*/
boolean create(${entity}CreateDTO createDTO);
/**
* 修改
* @param updateDTO 更新DTO
* @return 是否成功
*/
boolean update(${entity}UpdateDTO updateDTO);
/**
* 删除
* @param id 主键ID
* @return 是否成功
*/
boolean deleteById(Long id);
/**
* 批量删除
* @param ids ID列表
* @return 是否成功
*/
boolean deleteByIds(List<Long> ids);
/**
* 导出数据
* @param queryDTO 查询条件
* @return 导出数据
*/
List<${entity}ExportVO> exportData(${entity}QueryDTO queryDTO);
/**
* 导入数据
* @param importList 导入数据列表
* @return 导入结果
*/
ImportResult<${entity}ImportDTO> importData(List<${entity}ImportDTO> importList);
}
#end
Controller模板定制
velocity
package ${package.Controller};
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.*;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* <p>
* $!{table.comment} 前端控制器
* </p>
*
* @author ${author}
* @since ${date}
*/
#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
@Api(tags = "$!{table.comment}管理")
@Validated
#if(${kotlin})
class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end
#else
#if(${superControllerClass})
public class ${table.controllerName} extends ${superControllerClass} {
#else
public class ${table.controllerName} {
#end
@Autowired
private ${table.serviceName} ${table.entityPath}Service;
/**
* 分页查询
*/
@GetMapping("/page")
@ApiOperation(value = "分页查询$!{table.comment}", notes = "分页查询$!{table.comment}列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "current", value = "当前页", defaultValue = "1", dataType = "int", paramType = "query"),
@ApiImplicitParam(name = "size", value = "每页显示条数", defaultValue = "10", dataType = "int", paramType = "query")
})
public Result<IPage<${entity}VO>> page(
@RequestParam(value = "current", defaultValue = "1") Integer current,
@RequestParam(value = "size", defaultValue = "10") Integer size,
${entity}QueryDTO queryDTO) {
Page<${entity}> page = new Page<>(current, size);
IPage<${entity}VO> result = ${table.entityPath}Service.selectPage(page, queryDTO);
return Result.success(result);
}
/**
* 查询列表
*/
@GetMapping("/list")
@ApiOperation(value = "查询$!{table.comment}列表", notes = "根据条件查询$!{table.comment}列表")
public Result<List<${entity}VO>> list(${entity}QueryDTO queryDTO) {
List<${entity}VO> result = ${table.entityPath}Service.selectList(queryDTO);
return Result.success(result);
}
/**
* 根据ID查询详情
*/
@GetMapping("/{id}")
@ApiOperation(value = "查询$!{table.comment}详情", notes = "根据ID查询$!{table.comment}详情")
@ApiImplicitParam(name = "id", value = "主键ID", required = true, dataType = "long", paramType = "path")
public Result<${entity}VO> getById(@PathVariable @NotNull(message = "ID不能为空") Long id) {
${entity}VO result = ${table.entityPath}Service.selectById(id);
return Result.success(result);
}
/**
* 新增
*/
@PostMapping
@ApiOperation(value = "新增$!{table.comment}", notes = "新增$!{table.comment}")
public Result<Boolean> create(@RequestBody @Valid ${entity}CreateDTO createDTO) {
boolean result = ${table.entityPath}Service.create(createDTO);
return Result.success(result);
}
/**
* 修改
*/
@PutMapping
@ApiOperation(value = "修改$!{table.comment}", notes = "修改$!{table.comment}")
public Result<Boolean> update(@RequestBody @Valid ${entity}UpdateDTO updateDTO) {
boolean result = ${table.entityPath}Service.update(updateDTO);
return Result.success(result);
}
/**
* 删除
*/
@DeleteMapping("/{id}")
@ApiOperation(value = "删除$!{table.comment}", notes = "根据ID删除$!{table.comment}")
@ApiImplicitParam(name = "id", value = "主键ID", required = true, dataType = "long", paramType = "path")
public Result<Boolean> deleteById(@PathVariable @NotNull(message = "ID不能为空") Long id) {
boolean result = ${table.entityPath}Service.deleteById(id);
return Result.success(result);
}
/**
* 批量删除
*/
@DeleteMapping("/batch")
@ApiOperation(value = "批量删除$!{table.comment}", notes = "根据ID列表批量删除$!{table.comment}")
public Result<Boolean> deleteByIds(@RequestBody @Valid List<@NotNull(message = "ID不能为空") Long> ids) {
boolean result = ${table.entityPath}Service.deleteByIds(ids);
return Result.success(result);
}
/**
* 导出
*/
@GetMapping("/export")
@ApiOperation(value = "导出$!{table.comment}", notes = "导出$!{table.comment}数据")
public void export(HttpServletResponse response, ${entity}QueryDTO queryDTO) {
List<${entity}ExportVO> data = ${table.entityPath}Service.exportData(queryDTO);
ExcelUtils.export(response, "$!{table.comment}数据", ${entity}ExportVO.class, data);
}
/**
* 导入
*/
@PostMapping("/import")
@ApiOperation(value = "导入$!{table.comment}", notes = "导入$!{table.comment}数据")
public Result<ImportResult<${entity}ImportDTO>> importData(@RequestParam("file") MultipartFile file) {
List<${entity}ImportDTO> importList = ExcelUtils.read(file, ${entity}ImportDTO.class);
ImportResult<${entity}ImportDTO> result = ${table.entityPath}Service.importData(importList);
return Result.success(result);
}
}
#end
生成策略配置
简要描述:生成策略配置是代码生成器的核心配置,控制着代码生成的各种策略和规则。
核心概念:
- 命名策略:数据库表名到实体类名的转换规则
- 字段策略:数据库字段到实体属性的转换规则
- 包含排除策略:控制哪些表参与代码生成
- 填充策略:自动填充字段的配置
- 继承策略:实体类继承关系的配置
基础策略配置
java
/**
* 基础策略配置
*/
public class BasicStrategyConfig {
/**
* 构建基础策略配置
*/
public StrategyConfig buildBasicStrategy() {
StrategyConfig strategy = new StrategyConfig();
// 数据库表配置
strategy.setNaming(NamingStrategy.underline_to_camel) // 数据库表映射到实体的命名策略
.setColumnNaming(NamingStrategy.underline_to_camel) // 数据库表字段映射到实体的命名策略
.setInclude("sys_user", "sys_role", "sys_permission") // 需要包含的表名
.setExclude("temp_table", "log_table") // 需要排除的表名
.setTablePrefix("sys_", "t_") // 表前缀
.setFieldPrefix("is_", "has_"); // 字段前缀
// 实体类配置
strategy.setEntityLombokModel(true) // 是否为lombok模型
.setEntityTableFieldAnnotationEnable(true) // 是否生成实体时,生成字段注解
.setEntityColumnConstant(true) // 是否生成字段常量
.setEntityBuilderModel(false) // 是否为构建者模型
.setEntitySerialVersionUID(true) // 是否生成serialVersionUID
.setChainModel(false) // 是否为链式模型
.setEntityBooleanColumnRemoveIsPrefix(true); // Boolean类型字段是否移除is前缀
// Controller配置
strategy.setRestControllerStyle(true) // 生成 @RestController 控制器
.setControllerMappingHyphenStyle(true); // 驼峰转连字符
return strategy;
}
}
高级策略配置
java
/**
* 高级策略配置
*/
public class AdvancedStrategyConfig {
/**
* 构建高级策略配置
*/
public StrategyConfig buildAdvancedStrategy() {
StrategyConfig strategy = new StrategyConfig();
// 数据库表配置
strategy.setNaming(NamingStrategy.underline_to_camel)
.setColumnNaming(NamingStrategy.underline_to_camel)
.setInclude(getIncludeTables()) // 动态获取表名
.setTablePrefix("sys_", "t_", "tb_")
.setFieldPrefix("is_", "has_", "can_");
// 实体类配置
strategy.setEntityLombokModel(true)
.setEntityTableFieldAnnotationEnable(true)
.setEntityColumnConstant(true)
.setEntityBuilderModel(false)
.setEntitySerialVersionUID(true)
.setChainModel(false)
.setEntityBooleanColumnRemoveIsPrefix(true);
// 填充字段配置
List<TableFill> tableFillList = buildTableFillList();
strategy.setTableFillList(tableFillList);
// 乐观锁字段配置
strategy.setVersionFieldName("version");
// 逻辑删除字段配置
strategy.setLogicDeleteFieldName("deleted");
// 父类实体配置
strategy.setSuperEntityClass("com.example.common.entity.BaseEntity")
.setSuperEntityColumns("id", "create_time", "update_time", "create_by", "update_by", "deleted");
// 父类Mapper配置
strategy.setSuperMapperClass("com.example.common.mapper.BaseMapper");
// 父类Service配置
strategy.setSuperServiceClass("com.example.common.service.IBaseService")
.setSuperServiceImplClass("com.example.common.service.impl.BaseServiceImpl");
// 父类Controller配置
strategy.setSuperControllerClass("com.example.common.controller.BaseController");
// Controller配置
strategy.setRestControllerStyle(true)
.setControllerMappingHyphenStyle(true);
return strategy;
}
/**
* 动态获取需要生成的表名
*/
private String[] getIncludeTables() {
// 可以从配置文件、数据库或其他地方动态获取
List<String> tables = new ArrayList<>();
tables.add("sys_user");
tables.add("sys_role");
tables.add("sys_permission");
tables.add("sys_menu");
tables.add("sys_dept");
return tables.toArray(new String[0]);
}
/**
* 构建填充字段列表
*/
private List<TableFill> buildTableFillList() {
List<TableFill> tableFillList = new ArrayList<>();
// 创建时间字段自动填充
tableFillList.add(new TableFill("create_time", FieldFill.INSERT));
tableFillList.add(new TableFill("created_time", FieldFill.INSERT));
tableFillList.add(new TableFill("gmt_create", FieldFill.INSERT));
// 更新时间字段自动填充
tableFillList.add(new TableFill("update_time", FieldFill.INSERT_UPDATE));
tableFillList.add(new TableFill("updated_time", FieldFill.INSERT_UPDATE));
tableFillList.add(new TableFill("gmt_modified", FieldFill.INSERT_UPDATE));
// 创建人字段自动填充
tableFillList.add(new TableFill("create_by", FieldFill.INSERT));
tableFillList.add(new TableFill("created_by", FieldFill.INSERT));
tableFillList.add(new TableFill("creator", FieldFill.INSERT));
// 更新人字段自动填充
tableFillList.add(new TableFill("update_by", FieldFill.INSERT_UPDATE));
tableFillList.add(new TableFill("updated_by", FieldFill.INSERT_UPDATE));
tableFillList.add(new TableFill("modifier", FieldFill.INSERT_UPDATE));
return tableFillList;
}
}
条件策略配置
java
/**
* 条件策略配置
*/
public class ConditionalStrategyConfig {
/**
* 根据条件构建策略配置
*/
public StrategyConfig buildConditionalStrategy(GeneratorConfig config) {
StrategyConfig strategy = new StrategyConfig();
// 基础配置
strategy.setNaming(config.getNamingStrategy())
.setColumnNaming(config.getColumnNamingStrategy());
// 根据环境配置表前缀
if (config.getEnvironment().equals("dev")) {
strategy.setTablePrefix("dev_", "test_");
} else if (config.getEnvironment().equals("prod")) {
strategy.setTablePrefix("prod_");
}
// 根据项目类型配置实体
if (config.getProjectType().equals("microservice")) {
// 微服务项目配置
strategy.setEntityLombokModel(true)
.setEntityTableFieldAnnotationEnable(true)
.setEntityColumnConstant(false)
.setChainModel(true);
} else {
// 单体项目配置
strategy.setEntityLombokModel(true)
.setEntityTableFieldAnnotationEnable(false)
.setEntityColumnConstant(true)
.setChainModel(false);
}
// 根据是否使用Swagger配置注解
if (config.isUseSwagger()) {
// 在GlobalConfig中设置swagger2为true
}
// 根据是否使用父类配置继承
if (config.isUseSuperClass()) {
strategy.setSuperEntityClass(config.getSuperEntityClass())
.setSuperEntityColumns(config.getSuperEntityColumns())
.setSuperMapperClass(config.getSuperMapperClass())
.setSuperServiceClass(config.getSuperServiceClass())
.setSuperServiceImplClass(config.getSuperServiceImplClass())
.setSuperControllerClass(config.getSuperControllerClass());
}
// 根据表名模式配置包含表
if (config.getTablePattern() != null) {
strategy.setInclude(getTablesByPattern(config.getTablePattern()));
} else {
strategy.setInclude(config.getIncludeTables());
}
// 配置填充字段
if (config.isUseAutoFill()) {
strategy.setTableFillList(buildAutoFillList(config));
}
// 配置乐观锁
if (config.isUseOptimisticLock()) {
strategy.setVersionFieldName(config.getVersionFieldName());
}
// 配置逻辑删除
if (config.isUseLogicDelete()) {
strategy.setLogicDeleteFieldName(config.getLogicDeleteFieldName());
}
// Controller配置
strategy.setRestControllerStyle(config.isRestController())
.setControllerMappingHyphenStyle(config.isHyphenStyle());
return strategy;
}
/**
* 根据模式获取表名
*/
private String[] getTablesByPattern(String pattern) {
// 这里可以实现根据正则表达式或其他模式匹配表名的逻辑
// 例如:从数据库查询符合模式的表名
List<String> tables = new ArrayList<>();
if (pattern.equals("sys_*")) {
tables.add("sys_user");
tables.add("sys_role");
tables.add("sys_permission");
} else if (pattern.equals("biz_*")) {
tables.add("biz_order");
tables.add("biz_product");
tables.add("biz_customer");
}
return tables.toArray(new String[0]);
}
/**
* 构建自动填充列表
*/
private List<TableFill> buildAutoFillList(GeneratorConfig config) {
List<TableFill> tableFillList = new ArrayList<>();
// 根据配置添加自动填充字段
if (config.getAutoFillFields() != null) {
for (AutoFillField field : config.getAutoFillFields()) {
tableFillList.add(new TableFill(field.getFieldName(), field.getFieldFill()));
}
}
return tableFillList;
}
/**
* 生成器配置类
*/
@Data
public static class GeneratorConfig {
private String environment; // 环境:dev, test, prod
private String projectType; // 项目类型:microservice, monolith
private NamingStrategy namingStrategy = NamingStrategy.underline_to_camel;
private NamingStrategy columnNamingStrategy = NamingStrategy.underline_to_camel;
private boolean useSwagger = true;
private boolean useSuperClass = true;
private boolean useAutoFill = true;
private boolean useOptimisticLock = false;
private boolean useLogicDelete = true;
private boolean restController = true;
private boolean hyphenStyle = true;
// 父类配置
private String superEntityClass;
private String[] superEntityColumns;
private String superMapperClass;
private String superServiceClass;
private String superServiceImplClass;
private String superControllerClass;
// 表配置
private String tablePattern;
private String[] includeTables;
// 字段配置
private String versionFieldName = "version";
private String logicDeleteFieldName = "deleted";
private List<AutoFillField> autoFillFields;
}
/**
* 自动填充字段配置
*/
@Data
@AllArgsConstructor
public static class AutoFillField {
private String fieldName;
private FieldFill fieldFill;
}
}
策略配置工厂
java
/**
* 策略配置工厂
*/
@Component
public class StrategyConfigFactory {
/**
* 创建默认策略配置
*/
public StrategyConfig createDefaultStrategy() {
return new BasicStrategyConfig().buildBasicStrategy();
}
/**
* 创建微服务策略配置
*/
public StrategyConfig createMicroserviceStrategy() {
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel)
.setColumnNaming(NamingStrategy.underline_to_camel)
.setEntityLombokModel(true)
.setEntityTableFieldAnnotationEnable(true)
.setChainModel(true)
.setRestControllerStyle(true)
.setControllerMappingHyphenStyle(true);
// 微服务通用父类
strategy.setSuperEntityClass("com.example.common.entity.BaseEntity")
.setSuperEntityColumns("id", "create_time", "update_time", "deleted")
.setSuperMapperClass("com.example.common.mapper.BaseMapper")
.setSuperServiceClass("com.example.common.service.IBaseService")
.setSuperServiceImplClass("com.example.common.service.impl.BaseServiceImpl")
.setSuperControllerClass("com.example.common.controller.BaseController");
// 微服务通用填充字段
List<TableFill> tableFillList = Arrays.asList(
new TableFill("create_time", FieldFill.INSERT),
new TableFill("update_time", FieldFill.INSERT_UPDATE),
new TableFill("create_by", FieldFill.INSERT),
new TableFill("update_by", FieldFill.INSERT_UPDATE)
);
strategy.setTableFillList(tableFillList);
strategy.setLogicDeleteFieldName("deleted");
return strategy;
}
/**
* 创建DDD策略配置
*/
public StrategyConfig createDDDStrategy() {
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel)
.setColumnNaming(NamingStrategy.underline_to_camel)
.setEntityLombokModel(true)
.setEntityTableFieldAnnotationEnable(true)
.setEntityBuilderModel(true) // DDD中使用Builder模式
.setChainModel(false)
.setRestControllerStyle(true);
// DDD分层架构父类
strategy.setSuperEntityClass("com.example.domain.entity.AggregateRoot")
.setSuperEntityColumns("id", "version", "create_time", "update_time")
.setSuperMapperClass("com.example.infrastructure.mapper.BaseMapper")
.setSuperServiceClass("com.example.domain.service.DomainService")
.setSuperControllerClass("com.example.interfaces.controller.BaseController");
// DDD通用字段
List<TableFill> tableFillList = Arrays.asList(
new TableFill("create_time", FieldFill.INSERT),
new TableFill("update_time", FieldFill.INSERT_UPDATE)
);
strategy.setTableFillList(tableFillList);
strategy.setVersionFieldName("version"); // DDD中使用乐观锁
return strategy;
}
/**
* 创建CQRS策略配置
*/
public StrategyConfig createCQRSStrategy() {
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel)
.setColumnNaming(NamingStrategy.underline_to_camel)
.setEntityLombokModel(true)
.setEntityTableFieldAnnotationEnable(true)
.setEntityColumnConstant(true)
.setChainModel(false)
.setRestControllerStyle(true);
// CQRS读写分离父类
strategy.setSuperEntityClass("com.example.common.entity.BaseEntity")
.setSuperEntityColumns("id", "version", "create_time", "update_time")
.setSuperMapperClass("com.example.common.mapper.BaseMapper");
// 不生成默认Service,CQRS有专门的Command和Query处理器
strategy.setSuperServiceClass(null)
.setSuperServiceImplClass(null);
strategy.setSuperControllerClass("com.example.common.controller.BaseController");
List<TableFill> tableFillList = Arrays.asList(
new TableFill("create_time", FieldFill.INSERT),
new TableFill("update_time", FieldFill.INSERT_UPDATE)
);
strategy.setTableFillList(tableFillList);
strategy.setVersionFieldName("version");
return strategy;
}
/**
* 根据架构类型创建策略配置
*/
public StrategyConfig createByArchitecture(String architecture) {
switch (architecture.toLowerCase()) {
case "microservice":
return createMicroserviceStrategy();
case "ddd":
return createDDDStrategy();
case "cqrs":
return createCQRSStrategy();
default:
return createDefaultStrategy();
}
}
}
自定义生成模板
简要描述:自定义生成模板允许开发者根据项目需求定制代码生成的输出格式和内容,支持多种模板引擎。
核心概念:
- 模板引擎:支持Velocity、Freemarker、Beetl等模板引擎
- 模板变量:代码生成过程中可用的变量和对象
- 模板路径:自定义模板文件的存放位置
- 模板覆盖:用自定义模板覆盖默认模板
- 条件渲染:根据配置条件渲染不同的代码内容
Velocity模板引擎
java
/**
* Velocity模板配置
*/
public class VelocityTemplateConfig {
/**
* 配置Velocity模板引擎
*/
public TemplateConfig buildVelocityTemplate() {
TemplateConfig templateConfig = new TemplateConfig();
// 设置模板引擎为Velocity
templateConfig.setEntity("/templates/entity.java.vm")
.setMapper("/templates/mapper.java.vm")
.setMapperXml("/templates/mapper.xml.vm")
.setService("/templates/service.java.vm")
.setServiceImpl("/templates/serviceImpl.java.vm")
.setController("/templates/controller.java.vm");
return templateConfig;
}
}
Entity模板示例 (entity.java.vm)
velocity
package ${package.Entity};
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${swagger2})
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
#end
#if(${entityLombokModel})
import lombok.Data;
import lombok.EqualsAndHashCode;
#if(${chainModel})
import lombok.experimental.Accessors;
#end
#end
/**
* <p>
* $!{table.comment}
* </p>
*
* @author ${author}
* @since ${date}
*/
#if(${entityLombokModel})
@Data
#if(${superEntityClass})
@EqualsAndHashCode(callSuper = true)
#else
@EqualsAndHashCode(callSuper = false)
#end
#if(${chainModel})
@Accessors(chain = true)
#end
#end
#if(${table.convert})
@TableName("${table.name}")
#end
#if(${swagger2})
@ApiModel(value="${entity}对象", description="$!{table.comment}")
#end
#if(${superEntityClass})
public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {
#elseif(${activeRecord})
public class ${entity} extends Model<${entity}> {
#else
public class ${entity} implements Serializable {
#end
#if(${entitySerialVersionUID})
private static final long serialVersionUID = 1L;
#end
## ---------- BEGIN 字段循环遍历 ----------
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
#if("$!field.comment" != "")
#if(${swagger2})
@ApiModelProperty(value = "${field.comment}")
#else
/**
* ${field.comment}
*/
#end
#end
#if(${field.keyFlag})
## 主键
#if(${field.keyIdentityFlag})
@TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
#elseif(!$null.isNull(${idType}) && "$!idType" != "")
@TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
#elseif(${field.convert})
@TableId("${field.annotationColumnName}")
#end
## 普通字段
#elseif(${field.fill})
## ----- 存在字段填充设置 -----
#if(${field.convert})
@TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
#else
@TableField(fill = FieldFill.${field.fill})
#end
#elseif(${field.convert})
@TableField("${field.annotationColumnName}")
#end
## 乐观锁注解
#if(${versionFieldName}==${field.name})
@Version
#end
## 逻辑删除注解
#if(${logicDeleteFieldName}==${field.name})
@TableLogic
#end
private ${field.propertyType} ${field.propertyName};
#end
## ---------- END 字段循环遍历 ----------
#if(!${entityLombokModel})
#foreach($field in ${table.fields})
#if(${field.propertyType.equals("boolean")})
#set($getprefix="is")
#else
#set($getprefix="get")
#end
public ${field.propertyType} ${getprefix}${field.capitalName}() {
return ${field.propertyName};
}
#if(${chainModel})
public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
#else
public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
#end
this.${field.propertyName} = ${field.propertyName};
#if(${chainModel})
return this;
#end
}
#end
#end
#if(${entityColumnConstant})
#foreach($field in ${table.fields})
public static final String ${field.name.toUpperCase()} = "${field.name}";
#end
#end
#if(${activeRecord})
@Override
protected Serializable pkVal() {
#if(${keyPropertyName})
return this.${keyPropertyName};
#else
return null;
#end
}
#end
#if(!${entityLombokModel})
@Override
public String toString() {
return "${entity}{" +
#foreach($field in ${table.fields})
#if($!{foreach.index}==0)
"${field.propertyName}=" + ${field.propertyName} +
#else
", ${field.propertyName}=" + ${field.propertyName} +
#end
#end
"}";
}
#end
}
Mapper模板示例 (mapper.java.vm)
velocity
package ${package.Mapper};
import ${package.Entity}.${entity};
import ${superMapperClassPackage};
#if(${mapperAnnotation})
import org.apache.ibatis.annotations.Mapper;
#end
/**
* <p>
* $!{table.comment} Mapper 接口
* </p>
*
* @author ${author}
* @since ${date}
*/
#if(${mapperAnnotation})
@Mapper
#end
#if(${kotlin})
interface ${table.mapperName} : ${superMapperClass}<${entity}>
#else
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {
/**
* 根据用户名查询用户
* @param username 用户名
* @return 用户信息
*/
${entity} selectByUsername(String username);
/**
* 根据状态查询用户列表
* @param status 状态
* @return 用户列表
*/
List<${entity}> selectByStatus(Integer status);
/**
* 批量更新状态
* @param ids ID列表
* @param status 状态
* @return 更新数量
*/
int updateStatusByIds(@Param("ids") List<Long> ids, @Param("status") Integer status);
/**
* 统计各状态用户数量
* @return 统计结果
*/
List<Map<String, Object>> countByStatus();
}
#end
Service模板示例 (service.java.vm)
velocity
package ${package.Service};
import ${package.Entity}.${entity};
import ${superServiceClassPackage};
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import java.util.Map;
/**
* <p>
* $!{table.comment} 服务类
* </p>
*
* @author ${author}
* @since ${date}
*/
#if(${kotlin})
interface ${table.serviceName} : ${superServiceClass}<${entity}>
#else
public interface ${table.serviceName} extends ${superServiceClass}<${entity}> {
/**
* 根据用户名查询用户
* @param username 用户名
* @return 用户信息
*/
${entity} getByUsername(String username);
/**
* 分页查询用户
* @param page 分页参数
* @param queryParam 查询参数
* @return 分页结果
*/
Page<${entity}> pageQuery(Page<${entity}> page, ${entity}QueryParam queryParam);
/**
* 批量更新状态
* @param ids ID列表
* @param status 状态
* @return 是否成功
*/
boolean updateStatusBatch(List<Long> ids, Integer status);
/**
* 导出数据
* @param queryParam 查询参数
* @return 导出数据
*/
List<${entity}ExportVO> exportData(${entity}QueryParam queryParam);
/**
* 统计数据
* @return 统计结果
*/
Map<String, Object> getStatistics();
}
#end
Controller模板示例 (controller.java.vm)
velocity
package ${package.Controller};
import org.springframework.web.bind.annotation.*;
#if(${restControllerStyle})
import org.springframework.web.bind.annotation.RestController;
#else
import org.springframework.stereotype.Controller;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end
import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.util.List;
import java.util.Map;
#if(${swagger2})
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
#end
/**
* <p>
* $!{table.comment} 前端控制器
* </p>
*
* @author ${author}
* @since ${date}
*/
#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
#if(${swagger2})
@Api(value = "$!{table.comment}管理", tags = "$!{table.comment}管理")
#end
@Validated
#if(${kotlin})
class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end
#else
#if(${superControllerClass})
public class ${table.controllerName} extends ${superControllerClass} {
#else
public class ${table.controllerName} {
#end
@Autowired
private ${table.serviceName} ${table.entityPath}Service;
/**
* 分页查询
*/
@GetMapping("/page")
#if(${swagger2})
@ApiOperation(value = "分页查询$!{table.comment}")
#end
public Result<Page<${entity}>> page(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size,
${entity}QueryParam queryParam) {
Page<${entity}> page = new Page<>(current, size);
Page<${entity}> result = ${table.entityPath}Service.pageQuery(page, queryParam);
return Result.success(result);
}
/**
* 根据ID查询
*/
@GetMapping("/{id}")
#if(${swagger2})
@ApiOperation(value = "根据ID查询$!{table.comment}")
#end
public Result<${entity}> getById(
#if(${swagger2})
@ApiParam(value = "主键ID", required = true)
#end
@PathVariable Long id) {
${entity} entity = ${table.entityPath}Service.getById(id);
return Result.success(entity);
}
/**
* 新增
*/
@PostMapping
#if(${swagger2})
@ApiOperation(value = "新增$!{table.comment}")
#end
public Result<Boolean> save(
#if(${swagger2})
@ApiParam(value = "$!{table.comment}对象", required = true)
#end
@Valid @RequestBody ${entity}SaveDTO saveDTO) {
${entity} entity = BeanUtil.copyProperties(saveDTO, ${entity}.class);
boolean result = ${table.entityPath}Service.save(entity);
return Result.success(result);
}
/**
* 修改
*/
@PutMapping("/{id}")
#if(${swagger2})
@ApiOperation(value = "修改$!{table.comment}")
#end
public Result<Boolean> updateById(
#if(${swagger2})
@ApiParam(value = "主键ID", required = true)
#end
@PathVariable Long id,
#if(${swagger2})
@ApiParam(value = "$!{table.comment}对象", required = true)
#end
@Valid @RequestBody ${entity}UpdateDTO updateDTO) {
${entity} entity = BeanUtil.copyProperties(updateDTO, ${entity}.class);
entity.setId(id);
boolean result = ${table.entityPath}Service.updateById(entity);
return Result.success(result);
}
/**
* 删除
*/
@DeleteMapping("/{id}")
#if(${swagger2})
@ApiOperation(value = "删除$!{table.comment}")
#end
public Result<Boolean> removeById(
#if(${swagger2})
@ApiParam(value = "主键ID", required = true)
#end
@PathVariable Long id) {
boolean result = ${table.entityPath}Service.removeById(id);
return Result.success(result);
}
/**
* 批量删除
*/
@DeleteMapping("/batch")
#if(${swagger2})
@ApiOperation(value = "批量删除$!{table.comment}")
#end
public Result<Boolean> removeByIds(
#if(${swagger2})
@ApiParam(value = "主键ID列表", required = true)
#end
@NotEmpty @RequestBody List<Long> ids) {
boolean result = ${table.entityPath}Service.removeByIds(ids);
return Result.success(result);
}
/**
* 批量更新状态
*/
@PutMapping("/status")
#if(${swagger2})
@ApiOperation(value = "批量更新状态")
#end
public Result<Boolean> updateStatus(
#if(${swagger2})
@ApiParam(value = "状态更新参数", required = true)
#end
@Valid @RequestBody ${entity}StatusUpdateDTO statusUpdateDTO) {
boolean result = ${table.entityPath}Service.updateStatusBatch(
statusUpdateDTO.getIds(),
statusUpdateDTO.getStatus());
return Result.success(result);
}
/**
* 导出数据
*/
@GetMapping("/export")
#if(${swagger2})
@ApiOperation(value = "导出$!{table.comment}数据")
#end
public void export(HttpServletResponse response, ${entity}QueryParam queryParam) {
List<${entity}ExportVO> data = ${table.entityPath}Service.exportData(queryParam);
ExcelUtil.export(response, "$!{table.comment}数据", ${entity}ExportVO.class, data);
}
/**
* 获取统计数据
*/
@GetMapping("/statistics")
#if(${swagger2})
@ApiOperation(value = "获取统计数据")
#end
public Result<Map<String, Object>> getStatistics() {
Map<String, Object> statistics = ${table.entityPath}Service.getStatistics();
return Result.success(statistics);
}
}
#end
代码生成最佳实践
简要描述:代码生成最佳实践是在实际项目中使用MybatisPlus代码生成器的经验总结和规范指导。
核心概念:
- 规范化配置:统一的代码生成配置标准
- 模板标准化:可复用的模板设计
- 分层架构:符合项目架构的代码生成
- 扩展性设计:便于后续维护和扩展
- 自动化集成:与CI/CD流程的集成
项目结构最佳实践
java
/**
* 项目结构配置
*/
public class ProjectStructureBestPractice {
/**
* 标准项目结构配置
*/
public void configureStandardStructure() {
// 推荐的项目结构
/*
src/main/java/
├── com.example.project/
│ ├── common/ // 公共模块
│ │ ├── entity/ // 基础实体类
│ │ ├── mapper/ // 基础Mapper
│ │ ├── service/ // 基础Service
│ │ ├── controller/ // 基础Controller
│ │ ├── config/ // 配置类
│ │ ├── util/ // 工具类
│ │ └── exception/ // 异常类
│ ├── module1/ // 业务模块1
│ │ ├── entity/
│ │ ├── mapper/
│ │ ├── service/
│ │ │ └── impl/
│ │ ├── controller/
│ │ ├── dto/ // 数据传输对象
│ │ ├── vo/ // 视图对象
│ │ └── convert/ // 对象转换器
│ └── module2/ // 业务模块2
│ └── ...
src/main/resources/
├── mapper/ // MyBatis XML文件
│ ├── module1/
│ └── module2/
├── templates/ // 代码生成模板
│ ├── entity.java.vm
│ ├── mapper.java.vm
│ ├── mapper.xml.vm
│ ├── service.java.vm
│ ├── serviceImpl.java.vm
│ └── controller.java.vm
└── application.yml
*/
}
/**
* 微服务项目结构配置
*/
public void configureMicroserviceStructure() {
// 微服务推荐结构
/*
user-service/
├── user-api/ // API接口模块
│ └── src/main/java/
│ └── com.example.user.api/
│ ├── entity/ // 实体类
│ ├── dto/ // 传输对象
│ ├── vo/ // 视图对象
│ └── feign/ // Feign接口
├── user-service/ // 服务实现模块
│ └── src/main/java/
│ └── com.example.user.service/
│ ├── mapper/
│ ├── service/
│ ├── controller/
│ └── config/
└── user-common/ // 公共模块
└── src/main/java/
└── com.example.user.common/
├── base/
├── util/
└── exception/
*/
}
}
配置管理最佳实践
java
/**
* 配置管理最佳实践
*/
@Configuration
public class GeneratorConfigBestPractice {
/**
* 环境配置管理
*/
@Bean
@ConfigurationProperties(prefix = "generator")
public GeneratorProperties generatorProperties() {
return new GeneratorProperties();
}
/**
* 代码生成器配置
*/
public AutoGenerator buildGenerator(GeneratorProperties properties) {
AutoGenerator generator = new AutoGenerator();
// 全局配置
GlobalConfig globalConfig = buildGlobalConfig(properties);
generator.setGlobalConfig(globalConfig);
// 数据源配置
DataSourceConfig dataSourceConfig = buildDataSourceConfig(properties);
generator.setDataSource(dataSourceConfig);
// 包配置
PackageConfig packageConfig = buildPackageConfig(properties);
generator.setPackageInfo(packageConfig);
// 策略配置
StrategyConfig strategyConfig = buildStrategyConfig(properties);
generator.setStrategy(strategyConfig);
// 模板配置
TemplateConfig templateConfig = buildTemplateConfig(properties);
generator.setTemplate(templateConfig);
return generator;
}
/**
* 构建全局配置
*/
private GlobalConfig buildGlobalConfig(GeneratorProperties properties) {
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(properties.getOutputDir())
.setAuthor(properties.getAuthor())
.setOpen(false)
.setFileOverride(properties.isFileOverride())
.setActiveRecord(properties.isActiveRecord())
.setEnableCache(false)
.setBaseResultMap(true)
.setBaseColumnList(true)
.setSwagger2(properties.isSwagger2());
// 自定义文件命名
if (properties.getEntityName() != null) {
globalConfig.setEntityName(properties.getEntityName());
}
if (properties.getMapperName() != null) {
globalConfig.setMapperName(properties.getMapperName());
}
if (properties.getServiceName() != null) {
globalConfig.setServiceName(properties.getServiceName());
}
if (properties.getServiceImplName() != null) {
globalConfig.setServiceImplName(properties.getServiceImplName());
}
if (properties.getControllerName() != null) {
globalConfig.setControllerName(properties.getControllerName());
}
return globalConfig;
}
/**
* 构建数据源配置
*/
private DataSourceConfig buildDataSourceConfig(GeneratorProperties properties) {
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setUrl(properties.getUrl())
.setDriverName(properties.getDriverName())
.setUsername(properties.getUsername())
.setPassword(properties.getPassword())
.setDbType(DbType.MYSQL);
return dataSourceConfig;
}
/**
* 构建包配置
*/
private PackageConfig buildPackageConfig(GeneratorProperties properties) {
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent(properties.getParentPackage())
.setModuleName(properties.getModuleName())
.setEntity("entity")
.setMapper("mapper")
.setService("service")
.setServiceImpl("service.impl")
.setController("controller");
return packageConfig;
}
/**
* 构建策略配置
*/
private StrategyConfig buildStrategyConfig(GeneratorProperties properties) {
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setNaming(NamingStrategy.underline_to_camel)
.setColumnNaming(NamingStrategy.underline_to_camel)
.setEntityLombokModel(true)
.setRestControllerStyle(true)
.setControllerMappingHyphenStyle(true);
// 表配置
if (properties.getIncludeTables() != null && properties.getIncludeTables().length > 0) {
strategyConfig.setInclude(properties.getIncludeTables());
}
if (properties.getExcludeTables() != null && properties.getExcludeTables().length > 0) {
strategyConfig.setExclude(properties.getExcludeTables());
}
if (properties.getTablePrefix() != null && properties.getTablePrefix().length > 0) {
strategyConfig.setTablePrefix(properties.getTablePrefix());
}
// 父类配置
if (properties.getSuperEntityClass() != null) {
strategyConfig.setSuperEntityClass(properties.getSuperEntityClass())
.setSuperEntityColumns(properties.getSuperEntityColumns());
}
if (properties.getSuperMapperClass() != null) {
strategyConfig.setSuperMapperClass(properties.getSuperMapperClass());
}
if (properties.getSuperServiceClass() != null) {
strategyConfig.setSuperServiceClass(properties.getSuperServiceClass());
}
if (properties.getSuperServiceImplClass() != null) {
strategyConfig.setSuperServiceImplClass(properties.getSuperServiceImplClass());
}
if (properties.getSuperControllerClass() != null) {
strategyConfig.setSuperControllerClass(properties.getSuperControllerClass());
}
// 填充字段配置
if (properties.getTableFillList() != null && !properties.getTableFillList().isEmpty()) {
List<TableFill> tableFillList = properties.getTableFillList().stream()
.map(fill -> new TableFill(fill.getFieldName(), fill.getFieldFill()))
.collect(Collectors.toList());
strategyConfig.setTableFillList(tableFillList);
}
// 乐观锁和逻辑删除
if (properties.getVersionFieldName() != null) {
strategyConfig.setVersionFieldName(properties.getVersionFieldName());
}
if (properties.getLogicDeleteFieldName() != null) {
strategyConfig.setLogicDeleteFieldName(properties.getLogicDeleteFieldName());
}
return strategyConfig;
}
/**
* 构建模板配置
*/
private TemplateConfig buildTemplateConfig(GeneratorProperties properties) {
TemplateConfig templateConfig = new TemplateConfig();
// 自定义模板路径
if (properties.getTemplatePath() != null) {
String templatePath = properties.getTemplatePath();
templateConfig.setEntity(templatePath + "/entity.java.vm")
.setMapper(templatePath + "/mapper.java.vm")
.setMapperXml(templatePath + "/mapper.xml.vm")
.setService(templatePath + "/service.java.vm")
.setServiceImpl(templatePath + "/serviceImpl.java.vm")
.setController(templatePath + "/controller.java.vm");
}
// 禁用某些模板
if (!properties.isGenerateEntity()) {
templateConfig.setEntity(null);
}
if (!properties.isGenerateMapper()) {
templateConfig.setMapper(null);
}
if (!properties.isGenerateMapperXml()) {
templateConfig.setMapperXml(null);
}
if (!properties.isGenerateService()) {
templateConfig.setService(null);
}
if (!properties.isGenerateServiceImpl()) {
templateConfig.setServiceImpl(null);
}
if (!properties.isGenerateController()) {
templateConfig.setController(null);
}
return templateConfig;
}
}
/**
* 生成器配置属性
*/
@Data
@ConfigurationProperties(prefix = "generator")
public class GeneratorProperties {
// 全局配置
private String outputDir = System.getProperty("user.dir") + "/src/main/java";
private String author = "generator";
private boolean fileOverride = false;
private boolean activeRecord = false;
private boolean swagger2 = true;
// 数据源配置
private String url;
private String driverName;
private String username;
private String password;
// 包配置
private String parentPackage;
private String moduleName;
// 表配置
private String[] includeTables;
private String[] excludeTables;
private String[] tablePrefix;
// 父类配置
private String superEntityClass;
private String[] superEntityColumns;
private String superMapperClass;
private String superServiceClass;
private String superServiceImplClass;
private String superControllerClass;
// 填充字段配置
private List<TableFillConfig> tableFillList;
// 乐观锁和逻辑删除
private String versionFieldName;
private String logicDeleteFieldName;
// 文件命名配置
private String entityName;
private String mapperName;
private String serviceName;
private String serviceImplName;
private String controllerName;
// 模板配置
private String templatePath;
private boolean generateEntity = true;
private boolean generateMapper = true;
private boolean generateMapperXml = true;
private boolean generateService = true;
private boolean generateServiceImpl = true;
private boolean generateController = true;
@Data
public static class TableFillConfig {
private String fieldName;
private FieldFill fieldFill;
}
}
模板设计最佳实践
java
/**
* 模板设计最佳实践
*/
public class TemplateBestPractice {
/**
* 模板设计原则
*/
public void templateDesignPrinciples() {
/*
1. 单一职责原则
- 每个模板只负责生成一种类型的文件
- 避免在一个模板中混合多种逻辑
2. 可配置性原则
- 通过变量控制代码生成的行为
- 支持条件渲染,适应不同场景
3. 可扩展性原则
- 预留扩展点,便于后续功能增加
- 支持自定义变量和函数
4. 一致性原则
- 统一的代码风格和命名规范
- 一致的注释和文档格式
5. 可维护性原则
- 清晰的模板结构和逻辑
- 充分的注释说明
*/
}
/**
* 模板变量最佳实践
*/
public void templateVariablesBestPractice() {
/*
常用模板变量:
全局变量:
- ${author} // 作者
- ${date} // 日期
- ${package.Entity} // 实体包名
- ${package.Mapper} // Mapper包名
- ${package.Service} // Service包名
- ${package.Controller} // Controller包名
表相关变量:
- ${table.name} // 表名
- ${table.comment} // 表注释
- ${table.entityName} // 实体类名
- ${table.entityPath} // 实体路径
- ${table.mapperName} // Mapper名
- ${table.serviceName} // Service名
- ${table.controllerName} // Controller名
- ${table.fields} // 字段列表
- ${table.importPackages} // 导入包列表
字段相关变量:
- ${field.name} // 字段名
- ${field.type} // 字段类型
- ${field.propertyName} // 属性名
- ${field.propertyType} // 属性类型
- ${field.comment} // 字段注释
- ${field.keyFlag} // 是否主键
- ${field.keyIdentityFlag} // 是否自增主键
- ${field.fill} // 填充策略
配置相关变量:
- ${swagger2} // 是否启用Swagger
- ${entityLombokModel} // 是否使用Lombok
- ${chainModel} // 是否链式模型
- ${activeRecord} // 是否ActiveRecord
- ${restControllerStyle} // 是否REST风格
- ${superEntityClass} // 父类实体
- ${superMapperClass} // 父类Mapper
- ${superServiceClass} // 父类Service
- ${superControllerClass} // 父类Controller
*/
}
/**
* 条件渲染最佳实践
*/
public void conditionalRenderingBestPractice() {
/*
条件渲染示例:
1. 基础条件判断
#if(${swagger2})
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
#end
2. 字段循环与条件
#foreach($field in ${table.fields})
#if(${field.keyFlag})
@TableId(value = "${field.name}", type = IdType.AUTO)
#elseif(${field.fill})
@TableField(value = "${field.name}", fill = FieldFill.${field.fill})
#else
@TableField("${field.name}")
#end
private ${field.propertyType} ${field.propertyName};
#end
3. 复杂条件组合
#if(${entityLombokModel})
@Data
#if(${superEntityClass})
@EqualsAndHashCode(callSuper = true)
#else
@EqualsAndHashCode(callSuper = false)
#end
#if(${chainModel})
@Accessors(chain = true)
#end
#end
4. 包导入条件
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${swagger2})
import io.swagger.annotations.ApiModel;
#end
#if(${entityLombokModel})
import lombok.Data;
#end
*/
}
}
代码质量最佳实践
java
/**
* 代码质量最佳实践
*/
public class CodeQualityBestPractice {
/**
* 代码规范检查
*/
public void codeStandardCheck() {
/*
1. 命名规范
- 类名:大驼峰命名法(PascalCase)
- 方法名:小驼峰命名法(camelCase)
- 常量名:全大写下划线分隔(UPPER_SNAKE_CASE)
- 包名:全小写点分隔
2. 注释规范
- 类注释:包含类的作用、作者、创建时间
- 方法注释:包含方法作用、参数说明、返回值说明
- 字段注释:说明字段的含义和用途
3. 代码结构
- 合理的包结构划分
- 清晰的层次关系
- 适当的设计模式应用
4. 异常处理
- 统一的异常处理机制
- 合适的异常类型选择
- 完整的异常信息记录
*/
}
/**
* 性能优化建议
*/
public void performanceOptimization() {
/*
1. 数据库层面
- 合理的索引设计
- 避免N+1查询问题
- 使用批量操作
- 合适的分页策略
2. 代码层面
- 避免在循环中进行数据库操作
- 使用缓存减少重复查询
- 合理使用连接池
- 异步处理耗时操作
3. 架构层面
- 读写分离
- 数据库分片
- 缓存策略
- 消息队列
*/
}
/**
* 安全性考虑
*/
public void securityConsiderations() {
/*
1. SQL注入防护
- 使用参数化查询
- 输入验证和过滤
- 避免动态SQL拼接
2. 数据验证
- 前端和后端双重验证
- 数据类型和格式检查
- 业务规则验证
3. 权限控制
- 接口权限验证
- 数据权限控制
- 操作日志记录
4. 敏感信息保护
- 密码加密存储
- 敏感数据脱敏
- 日志信息过滤
*/
}
}
团队协作最佳实践
java
/**
* 团队协作最佳实践
*/
public class TeamCollaborationBestPractice {
/**
* 配置标准化
*/
public void configurationStandardization() {
/*
1. 统一配置文件
- 使用配置文件管理生成器配置
- 不同环境使用不同配置
- 版本控制配置文件
2. 模板标准化
- 团队统一的代码模板
- 定期更新和维护模板
- 模板使用文档
3. 命名规范
- 统一的命名约定
- 清晰的包结构规范
- 一致的文件组织方式
*/
}
/**
* 版本控制策略
*/
public void versionControlStrategy() {
/*
1. 代码生成器配置版本控制
- 配置文件纳入版本控制
- 模板文件版本管理
- 生成脚本版本控制
2. 生成代码的版本控制策略
- 基础代码纳入版本控制
- 自定义修改的代码保护
- 重新生成时的冲突处理
3. 分支管理
- 功能分支开发
- 代码审查流程
- 合并策略制定
*/
}
/**
* 文档管理
*/
public void documentationManagement() {
/*
1. 代码生成文档
- 生成器使用说明
- 配置参数文档
- 模板自定义指南
2. API文档
- 自动生成API文档
- 接口变更记录
- 版本兼容性说明
3. 开发规范文档
- 编码规范
- 数据库设计规范
- 接口设计规范
*/
}
/**
* 持续集成实践
*/
public void continuousIntegrationPractice() {
/*
1. 自动化代码生成
- CI/CD流程中集成代码生成
- 数据库变更自动生成代码
- 生成代码的自动测试
2. 代码质量检查
- 静态代码分析
- 代码覆盖率检查
- 性能测试集成
3. 部署自动化
- 自动化构建
- 环境配置管理
- 回滚策略制定
*/
}
}