Boost Your Spring Boot CRUD with MyBatisPlusPro: A Step‑by‑Step Guide

This tutorial walks you through creating a reusable BaseController in Spring Boot using MyBatis‑Plus, introducing a utility class for dynamic query building, configuring pagination support, and extending the controller for any entity to instantly gain full CRUD, list, pagination, sorting, and count operations.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Boost Your Spring Boot CRUD with MyBatisPlusPro: A Step‑by‑Step Guide

Hello, I'm Peng Lei.

MyBatis‑Plus is convenient for DAO‑level CRUD, but to provide similar capabilities at the controller layer we create MyBatisPlusPro . By extending a generic BaseController, you obtain CRUD, list query, pagination, sorting, parameterized query, and count functionality with minimal code.

Step 1: Add MyBatis‑Plus Dependency

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

Step 2: Write Utility Class

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * Apprentice system utility
 */
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 an entity */
    public static <E> QueryWrapper<E> getQueryWrapper(E entity) {
        Field[] fields = entity.getClass().getDeclaredFields();
        QueryWrapper<E> eQueryWrapper = new QueryWrapper<>();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (Modifier.isFinal(field.getModifiers())) {
                continue;
            }
            field.setAccessible(true);
            try {
                Object obj = field.get(entity);
                if (!ObjectUtils.isEmpty(obj)) {
                    String name = ApprenticeUtil.humpToLine(field.getName());
                    eQueryWrapper.eq(name, obj);
                }
            } catch (IllegalAccessException e) {
                return null;
            }
        }
        return eQueryWrapper;
    }
    /** Reflectively get a field value */
    public static <E> Object getValueForClass(E entity, String value) {
        Field id = null;
        PropertyDescriptor pd = null;
        try {
            id = entity.getClass().getDeclaredField(value);
            pd = new PropertyDescriptor(id.getName(), entity.getClass());
        } catch (NoSuchFieldException | IntrospectionException e) {
            e.printStackTrace();
        }
        Method getMethod = Objects.requireNonNull(pd).getReadMethod();
        return ReflectionUtils.invokeMethod(getMethod, entity);
    }
}

Step 3: Create a Generic BaseController

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.wangfugui.apprentice.common.util.ApprenticeUtil;
import com.wangfugui.apprentice.common.util.ResponseUtils;
import com.wangfugui.apprentice.dao.dto.PageParamDto;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/** Core public controller */
public class BaseController<S extends IService<E>, E> {
    @Autowired
    protected S baseService;
    @ApiOperation("Add")
    @PostMapping("/insert")
    public ResponseUtils insert(@RequestBody E entity) {
        baseService.save(entity);
        return ResponseUtils.success("Add successful");
    }
    @ApiOperation("Delete")
    @PostMapping("/deleteById")
    public ResponseUtils delete(@RequestBody List<Integer> ids) {
        baseService.removeByIds(ids);
        return ResponseUtils.success("Delete successful");
    }
    @ApiOperation("Update")
    @PostMapping("/updateById")
    public ResponseUtils updateById(@RequestBody E entity) {
        baseService.updateById(entity);
        return ResponseUtils.success("Update successful");
    }
    @ApiOperation("Get by ID")
    @GetMapping("/getById")
    public ResponseUtils getById(@RequestParam Integer id) {
        return ResponseUtils.success(baseService.getById(id));
    }
    @ApiOperation("Save")
    @PostMapping("/save")
    public ResponseUtils save(@RequestBody E entity) {
        baseService.saveOrUpdate(entity);
        return ResponseUtils.success("Save successful");
    }
    @ApiOperation("List query")
    @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 query")
    @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<>();
        String asc = pageParamDto.getAsc();
        if (!StrUtil.isEmpty(asc) && !"null".equals(asc)) {
            queryWrapper.orderByAsc(asc.split(","));
        }
        String desc = pageParamDto.getDesc();
        if (!StrUtil.isEmpty(desc) && !"null".equals(desc)) {
            queryWrapper.orderByDesc(desc.split(","));
        }
        Page<E> ePage = baseService.page(page, queryWrapper);
        return ResponseUtils.success(ePage);
    }
    @ApiOperation("Count")
    @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: Configure Pagination Plugin

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
    /** Set pagination interceptor */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

Step 5: Extend BaseController in Your Own Controllers

import com.wangfugui.apprentice.dao.domain.Dynamic;
import com.wangfugui.apprentice.service.IDynamicService;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/apprentice/dynamic")
@Api("Dynamic Management")
public class DynamicController extends BaseController<IDynamicService, Dynamic> {
}
import com.wangfugui.apprentice.dao.domain.Blog;
import com.wangfugui.apprentice.service.IBlogService;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/apprentice/blog")
@Api(tags = "Blog Management")
public class BlogController extends BaseController<IBlogService, Blog> {
}

After extending BaseController, the concrete controllers automatically inherit all CRUD endpoints.

Repository address: https://gitee.com/WangFuGui-Ma/mybatis-plus-pro

Reference

[1] https://gitee.com/WangFuGui-Ma/mybatis-plus-pro

JavaSpring BootCRUDRESTful 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.