MybatisPlus Pro: Supercharging CRUD Development Efficiency

This article analyzes MybatisPlus Pro, explaining how it eliminates repetitive CRUD code in MyBatis‑Plus projects by providing a BaseController and utility classes that auto‑generate service and controller layers, while also detailing its internal mechanisms, advantages, drawbacks, suitable scenarios, and common pitfalls.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
MybatisPlus Pro: Supercharging CRUD Development Efficiency

Why MybatisPlus Pro is needed

Even after adopting MyBatis‑Plus, developers still write repetitive code in Service and Controller layers:

Service interfaces repeatedly declare findById, findAll, insert, update, delete methods that merely delegate to BaseMapper.

Each new module requires a copy‑paste of CRUD controller methods, leading to hours of boiler‑plate for dozens of modules.

Code quality varies between developers, causing inconsistent validation and error handling.

Conditional queries require almost identical QueryWrapper configuration in every controller.

The goal of MyBatis‑Plus Pro is to automate these patterns so that most effort can focus on business logic.

What MybatisPlus Pro provides

MyBatis‑Plus Pro adds a set of ready‑made base classes that automatically handle CRUD, pagination, list, and conditional queries without writing any XML or controller code. It builds on the existing MyBatis‑Plus core without altering its behavior.

Quick start

Step 1 – Add dependency

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.5.15</version>
</dependency>
Version 3.5.15 (released 2025‑11‑30) supports Spring Boot 4.0.0 and Jackson 3.0.

Step 2 – Create utility class

The utility class supplies three core functions: camel‑case ↔ snake‑case conversion, reflection‑based extraction of entity field values, and automatic generation of a QueryWrapper from non‑null fields.

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

    // camelCase → 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();
    }

    // snake_case → 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> wrapper = new QueryWrapper<>();
        for (Field field : fields) {
            if (Modifier.isFinal(field.getModifiers())) continue;
            field.setAccessible(true);
            try {
                Object val = field.get(entity);
                if (val != null) {
                    String name = humpToLine(field.getName());
                    wrapper.eq(name, val);
                }
            } catch (IllegalAccessException e) {
                return null;
            }
        }
        return wrapper;
    }
}

Step 3 – Implement BaseController

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

    @PostMapping("add")
    public Result add(@RequestBody T entity) {
        return Result.success(baseService.save(entity));
    }

    @PostMapping("update")
    public Result update(@RequestBody T entity) {
        return Result.success(baseService.updateById(entity));
    }

    @GetMapping("delete")
    public Result delete(String id) {
        return Result.success(baseService.removeById(id));
    }

    @GetMapping("detail")
    public Result detail(String id) {
        return Result.success(baseService.getById(id));
    }

    @PostMapping("list")
    public Result list(@RequestBody T entity) {
        QueryWrapper<T> wrapper = ApprenticeUtil.getQueryWrapper(entity);
        if (wrapper == null) wrapper = new QueryWrapper<>();
        return Result.success(baseService.list(wrapper));
    }

    @PostMapping("page")
    public Result page(@RequestBody PageDto<T> pageDto) {
        T entity = pageDto.getEntity();
        QueryWrapper<T> wrapper = ApprenticeUtil.getQueryWrapper(entity);
        if (wrapper == null) wrapper = new QueryWrapper<>();
        IPage<T> page = new Page<>(pageDto.getPageNo(), pageDto.getPageSize());
        return Result.success(baseService.page(page, wrapper));
    }
}

Extending this class for a concrete entity reduces the controller to a single line:

@RestController
@RequestMapping("/product")
public class ProductController extends BaseController<ProductService, Product> { }

Underlying mechanism

SQL execution chain

The flow from controller invocation to database response is illustrated below:

SQL execution chain
SQL execution chain

BaseMapper injection mechanism

Spring creates a JDK dynamic proxy for every interface extending BaseMapper. Calls are intercepted by MapperProxy, which determines the intended operation.

MyBatis‑Plus’s SqlInjector generates MappedStatement objects for all generic methods during application startup. MybatisPlusInterceptor builds an interceptor chain that injects pagination, optimistic lock, multi‑tenant, and other enhancements before executor execution.

The method‑resolution logic distinguishes generic from custom methods and locates the appropriate MappedStatement for execution.

Wrapper construction process

When ApprenticeUtil.getQueryWrapper(entity) is called, the utility reflects over the entity, converts each non‑null field name from camelCase to snake_case, and adds an eq condition. The resulting QueryWrapper is passed to the service layer, producing a zero‑code conditional query.

Wrapper flow
Wrapper flow

Pros and Cons

Advantages

Development speed increases dramatically; a module can be functional with only a few lines of code, reducing effort by over 80 % compared with raw MyBatis.

Uniform code style lowers maintenance cost and onboarding friction.

No XML configuration is required for either mapper or controller layers.

Built‑in pagination, conditional queries, and other common features work out‑of‑the‑box.

Non‑intrusive design—original MyBatis can still be used for complex SQL.

Disadvantages

Complex multi‑table joins become cumbersome with Wrapper; raw SQL may be clearer.

Generic methods use SELECT *, which can cause performance issues on large tables.

String‑based field names in wrappers are prone to runtime errors; LambdaQueryWrapper is recommended for type safety.

Over‑encapsulation may limit flexibility for highly specialized business logic.

Usage scenarios and selection guidance

Recommended for small‑to‑medium projects, rapid‑iteration back‑office systems, and any application where CRUD dominates the workload.

Not advised for high‑performance transaction systems, complex reporting, or security‑sensitive environments that require hand‑crafted SQL.

Pitfall avoidance

Avoid overusing generic selectList with SELECT *; specify fields for performance‑critical queries.

Prefer LambdaQueryWrapper over string‑based wrappers for compile‑time safety.

Use MyBatis‑Plus’s built‑in pagination plugin instead of manual LIMIT clauses.

For queries involving three or more tables, write native SQL in mapper XML.

Follow camelCase naming for entity fields to ensure automatic mapping works correctly.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javacode generationSpring BootMyBatisMyBatis-PlusCRUD
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

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.