Skip to content
清晨的一缕阳光
返回

Spring Boot MyBatis-Plus 集成实战

前言

MyBatis-Plus 是在 MyBatis 基础上的增强工具,简化了 CRUD 操作,提供了强大的单表操作能力。本文将介绍 Spring Boot 集成 MyBatis-Plus 的完整用法。

快速开始

1. 添加依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.5</version>
</dependency>

2. 配置数据库

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 10
      minimum-idle: 5
      connection-timeout: 30000

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: auto
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

3. 创建实体类

package com.example.demo.entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;

@Data
@TableName("user")
public class User {
    
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String username;
    
    private String email;
    
    private Integer age;
    
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    
    @TableLogic
    private Integer deleted;
}

4. 创建 Mapper

package com.example.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 继承 BaseMapper,自动获得 CRUD 方法
}

5. 创建 Service

package com.example.demo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.User;

public interface UserService extends IService<User> {
    // 继承 IService,自动获得 CRUD 方法
}

6. 实现 Service

package com.example.demo.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    // 实现类,可使用所有 CRUD 方法
}

CRUD 操作

1. 新增

@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    @Override
    public UserDTO create(UserCreateDTO dto) {
        User user = new User();
        user.setUsername(dto.getUsername());
        user.setEmail(dto.getEmail());
        user.setAge(dto.getAge());
        
        // 插入
        boolean success = save(user);
        
        return convertToDTO(user);
    }
    
    /**
     * 批量插入
     */
    public boolean batchInsert(List<User> users) {
        return saveBatch(users, 100); // 每批 100 条
    }
}

2. 删除

@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    /**
     * 根据 ID 删除
     */
    public boolean deleteById(Long id) {
        return removeById(id);
    }
    
    /**
     * 批量删除
     */
    public boolean deleteByIds(List<Long> ids) {
        return removeByIds(ids);
    }
    
    /**
     * 条件删除
     */
    public boolean deleteByCondition(String email) {
        return remove(new LambdaQueryWrapper<User>()
            .eq(User::getEmail, email));
    }
}

3. 修改

@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    /**
     * 根据 ID 更新
     */
    public UserDTO update(Long id, UserUpdateDTO dto) {
        User user = getById(id);
        if (user == null) {
            throw BusinessException.notFound("用户");
        }
        
        user.setUsername(dto.getUsername());
        user.setEmail(dto.getEmail());
        
        boolean success = updateById(user);
        return convertToDTO(user);
    }
    
    /**
     * 根据条件更新
     */
    public boolean updateByCondition(UserUpdateDTO dto) {
        User user = new User();
        user.setAge(dto.getAge());
        
        return update(user, new LambdaQueryWrapper<User>()
            .eq(User::getAge, dto.getOldAge()));
    }
}

4. 查询

@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    /**
     * 根据 ID 查询
     */
    public UserDTO findById(Long id) {
        User user = getById(id);
        return convertToDTO(user);
    }
    
    /**
     * 查询列表
     */
    public List<UserDTO> findAll() {
        return list().stream()
            .map(this::convertToDTO)
            .collect(Collectors.toList());
    }
    
    /**
     * 条件查询
     */
    public List<UserDTO> findByCondition(String username, Integer minAge) {
        return list(new LambdaQueryWrapper<User>()
            .like(StringUtils.isNotBlank(username), User::getUsername, username)
            .ge(minAge != null, User::getAge, minAge));
    }
}

分页查询

1. 配置分页插件

@Configuration
public class MybatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

2. 分页查询

