简介:本文聚焦MyBatis-Plus框架中Wrapper自定义SQL与模板生成的进阶用法,通过代码示例与场景分析,揭示如何高效实现复杂查询与代码自动化生成,助力开发者提升开发效率与代码质量。
MyBatis-Plus作为MyBatis的增强工具,其核心优势在于简化CRUD操作,而Wrapper机制(如QueryWrapper、LambdaQueryWrapper)则进一步抽象了SQL构建过程。然而,当业务需求超出标准查询范围时,自定义SQL成为关键突破口。
FIND_IN_SET)。MyBatis-Plus提供了两种集成方式:
apply()方法注入原始SQL。
QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.apply("date_format(create_time,'%Y-%m') = {0}", "2023-10");
<!-- UserMapper.xml --><select id="selectByCustomCondition" resultType="User">SELECT * FROM userWHERE ${ew.customSqlSegment}AND status = #{status}</select>
业务中常需根据条件切换表名(如分表场景),可通过@TableField注解与Wrapper结合实现:
// 动态表名拦截器配置@Beanpublic DynamicTableNameInterceptor dynamicTableNameInterceptor() {DynamicTableNameInterceptor interceptor = new DynamicTableNameInterceptor();Map<String, TableNameHandler> map = new HashMap<>();map.put("user", (sql, tableName) -> {// 从ThreadLocal或参数中获取动态表名String suffix = ThreadLocalUtil.get();return tableName + "_" + suffix;});interceptor.setTableNameHandlerMap(map);return interceptor;}// 使用示例LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();wrapper.eq(User::getName, "test");// 实际执行SQL:SELECT * FROM user_2023 WHERE name = 'test'
对于包含OR、IN等复杂逻辑的查询,建议采用以下模式:
// 方式1:使用nested()构建嵌套条件QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.nested(w -> w.eq("role", "admin").or().eq("role", "super_admin")).inSql("dept_id", "SELECT id FROM department WHERE level > 2");// 方式2:XML中结合OGNL表达式<select id="selectByComplexCondition" resultType="User">SELECT * FROM user<where><if test="ew != null">${ew.sqlSegment}</if>AND (role = 'admin'OR<foreach collection="roles" item="role" open="(" separator="OR" close=")">role = #{role}</foreach>)</where></select>
MyBatis-Plus的代码生成器支持通过TemplateEngine自定义输出模板,典型配置如下:
AutoGenerator generator = new AutoGenerator();// 全局配置GlobalConfig gc = new GlobalConfig();gc.setAuthor("dev");gc.setOutputDir(System.getProperty("user.dir") + "/src/main/java");gc.setFileOverride(true);generator.setGlobalConfig(gc);// 数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl("jdbc:mysql://localhost:3306/test");dsc.setDriverName("com.mysql.cj.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("password");generator.setDataSource(dsc);// 模板配置TemplateConfig templateConfig = new TemplateConfig();templateConfig.setEntity("templates/entity.java"); // 自定义实体模板路径templateConfig.setMapper("templates/mapper.java");generator.setTemplate(templateConfig);// 注入自定义模板引擎FastAutoGenerator.createGenerator(generator).templateEngine(new FreemarkerTemplateEngine() {@Overridepublic String getTemplateContent(TemplateFile templateFile) {// 修改默认模板内容if ("entity.java".equals(templateFile.getFileName())) {return "package ${package.Entity};\n" +"// 自定义实体类模板\n" +"public class ${entity} {\n" +" // 示例:自动添加审计字段\n" +" private Date createTime;\n" +" private Date updateTime;\n" +"}";}return super.getTemplateContent(templateFile);}}).execute();
<!-- mapper.java.ftl 片段 -->public interface ${table.mapperName} extends BaseMapper<${entity}> {/*** 自定义查询方法* @param wrapper 条件构造器* @return 查询结果*/@Select("SELECT * FROM ${table.name} ${ew.customSqlSegment}")List<${entity}> selectByWrapper(@Param(Constants.WRAPPER) Wrapper<${entity}> wrapper);}
// 生成的Service类示例public interface IUserService extends IService<User> {default List<User> selectByCustomCondition(String name, List<Integer> ages) {LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();wrapper.like(User::getName, name).in(User::getAge, ages);return baseMapper.selectList(wrapper);}}
#{}参数占位符,避免${}直接拼接。
public class UserQueryWrapper {public static final LambdaQueryWrapper<User> ACTIVE_USER = Wrappers.lambdaQuery().eq(User::getStatus, 1).ge(User::getCreateTime, LocalDate.now().minusYears(1));}
saveBatch()结合自定义SQL实现高效批量插入。问题:自定义SQL中Wrapper条件不生效。
解决:检查XML中是否正确使用${ew.customSqlSegment},而非${ew.sqlSegment}(后者不包含WHERE关键字)。
问题:动态表名拦截器失效。
解决:确保拦截器配置在Spring容器中且执行顺序优先于分页拦截器。
通过合理运用MyBatis-Plus的Wrapper自定义SQL与模板生成功能,开发者可以:
未来发展方向可关注:
(全文约3200字,涵盖了从基础用法到高级定制的全流程实践,适用于MyBatis-Plus 3.x版本)