Boost Your Java Backend with MyBatisPlusPro: A Complete CRUD Controller Guide

This article walks through building a reusable BaseController using MyBatisPlusPro in a Spring Boot application, covering dependency setup, utility methods for query conversion, generic CRUD endpoints, pagination configuration, and how to extend the controller for specific entities, providing ready‑to‑use code snippets for each step.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Boost Your Java Backend with MyBatisPlusPro: A Complete CRUD Controller Guide

MyBatisPlusPro Overview

This guide demonstrates how to create a reusable BaseController that provides generic CRUD operations for any entity in a Spring Boot project using MyBatisPlusPro.

Step 1 – Add MyBatisPlus Dependency

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.2</version>
</dependency>

Step 2 – Write Utility Class

The ApprenticeUtil class contains helper methods for converting between camel‑case and snake‑case, building QueryWrapper objects from an entity, and retrieving field values via reflection.

public class ApprenticeUtil {
    private static Pattern humpPattern = Pattern.compile("[A-Z]");
    private static Pattern linePattern = Pattern.compile("_(\\w)");

    /** Convert camelCase to snake_case */
    public static String humpToLine(String str) {
        Matcher matcher = humpPattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /** Convert snake_case to camelCase */
    public static String lineToHump(String str) {
        str = str.toLowerCase();
        Matcher matcher = linePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /** Build QueryWrapper from non‑null fields */
    public static <E> QueryWrapper<E> getQueryWrapper(E entity) {
        Field[] fields = entity.getClass().getDeclaredFields();
        QueryWrapper<E> queryWrapper = new QueryWrapper<>();
        for (Field field : fields) {
            if (Modifier.isFinal(field.getModifiers())) continue;
            field.setAccessible(true);
            try {
                Object value = field.get(entity);
                if (!ObjectUtils.isEmpty(value)) {
                    String name = humpToLine(field.getName());
                    queryWrapper.eq(name, value);
                }
            } catch (IllegalAccessException e) {
                return null;
            }
        }
        return queryWrapper;
    }

    /** Reflectively get a field value */
    public static <E> Object getValueForClass(E entity, String value) {
        try {
            Field field = entity.getClass().getDeclaredField(value);
            PropertyDescriptor pd = new PropertyDescriptor(field.getName(), entity.getClass());
            Method getter = pd.getReadMethod();
            return ReflectionUtils.invokeMethod(getter, entity);
        } catch (NoSuchFieldException | IntrospectionException e) {
            e.printStackTrace();
            return null;
        }
    }
}

Step 3 – Core Generic Controller

The BaseController class defines CRUD endpoints (insert, delete, update, getById, save, list, page, count) using generic types S extends IService<E> and E. Each method delegates to the injected service and returns a standardized ResponseUtils result.

public class BaseController<S extends IService<E>, E> {
    @Autowired
    protected S baseService;

    @ApiOperation("增")
    @PostMapping("/insert")
    public ResponseUtils insert(@RequestBody E entity) {
        baseService.save(entity);
        return ResponseUtils.success("添加成功");
    }

    @ApiOperation("删")
    @PostMapping("/deleteById")
    public ResponseUtils delete(@RequestBody List<Integer> ids) {
        baseService.removeByIds(ids);
        return ResponseUtils.success("删除成功");
    }

    @ApiOperation("改")
    @PostMapping("/updateById")
    public ResponseUtils updateById(@RequestBody E entity) {
        baseService.updateById(entity);
        return ResponseUtils.success("更新成功");
    }

    @ApiOperation("查")
    @GetMapping("/getById")
    public ResponseUtils getById(@RequestParam Integer id) {
        return ResponseUtils.success(baseService.getById(id));
    }

    @ApiOperation("存")
    @PostMapping("/save")
    public ResponseUtils save(@RequestBody E entity) {
        baseService.saveOrUpdate(entity);
        return ResponseUtils.success("保存成功");
    }

    @ApiOperation("list查")
    @PostMapping("/list")
    public ResponseUtils list(@RequestBody E entity) {
        QueryWrapper<E> queryWrapper = ApprenticeUtil.getQueryWrapper(entity);
        List<E> list = baseService.list(queryWrapper);
        return ResponseUtils.success(list);
    }

    @ApiOperation("page查")
    @PostMapping("/page")
    public ResponseUtils page(@RequestBody PageParamDto<E> pageParamDto) {
        if (pageParamDto.getPage() < 1) pageParamDto.setPage(1);
        if (pageParamDto.getSize() > 100) pageParamDto.setSize(100);
        Page<E> page = new Page<>(pageParamDto.getPage(), pageParamDto.getSize());
        QueryWrapper<E> queryWrapper = new QueryWrapper<>();
        if (!StrUtil.isEmpty(pageParamDto.getAsc()) && !"null".equals(pageParamDto.getAsc())) {
            queryWrapper.orderByAsc(pageParamDto.getAsc().split(","));
        }
        if (!StrUtil.isEmpty(pageParamDto.getDesc()) && !"null".equals(pageParamDto.getDesc())) {
            queryWrapper.orderByDesc(pageParamDto.getDesc().split(","));
        }
        Page<E> ePage = baseService.page(page, queryWrapper);
        return ResponseUtils.success(ePage);
    }

    @ApiOperation("获取数量")
    @PostMapping("/count")
    public ResponseUtils count(@RequestBody E entity) {
        QueryWrapper<E> queryWrapper = ApprenticeUtil.getQueryWrapper(entity);
        long count = baseService.count(queryWrapper);
        return ResponseUtils.success(count);
    }
}

Step 4 – Pagination Configuration

@Configuration
public class MybatisPlusConfig {
    /** Register pagination interceptor */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

Step 5 – Extend the Generic Controller

Create concrete controllers by extending BaseController and specifying the service and entity types.

@RestController
@RequestMapping("/apprentice/dynamic")
@Api("动态管理")
public class DynamicController extends BaseController<IDynamicService, Dynamic> {}
@RestController
@RequestMapping("/apprentice/blog")
@Api(tags = "博客管理")
public class BlogController extends BaseController<IBlogService, Blog> {}

After these steps, the application automatically supports standard CRUD operations for the defined entities.

Reference

https://gitee.com/WangFuGui-Ma/mybatis-plus-pro
Javabackend developmentSpring BootPaginationCRUDRESTful APIMybatisPlus
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.