Backend Development 11 min read

Automating CRUD Operations with MyBatisPlus and a Generic BaseController in Spring Boot

This article demonstrates how to automate CRUD functionality in a Spring Boot application by adding MyBatisPlus, creating utility methods for query building and reflection, implementing a generic BaseController, configuring pagination support, and extending the controller for specific entities, providing a reusable RESTful API template.

Java Captain
Java Captain
Java Captain
Automating CRUD Operations with MyBatisPlus and a Generic BaseController in Spring Boot

Even for simple CRUD, you can extract a reusable set of utilities to automate the operations; this guide walks through building such automation using MyBatisPlus and a generic BaseController in a Spring Boot project.

Step 1: Add MyBatisPlus dependency

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

Step 2: Create 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 utilities
 */
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 a QueryWrapper from an entity */
    public static
QueryWrapper
getQueryWrapper(E entity) {
        Field[] fields = entity.getClass().getDeclaredFields();
        QueryWrapper
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
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: Implement 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
, 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
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 or Update")
    @PostMapping("/save")
    public ResponseUtils save(@RequestBody E entity) {
        baseService.saveOrUpdate(entity);
        return ResponseUtils.success("Save successful");
    }

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

    @ApiOperation("Page")
    @PostMapping("/page")
    public ResponseUtils page(@RequestBody PageParamDto
pageParamDto) {
        if (pageParamDto.getPage() < 1) pageParamDto.setPage(1);
        if (pageParamDto.getSize() > 100) pageParamDto.setSize(100);
        Page
page = new Page<>(pageParamDto.getPage(), pageParamDto.getSize());
        QueryWrapper
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
ePage = baseService.page(page, queryWrapper);
        return ResponseUtils.success(ePage);
    }

    @ApiOperation("Count")
    @PostMapping("/count")
    public ResponseUtils count(@RequestBody E entity) {
        QueryWrapper
queryWrapper = ApprenticeUtil.getQueryWrapper(entity);
        long count = baseService.count(queryWrapper);
        return ResponseUtils.success(count);
    }
}

Step 4: Configure MyBatisPlus pagination support

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 controller

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
{
    // Inherit all CRUD endpoints automatically
}

After extending BaseController , you obtain ready‑made endpoints for insert, delete, update, get by ID, list, pagination, and count without writing additional business logic. This pattern can be reused for any entity, such as Blog , by simply creating a controller that extends BaseController<IBlogService, Blog> .

Use the generated API, give it a star if it helped you, and enjoy rapid development!

JavaSpring BootCRUDRESTful APIMyBatisPlusBaseController
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login 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.