MyBatis Plus Practical Guide: Spring Boot CRUD, Pagination, Logical Delete, and Optimistic Locking

[AI Readability Summary]

MyBatis Plus extends MyBatis and helps you complete CRUD operations, conditional queries, and common data-layer governance with far less boilerplate code. It reduces repetitive work around handwritten Mapper interfaces, XML, and generic SQL. Keywords: MyBatis Plus, Spring Boot, LambdaQueryWrapper.

The technical specification snapshot outlines the stack at a glance

Parameter Description
Core Language Java
Runtime Framework Spring Boot
ORM Enhancement MyBatis Plus
Typical Protocols JDBC / SQL
Reference Version mybatis-plus-boot-starter 3.5.3.1
Core Dependencies BaseMapper, IService, MybatisPlusInterceptor
Source Article Profile Curated from a CSDN technical article
Star Count Not provided in the original content

MyBatis Plus delivers value by dramatically reducing repetitive data-layer code

MyBatis Plus preserves native MyBatis capabilities and focuses on enhancement rather than replacement. By extending BaseMapper and IService, developers can immediately gain common single-table CRUD capabilities and avoid writing large amounts of template-style SQL and XML.

It works especially well for internal business systems, admin platforms, and standard business-table development. For scenarios such as frequent create, read, update, and delete operations, conditional filtering, pagination, and audit field handling, MP shifts your focus from building plumbing to implementing business rules.

Minimal dependencies and configuration get you running quickly


<dependency>

<groupId>com.baomidou</groupId>

<artifactId>mybatis-plus-boot-starter</artifactId>

<version>3.5.3.1</version>
</dependency>

This dependency enables the core MyBatis Plus features in Spring Boot.

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis-plus?useSSL=false
    username: root
    password: 123456
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # Output SQL logs

This configuration sets up the database connection and enables SQL console logging for easier query debugging.

The combination of entity and Mapper forms the most basic data access unit

The entity class uses annotations to define table mapping. Primary key strategies, field existence, logical deletion, and optimistic locking are all declared at this layer. This approach centralizes rules and reduces scattered XML configuration.

@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO) // Auto-increment primary key strategy
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

This code defines the entity object that maps to the user table and declares the primary key generation strategy.

@Repository
public interface UserMapper extends BaseMapper
<User> {
    // Inherit BaseMapper to get common CRUD methods automatically
}

This interface allows UserMapper to directly support insert, delete, update, and primary-key lookup operations.

Basic CRUD operations can be called directly through the Mapper

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void testCrud() {
        User user = new User();
        user.setName("张三");
        user.setAge(30);
        user.setEmail("[email protected]");
        userMapper.insert(user); // Insert a user record

        User dbUser = userMapper.selectById(user.getId()); // Query by primary key
        System.out.println(dbUser);
    }
}

This test demonstrates the smallest working loop for insert and primary-key lookup without writing XML.

Wrapper is the most productive query abstraction in MyBatis Plus

QueryWrapper and UpdateWrapper let you express conditions without hand-writing WHERE clauses. Compared with string concatenation, they provide a more consistent and reusable approach. Common capabilities include eq, gt, like, between, and orderByDesc.

In most cases, you should prefer LambdaQueryWrapper. Because it references fields through method references, it avoids mistakes in string-based field names and improves refactoring safety.

Lambda condition builders are better for long-term maintenance

@Test
void testLambdaQuery() {
    LambdaQueryWrapper
<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.gt(User::getAge, 20)          // Age greater than 20
           .like(User::getName, "张")     // Name contains "张"
           .orderByDesc(User::getAge);    // Sort by age in descending order

    List
<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

This code demonstrates a type-safe conditional query style that fits medium and large projects over the long term.

@Test
void testUpdateWrapper() {
    UpdateWrapper
<User> wrapper = new UpdateWrapper<>();
    wrapper.gt("age", 25)                            // Update records where age is greater than 25
           .set("email", "[email protected]");    // Update the email field in bulk

    int rows = userMapper.update(null, wrapper);
    System.out.println("更新行数=" + rows);
}

This code shows how to build an update statement directly with conditions when no entity object is passed.

The Service layer abstraction further unifies the business access entry point

When a project grows into a multi-module collaboration environment, relying only on Mapper is often not enough. MyBatis Plus provides generic service-layer implementations through IService and ServiceImpl, giving business code a more consistent entry point.

public interface UserService extends IService
<User> {
    // Extend domain methods here if needed
}

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

This code establishes a standard Service structure that is suitable for reusing common methods between controllers and business services.

Controllers can directly reuse the generic capabilities of IService

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/users")
    public List
<User> list() {
        return userService.list(); // Query all users directly
    }
}

