Home

MybatisPlus

Li

Li Wei

April 26, 202611 min read

MybatisPlus

Overview

Summary

MyBatis-Plus (opens new window) (abbreviated MP) is an enhancement tool that adds features on top of MyBatis without changing its core. It was created to simplify development and improve efficiency.

Official site:

Features

  • Non‑intrusive: only adds enhancements, does not alter existing code, integrates smoothly.
  • Low overhead: basic CRUD methods are auto‑injected at startup, with virtually no performance loss; you work directly with objects.
  • Powerful CRUD: includes a generic Mapper and Service; with minimal configuration you can handle most single‑table CRUD operations, plus a robust condition builder for various needs.
  • Lambda support: use lambda expressions to write query conditions safely, avoiding typo‑prone field names.
  • Automatic primary‑key generation: up to four strategies (including a distributed unique ID generator – Sequence) can be configured, solving primary‑key issues effortlessly.
  • ActiveRecord mode: entities that extend the Model class gain full CRUD capabilities.
  • Custom global operations: inject global methods once and use them anywhere.
  • Built‑in code generator: generate Mapper, Model, Service, and Controller code via code or Maven plugin; supports template engines and many custom options.
  • Built‑in pagination plugin: uses MyBatis physical pagination; after configuring the plugin, pagination queries are written like ordinary List queries.
  • Multi‑database pagination support: works with MySQL, MariaDB, Oracle, DB2, H2, HSQL, SQLite, PostgreSQL, SQL Server, etc.
  • Performance analysis plugin: logs SQL statements and execution times; recommended during development to spot slow queries.
  • Global interceptor plugin: intelligently blocks whole‑table delete/update operations and allows custom interception rules to prevent accidental data loss.

Architecture

Quick Start

  • Add the dependency: MybatisPlus provides a starter that auto‑configures MyBatis and MybatisPlus. Coordinates are:

    • Because this starter also auto‑configures MyBatis, it can completely replace the MyBatis starter.
  • Configuration file: create a Spring Boot configuration file and specify the database URL.

  • Create the Spring Boot main class

  • Create entity classes

  • Create a mapper that extends BaseMapper: to simplify single‑table CRUD, MybatisPlus offers a basic BaseMapper interface that already implements CRUD for a single table.

    Consequently, a custom mapper only needs to extend BaseMapper and specify the entity class as the generic type; you no longer have to write CRUD methods yourself. Example code:

    • Test: create a test class and write a few unit tests to verify basic CRUD functionality.

    • You’ll see standard SQL logs printed during execution:

Core Features

Common Annotations

Overview

MybatisPlus infers table information from the PO entity type declared in a Mapper’s generic parameter, then generates the corresponding SQL.

By default:

  • The PO class name is converted from camelCase to snake_case to become the table name.
  • All PO field names are converted from camelCase to snake_case to become column names, and the field type is used to infer the column type.
  • A field named id is treated as the primary key.

@TableName

Description: table‑name annotation that maps an entity class to a database table.
Target: entity class.

Example:

Besides specifying the table name, @TableName can set many other attributes:

Attribute Type Required Default Description
value String No "" Table name
schema String No "" Schema name
keepGlobalPrefix boolean No false Keep the global tablePrefix when it is active
resultMap String No "" ID of the resultMap in XML (for special entity bindings)
autoResultMap boolean No false Auto‑generate and use a resultMap (disabled if resultMap is set)
excludeProperty String[] No {} Property names to exclude (since 3.3.1)

@TableId

Description: primary‑key annotation that marks the primary‑key field in an entity.
Target: primary‑key field of an entity class.

Example:

@TableId supports two attributes:

Attribute Type Required Default Description
value String No "" Column name
type Enum No IdType.NONE Primary‑key generation strategy

IdType options:

Value Description
AUTO Database auto‑increment (e.g., auto_increment)
NONE No strategy; behaves as if not set (falls back to global or INPUT)
INPUT User sets the primary‑key value before insertion
ASSIGN_ID Assign a numeric ID (Long/Integer) or String (since 3.3.0) using the IdentifierGenerator (default is DefaultIdentifierGenerator – Snowflake algorithm)
ASSIGN_UUID Assign a UUID string (since 3.3.0) using IdentifierGenerator.nextUUID
ID_WORKER Distributed global unique long ID (use ASSIGN_ID instead)
UUID 32‑character UUID string (use ASSIGN_UUID instead)
ID_WORKER_STR Distributed global unique ID as a string (use ASSIGN_ID instead)