@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    /**
     * 简单分页
     */
    public Page<UserDTO> pageQuery(int page, int size) {
        Page<User> userPage = new Page<>(page, size);
        Page<User> result = page(userPage);
        
        return convertToDTOPage(result);
    }
    
    /**
     * 条件分页
     */
    public Page<UserDTO> pageQueryCondition(PageQueryDTO query) {
        Page<User> userPage = new Page<>(query.getPage(), query.getSize());
        
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
            .like(StringUtils.isNotBlank(query.getUsername()), User::getUsername, query.getUsername())
            .ge(query.getMinAge() != null, User::getAge, query.getMinAge())
            .le(query.getMaxAge() != null, User::getAge, query.getMaxAge())
            .orderByDesc(User::getCreateTime);
        
        Page<User> result = page(userPage, wrapper);
        return convertToDTOPage(result);
    }
    
    /**
     * 自定义分页
     */
    public Page<UserDTO> pageCustom(PageQueryDTO query) {
        Page<UserDTO> page = new Page<>(query.getPage(), query.getSize());
        return baseMapper.selectPageCustom(page, query);
    }
}

3. Mapper 自定义分页

@Mapper
public interface UserMapper extends BaseMapper<User> {
    
    @Select("SELECT * FROM user ${ew.customSqlSegment}")
    Page<UserDTO> selectPageCustom(Page<UserDTO> page, @Param("ew") Wrapper<User> wrapper);
}

常用插件

1. 乐观锁插件

@Configuration
public class MybatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}
@Data
@TableName("user")
public class User {
    
    @TableId
    private Long id;
    
    private String username;
    
    @Version
    private Integer version; // 乐观锁版本号
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    public boolean updateWithOptimisticLock(User user) {
        // 自动比较版本号
        return updateById(user);
    }
}

2. 数据权限插件

@Configuration
public class MybatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 数据权限插件
        interceptor.addInnerInterceptor(new DataPermissionInnerInterceptor(
            (sql, mappedStatementId, parameter, boundSql) -> {
                // 添加数据权限过滤
                return sql + " AND dept_id = 1";
            }
        ));
        return interceptor;
    }
}

3. 动态表名插件

@Configuration
public class MybatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        
        DynamicTableNameInnerInterceptor dynamicTableName = new DynamicTableNameInnerInterceptor();
        dynamicTableName.setTableNameHandler((sql, tableName) -> {
            if ("user".equals(tableName)) {
                String year = "_2024"; // 动态获取
                return tableName + year;
            }
            return tableName;
        });
        
        interceptor.addInnerInterceptor(dynamicTableName);
        return interceptor;
    }
}

代码生成器

1. 添加依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.5</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
</dependency>

2. 配置生成器

public class CodeGenerator {
    
    public static void main(String[] args) {
        FastAutoGenerator.create(
            "jdbc:mysql://localhost:3306/demo",
            "root",
            "123456"
        )
        .globalConfig(builder -> {
            builder.author("zhangsan")
                .outputDir("src/main/java")
                .disableOpenDir()
                .commentDate("yyyy-MM-dd");
        })
        .packageConfig(builder -> {
            builder.parent("com.example.demo")
                .entity("entity")
                .mapper("mapper")
                .service("service")
                .serviceImpl("service.impl")
                .controller("controller");
        })
        .strategyConfig(builder -> {
            builder.addInclude("user", "order", "product")
                .entityBuilder()
                .enableLombok()
                .enableTableFieldAnnotation()
                .mapperBuilder()
                .enableMapperAnnotation()
                .serviceBuilder()
                .formatServiceFileName("%sService")
                .formatServiceImplFileName("%sServiceImpl")
                .controllerBuilder()
                .enableRestStyle();
        })
        .templateEngine(new FreemarkerTemplateEngine())
        .execute();
    }
}

最佳实践

1. 逻辑删除

@Data
@TableName("user")
public class User {
    
    @TableId
    private Long id;
    
    private String username;
    
    @TableLogic
    private Integer deleted; // 0-未删除,1-已删除
}
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

2. 自动填充

@Data
@TableName("user")
public class User {
    
    @TableId
    private Long id;
    
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
    }
    
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
    }
}

3. 防止全表更新

mybatis-plus:
  global-config:
    db-config:
      # 禁止全表更新
      enable-update-batch: false

总结

MyBatis-Plus 要点:

MyBatis-Plus 让单表操作变得简单高效。


分享这篇文章到:

上一篇文章
MySQL COUNT 优化实战
下一篇文章
Gin 框架核心原理