This controller code shows that the Service layer can directly expose standard CRUD capabilities.

Pagination, logical deletion, and auto-fill are the most commonly used engineering features

Pagination is essential for admin-side list queries. MyBatis Plus wires in PaginationInnerInterceptor through MybatisPlusInterceptor, allowing plugins to intercept the SQL execution chain and eliminate handwritten pagination SQL.

@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // Enable pagination plugin
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // Enable optimistic locking plugin
        return interceptor;
    }
}

This configuration enables pagination and optimistic locking in one place, which is a common infrastructure pattern in real projects.

@Test
void testPage() {
    Page
<User> page = new Page<>(1, 5); // Page 1, 5 records per page
    userMapper.selectPage(page, null);
    System.out.println(page.getRecords());
}

This code demonstrates the standard way to execute a paginated query.

Logical deletion and audit fields significantly reduce common business checks

Logical deletion uses a marker field instead of physically deleting records, which makes recovery, auditing, and history tracking easier. Auto-fill handles fields such as createTime and updateTime consistently, so you do not have to manage them manually in every endpoint.

public class User {
    @TableLogic
    private Integer deleted; // 0 = not deleted, 1 = deleted

    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

    @Version
    private Integer version; // Optimistic lock version number
}

This entity definition centralizes logical deletion, audit fields, and optimistic locking.

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", Date.class, new Date()); // Fill create time on insert
        this.strictInsertFill(metaObject, "updateTime", Date.class, new Date()); // Fill update time on insert
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date()); // Refresh update time on update
    }
}

This handler automatically maintains audit fields during insert and update operations.

The code generator is ideal for improving efficiency at scale in standardized projects

For systems with stable table structures and many modules, the code generator can batch-generate Entity, Mapper, Service, and Controller classes. It is not a core runtime capability, but it is highly efficient during project bootstrapping.

FastAutoGenerator.create("jdbc:mysql://localhost:3306/your_db", "username", "password")
    .globalConfig(builder -> builder.outputDir("D://code")) // Specify the output directory
    .packageConfig(builder -> builder.parent("com.example")) // Specify the base package
    .strategyConfig(builder -> builder.addInclude("user", "order")) // Specify the tables to generate
    .execute();

This code quickly generates a standard layered code skeleton from the database schema.

The conclusion is that MyBatis Plus fits most standard CRUD systems

If your project mainly involves single-table operations, paginated lists, back-office management, audit fields, and a generic service layer, MyBatis Plus is one of the most cost-effective choices for Java back-end development. Its greatest strength is not that it writes all SQL for you, but that it minimizes repetitive work.

For complex joins, extreme performance tuning, or highly specialized SQL scenarios, you can still fall back to native MyBatis. That is exactly why MP works best as the default option, not the only option.

The FAQ clarifies common implementation questions

1. Can MyBatis Plus completely replace MyBatis?

No. It is an enhancement layer that works well for standard CRUD and general-purpose scenarios. For complex joins and manually optimized SQL, you should still combine it with native MyBatis.

2. Why is LambdaQueryWrapper the preferred option?

Because it binds fields through method references instead of hard-coded string field names, making it safer for refactoring, field changes, and compile-time validation.

3. Should logical deletion, auto-fill, and optimistic locking be used together?

Yes, in most internal business systems. These three features address data retention, audit consistency, and concurrency safety respectively. Together, they provide a more production-ready data governance model.

Core summary

This article systematically reconstructs the core capabilities and best practices of MyBatis Plus, including rapid integration, BaseMapper and IService, Wrapper-based condition builders, the pagination plugin, logical deletion, auto-fill, optimistic locking, and the code generator. It helps Java back-end developers build a high-quality data access layer with less code.