Commonly used strategies:

  • AUTO: use the database’s auto‑increment ID
  • INPUT: manually generate the ID
  • ASSIGN_ID: generate a globally unique Long using the Snowflake algorithm (the default strategy)

@TableField

Description: ordinary field annotation.

Example:

Usually you don’t need to add @TableField annotation to a field, except in special cases:

  • The Java field name differs from the column name.
  • The field follows the isXXX convention; MybatisPlus strips the leading is, which may cause mismatches.
  • The field name collides with a SQL keyword. Use @TableField to add escape characters: ````

Supported additional attributes:

Attribute Type Required Default Description
value String No "" Column name
exist boolean No true Whether the column exists in the table
condition String No "" Custom where clause; if empty, defaults to global %s=#{%s} (see reference)
update String No "" Custom SET fragment, e.g., update="%s+1" results in SET version=version+1 (higher priority than el)
insertStrategy Enum No FieldStrategy.DEFAULT Example: NOT_NULLINSERT INTO table_a (column) VALUES (#{columnProperty})
updateStrategy Enum No FieldStrategy.DEFAULT Example: IGNOREDUPDATE table_a SET column=#{columnProperty}
whereStrategy Enum No FieldStrategy.DEFAULT Example: NOT_EMPTYWHERE column=#{columnProperty}
fill Enum No FieldFill.DEFAULT Automatic field filling strategy
select boolean No true Include in SELECT queries
keepGlobalFormat boolean No false Keep global format processing
jdbcType JdbcType No JdbcType.UNDEFINED JDBC type (default does not enforce)
typeHandler TypeHandler No Custom type handler (default does not enforce)
numericScale String No "" Number of digits after the decimal point

Common Configuration

MybatisPlus also supports custom configuration via a YAML file; see the official docs:

https://www.baomidou.com/pages/56bac0/#%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AE

Most settings have defaults, so you often don’t need to specify them. Some items must be configured, such as:

  • Packages to scan for entity classes
  • Global ID type

Note that MybatisPlus also allows handwritten SQL. The location of mapper XML files can be customized:

The default location is classpath*:/mapper/**/*.xml, meaning any mapper.xml placed under that path will be automatically loaded.

Condition Builder

Overview

Beyond inserts, update, delete, and select statements all require WHERE conditions. BaseMapper provides methods that accept complex conditions, not just primary‑key based ones.

Wrapper is the abstract base for condition building. Many concrete implementations inherit from it (see diagram).

AbstractWrapper, a subclass of Wrapper, supplies all the methods for constructing WHERE clauses.

QueryWrapper extends AbstractWrapper with a select method to specify the fields to retrieve.

UpdateWrapper extends AbstractWrapper with a set method to define the SET part of an UPDATE statement.

QueryWrapper

Use QueryWrapper for building conditions for selects, updates, or deletes.

Example – Query: Find people whose name contains “o” and whose balance is ≥ 1000.

QueryWrapper<User> qw = new QueryWrapper<>();
qw.like("name", "o").ge("balance", 1000);
List<User> list = userMapper.selectList(qw);

Example – Update: Set the balance of the user whose username is “jack” to 2000.

UpdateWrapper<User> uw = new UpdateWrapper<>();
uw.eq("username", "jack").set("balance", 2000);
userMapper.update(null, uw);

UpdateWrapper

BaseMapper.update normally only allows direct value assignment, which is insufficient for complex needs.

Example: Decrease the balance of users with IDs 1, 2, 4 by 200.

UpdateWrapper<User> uw = new UpdateWrapper<>();
uw.in("id", 1, 2, 4).setSql("balance = balance - 200");
userMapper.update(null, uw);

LambdaWrapper

Writing field names as strings creates “magic values” that are error‑prone. To avoid hard‑coding field names, you can use method references or lambda expressions, which let MybatisPlus infer the field name via reflection.

MybatisPlus therefore provides Lambda‑based wrappers:

  • LambdaQueryWrapper
  • LambdaUpdateWrapper

These correspond to QueryWrapper and UpdateWrapper.

Usage example:

LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.like(User::getName, "o")
   .ge(User::getBalance, 1000);
List<User> list = userMapper.selectList(lqw);

Custom SQL

Overview

In the earlier UpdateWrapper example we wrote the SQL directly in Java. Some companies forbid this practice because SQL should reside in the persistence layer, not the service layer. For an IN condition, you would normally place the SQL in Mapper.xml and use <foreach> to generate dynamic SQL—tedious, especially for complex queries.

MybatisPlus offers a custom‑SQL feature: you can build conditions with a Wrapper, then write the actual SQL in the mapper XML, combining the two approaches.

Basic Usage

Continuing the previous example, you could write:

List<User> list = userMapper.selectList(
    new QueryWrapper<User>().in("id", ids)
);

and in UserMapper.xml define a custom statement that uses the generated condition.

Multi‑Table Joins

MybatisPlus does not natively support multi‑table queries, but you can achieve them by combining a custom Wrapper condition with hand‑written SQL.

Scenario: Find users whose shipping address is in Beijing and whose IDs are 1, 2, 4.

A pure MyBatis implementation might look like:

SELECT u.* FROM user u
JOIN address a ON u.id = a.user_id
WHERE a.city = 'Beijing' AND u.id IN (1,2,4);

The hardest part is the WHERE clause. With the custom‑SQL + Wrapper technique, you let the wrapper build the condition and hand‑write the SELECT/FROM part.

Build the condition:

QueryWrapper<User> qw = new QueryWrapper<>();
qw.in("u.id", Arrays.asList(1,2,4))
  .eq("a.city", "Beijing");

Mapper method:

List<User> selectUserWithAddress(@Param("ew") Wrapper<User> wrapper);

Corresponding XML:

<select id="selectUserWithAddress" resultType="User">
  SELECT u.* FROM user u
  JOIN address a ON u.id = a.user_id
  <where>
    ${ew.sqlSegment}
  </where>
</select>

IService Interface

Overview

MybatisPlus not only provides BaseMapper but also a generic Service interface and its default implementation, encapsulating common service‑layer patterns. The generic interface is IService; the default implementation is ServiceImpl. The methods can be grouped as:

  • save: create
  • remove: delete
  • update: update
  • get: fetch a single result
  • list: fetch a collection
  • count: count
  • page: paginate

CRUD

Typical CRUD methods include:

  • Create:

    • save – insert a single record
    • saveBatch – batch insert
    • saveOrUpdate – insert or update based on ID existence
    • saveOrUpdateBatch – batch insert or update
  • Delete:

    • removeById – delete by ID
    • removeByIds – batch delete by IDs
    • removeByMap – delete by a Map of column/value pairs
    • remove(Wrapper ) – delete by Wrapper condition
    • ~~removeBatchByIds~~ – (not supported)
  • Update:

    • updateById – update by ID
    • update(Wrapper ) – update using UpdateWrapper (contains SET and WHERE)
    • update(T,Wrapper ) – update records matching a Wrapper with values from the entity
    • updateBatchById – batch update by IDs
  • Get:

    • getById – fetch one record by ID
    • getOne(Wrapper ) – fetch one record by Wrapper
    • getBaseMapper – obtain the underlying BaseMapper from the service (useful for calling custom mapper SQL)
  • List:

    • listByIds – batch fetch by IDs
    • list(Wrapper ) – fetch multiple records by Wrapper condition
    • list() – fetch all records
  • Count:

    • count() – count all records
    • count(Wrapper ) – count records matching a Wrapper condition
  • getBaseMapper: When a service needs to call custom SQL defined in its mapper, retrieve the mapper via this method.

Basic Usage

Because services often need business‑specific methods, you typically define your own service interface that extends IService, then implement it by extending ServiceImpl. This way you inherit all generic methods without writing them yourself.

Interface hierarchy:

public interface UserService extends IService<User> {
    // custom business methods
}

Implementation:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {
    // custom method implementations
}

First, define IUserService extending IService:

public interface UserService extends IService<User> {
    // additional methods
}

Then create the UserServiceImpl class extending ServiceImpl:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {
    // custom logic
}

(content truncated)


Originally written by Li Wei (李唯_) and published in Chinese on 后端技术栈全书 (Full-Stack Backend Engineering). Translated and adapted for DriftSeas with permission.

Keep reading

More related articles from DriftSeas.