MyBatis-Plus从入门到精通

MyBatis-Plus从入门到精通

目录

  1. 简介
  2. 源码分析
  3. SpringBoot集成
  4. 逆向工程配置
  5. 面试高频点
  6. 最佳实践
  7. 总结

简介

什么是MyBatis-Plus

MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。

核心特性

特性说明
无侵入只做增强不做改变,引入它不会对现有工程产生影响
CRUD操作内置通用Mapper、通用Service,可以无需编写SQL即可实现单表CRUD操作
条件构造器使用Lambda表达式,可以方便地编写各类查询条件
主键策略支持多达4种主键策略(内含分布式唯一ID生成器)
分页插件基于MyBatis物理分页,开发者无需关心具体操作
性能分析可输出SQL语句以及其执行时间,建议开发测试时启用该功能
代码生成器可快速生成Entity、Mapper、Mapper XML、Service、Controller等各个模块的代码

版本对比

版本特性兼容性
3.4.x基础功能完善,稳定版本MyBatis 3.4+
3.5.x新增Lambda查询,性能优化MyBatis 3.5+
3.6.x最新特性,支持JDK17+MyBatis 3.5+

源码分析

1. 核心架构图

MyBatis-Plus
核心模块
扩展模块
工具模块
BaseMapper
IService
AbstractMethod
分页插件
代码生成器
性能分析插件
条件构造器
主键生成器
工具类
SQL执行
MyBatis

2. BaseMapper源码分析

// BaseMapper接口定义
public interface BaseMapper<T> extends Mapper<T> {
  
    // 插入一条记录
    int insert(T entity);
  
    // 根据 ID 删除
    int deleteById(Serializable id);
  
    // 根据 ID 修改
    int updateById(@Param("et") T entity);
  
    // 根据 ID 查询
    T selectById(Serializable id);
  
    // 查询所有记录
    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
  
    // 根据 Wrapper 条件,查询总记录数
    Long selectCount(@Param("ew") Wrapper<T> queryWrapper);
  
    // 根据 Wrapper 条件,查询全部记录
    List<T> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
  
    // 根据 Wrapper 条件,查询全部记录
    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
  
    // 根据 Wrapper 条件,查询全部记录(并翻页)
    IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
  
    // 根据 Wrapper 条件,查询全部记录(并翻页)
    IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
}

3. AbstractMethod源码分析

// AbstractMethod抽象类
public abstract class AbstractMethod implements Method {
  
    protected Configuration configuration;
    protected MethodSignature ms;
    protected Class<?> mapperClass;
    protected Class<?> modelClass;
  
    @Override
    public Object execute(Object[] args, SqlSession sqlSession) {
        // 获取SQL语句
        String sql = getSql();
        // 获取参数
        Object param = getParam(args);
        // 执行SQL
        return execute(sql, param, sqlSession);
    }
  
    // 抽象方法,由子类实现
    protected abstract String getSql();
  
    // 获取参数
    protected Object getParam(Object[] args) {
        return args[0];
    }
  
    // 执行SQL
    protected Object execute(String sql, Object param, SqlSession sqlSession) {
        // 具体实现逻辑
        return null;
    }
}

// SelectById方法实现
public class SelectById extends AbstractMethod {
  
    @Override
    protected String getSql() {
        return String.format("SELECT %s FROM %s WHERE %s = #{id}",
            getColumns(), getTableName(), getPrimaryKey());
    }
  
    @Override
    protected Object execute(String sql, Object param, SqlSession sqlSession) {
        return sqlSession.selectOne(sql, param);
    }
}

4. 条件构造器源码分析

// AbstractWrapper抽象类
public abstract class AbstractWrapper<T, R, Children> 
    extends Wrapper<T> implements Compare<Children, R>, Nested<Children, Children>, Func<Children, R> {
  
    // 查询字段
    protected final List<String> sqlSelect = new ArrayList<>();
    // 查询条件
    protected final List<Object> expression = new ArrayList<>();
    // 排序字段
    protected final List<OrderBy> orderBy = new ArrayList<>();
    // 分组字段
    protected final List<String> groupBy = new ArrayList<>();
  
    // 添加查询条件
    protected Children addCondition(String column, Object val, String type) {
        expression.add(new Condition(column, val, type));
        return typedThis();
    }
  
    // 构建SQL
    public String getSqlSegment() {
        StringBuilder sql = new StringBuilder();
      
        // 添加WHERE条件
        if (!expression.isEmpty()) {
            sql.append(" WHERE ");
            for (int i = 0; i < expression.size(); i++) {
                if (i > 0) sql.append(" AND ");
                sql.append(expression.get(i).getSqlSegment());
            }
        }
      
        // 添加GROUP BY
        if (!groupBy.isEmpty()) {
            sql.append(" GROUP BY ").append(String.join(", ", groupBy));
        }
      
        // 添加ORDER BY
        if (!orderBy.isEmpty()) {
            sql.append(" ORDER BY ");
            for (int i = 0; i < orderBy.size(); i++) {
                if (i > 0) sql.append(", ");
                sql.append(orderBy.get(i).getSqlSegment());
            }
        }
      
        return sql.toString();
    }
}

// QueryWrapper实现类
public class QueryWrapper<T> extends AbstractWrapper<T, String, QueryWrapper<T>> {
  
    // 等值查询
    public QueryWrapper<T> eq(String column, Object val) {
        return addCondition(column, val, "=");
    }
  
    // 模糊查询
    public QueryWrapper<T> like(String column, Object val) {
        return addCondition(column, val, "LIKE");
    }
  
    // 范围查询
    public QueryWrapper<T> between(String column, Object val1, Object val2) {
        return addCondition(column, Arrays.asList(val1, val2), "BETWEEN");
    }
}

5. 分页插件源码分析

// PaginationInnerInterceptor分页插件
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PaginationInnerInterceptor implements Interceptor {
  
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
      
        // 获取分页参数
        IPage<?> page = getPage(metaObject);
        if (page == null) {
            return invocation.proceed();
        }
      
        // 获取原始SQL
        String originalSql = getOriginalSql(metaObject);
      
        // 构建分页SQL
        String pageSql = buildPageSql(originalSql, page);
      
        // 设置分页SQL
        metaObject.setValue("delegate.boundSql.sql", pageSql);
      
        return invocation.proceed();
    }
  
    // 构建分页SQL
    protected String buildPageSql(String originalSql, IPage<?> page) {
        if (page.getCurrent() <= 1) {
            // 第一页
            return originalSql + " LIMIT " + page.getSize();
        } else {
            // 其他页
            long offset = (page.getCurrent() - 1) * page.getSize();
            return originalSql + " LIMIT " + offset + "," + page.getSize();
        }
    }
}

SpringBoot集成

1. 依赖配置

<!-- Maven依赖 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3</version>
</dependency>

<!-- 代码生成器 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.3</version>
</dependency>

<!-- 模板引擎 -->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>

2. 配置文件

# application.yml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 123456

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: false
    call-setters-on-nulls: true
    jdbc-type-for-null: 'null'
  global-config:
    db-config:
      id-type: auto
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0
  mapper-locations: classpath*:/mapper/**/*.xml
  type-aliases-package: com.example.entity

3. 配置类

@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {
  
    /**
     * 分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
  
    /**
     * 乐观锁插件
     */
    @Bean
    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
        return new OptimisticLockerInnerInterceptor();
    }
  
    /**
     * 动态表名插件
     */
    @Bean
    public DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {
        DynamicTableNameInnerInterceptor interceptor = new DynamicTableNameInnerInterceptor();
        interceptor.setTableNameHandler((sql, tableName) -> {
            // 动态表名逻辑
            return tableName + "_" + LocalDate.now().getYear();
        });
        return interceptor;
    }
}

4. 实体类

@Data
@TableName("user")
public class User {
  
    @TableId(type = IdType.AUTO)
    private Long id;
  
    @TableField("user_name")
    private String userName;
  
    @TableField("email")
    private String email;
  
    @TableField("age")
    private Integer age;
  
    @TableField("create_time")
    private LocalDateTime createTime;
  
    @TableField("update_time")
    private LocalDateTime updateTime;
  
    @TableLogic
    @TableField("deleted")
    private Integer deleted;
  
    @Version
    @TableField("version")
    private Integer version;
}

5. Mapper接口

@Mapper
public interface UserMapper extends BaseMapper<User> {
  
    // 自定义查询方法
    @Select("SELECT * FROM user WHERE age > #{age}")
    List<User> selectUsersByAge(@Param("age") Integer age);
  
    // 自定义更新方法
    @Update("UPDATE user SET email = #{email} WHERE id = #{id}")
    int updateEmailById(@Param("id") Long id, @Param("email") String email);
  
    // 复杂查询
    List<User> selectUsersByCondition(@Param("userName") String userName, 
                                    @Param("minAge") Integer minAge, 
                                    @Param("maxAge") Integer maxAge);
}

6. Service接口和实现

// Service接口
public interface UserService extends IService<User> {
  
    // 自定义业务方法
    List<User> getUsersByAgeRange(Integer minAge, Integer maxAge);
  
    // 批量操作
    boolean batchUpdateUsers(List<User> users);
  
    // 分页查询
    IPage<User> getUserPage(IPage<User> page, String userName);
}

// Service实现
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
  
    @Override
    public List<User> getUsersByAgeRange(Integer minAge, Integer maxAge) {
        return this.list(new QueryWrapper<User>()
            .between("age", minAge, maxAge)
            .orderByDesc("create_time"));
    }
  
    @Override
    public boolean batchUpdateUsers(List<User> users) {
        return this.updateBatchById(users);
    }
  
    @Override
    public IPage<User> getUserPage(IPage<User> page, String userName) {
        return this.page(page, new QueryWrapper<User>()
            .like(StringUtils.isNotBlank(userName), "user_name", userName)
            .orderByDesc("create_time"));
    }
}

7. Controller

@RestController
@RequestMapping("/user")
public class UserController {
  
    @Autowired
    private UserService userService;
  
    /**
     * 分页查询用户
     */
    @GetMapping("/page")
    public Result<IPage<User>> getUserPage(
            @RequestParam(defaultValue = "1") Integer current,
            @RequestParam(defaultValue = "10") Integer size,
            @RequestParam(required = false) String userName) {
      
        IPage<User> page = new Page<>(current, size);
        IPage<User> result = userService.getUserPage(page, userName);
        return Result.success(result);
    }
  
    /**
     * 新增用户
     */
    @PostMapping
    public Result<Boolean> addUser(@RequestBody User user) {
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        boolean result = userService.save(user);
        return Result.success(result);
    }
  
    /**
     * 更新用户
     */
    @PutMapping
    public Result<Boolean> updateUser(@RequestBody User user) {
        user.setUpdateTime(LocalDateTime.now());
        boolean result = userService.updateById(user);
        return Result.success(result);
    }
  
    /**
     * 删除用户
     */
    @DeleteMapping("/{id}")
    public Result<Boolean> deleteUser(@PathVariable Long id) {
        boolean result = userService.removeById(id);
        return Result.success(result);
    }
}

逆向工程配置

1. 代码生成器配置

@Configuration
public class CodeGeneratorConfig {
  
    @Autowired
    private DataSource dataSource;
  
    /**
     * 代码生成器
     */
    public void generateCode() {
        // 数据源配置
        DataSourceConfig.Builder dataSourceConfigBuilder = new DataSourceConfig
            .Builder("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8", "root", "123456");
      
        // 全局配置
        GlobalConfig.Builder globalConfigBuilder = new GlobalConfig.Builder()
            .author("作者名")
            .dateType(DateType.TIME_PACK)
            .commentDate("yyyy-MM-dd")
            .outputDir(System.getProperty("user.dir") + "/src/main/java")
            .disableOpenDir();
      
        // 包配置
        PackageConfig.Builder packageConfigBuilder = new PackageConfig.Builder()
            .parent("com.example")
            .entity("entity")
            .mapper("mapper")
            .xml("mapper.xml")
            .service("service")
            .serviceImpl("service.impl")
            .controller("controller");
      
        // 策略配置
        StrategyConfig.Builder strategyConfigBuilder = new StrategyConfig.Builder()
            .addInclude("user", "order", "product") // 表名
            .addTablePrefix("t_") // 表前缀
            .entityBuilder()
                .enableLombok()
                .enableTableFieldAnnotation()
                .logicDeleteColumnName("deleted")
                .versionColumnName("version")
                .naming(NamingStrategy.underline_to_camel)
            .mapperBuilder()
                .enableMapperAnnotation()
                .enableBaseResultMap()
                .enableBaseColumnList()
            .serviceBuilder()
                .formatServiceFileName("%sService")
                .formatServiceImplFileName("%sServiceImpl")
            .controllerBuilder()
                .enableRestStyle()
                .enableHyphenStyle();
      
        // 模板配置
        TemplateConfig.Builder templateConfigBuilder = new TemplateConfig.Builder()
            .entity("/templates/entity.java")
            .mapper("/templates/mapper.java")
            .xml("/templates/mapper.xml")
            .service("/templates/service.java")
            .serviceImpl("/templates/serviceImpl.java")
            .controller("/templates/controller.java");
      
        // 执行生成
        FastAutoGenerator.create(dataSourceConfigBuilder)
            .globalConfig(globalConfigBuilder.build())
            .packageConfig(packageConfigBuilder.build())
            .strategyConfig(strategyConfigBuilder.build())
            .templateConfig(templateConfigBuilder.build())
            .execute();
    }
}

2. 自定义模板

// 自定义Entity模板
public class CustomEntityTemplate extends AbstractTemplate {
  
    @Override
    public String getTemplatePath() {
        return "/templates/entity.java.ftl";
    }
  
    @Override
    public String getFileName(String tableName) {
        return String.format("%s.java", tableName);
    }
}

// 自定义Mapper模板
public class CustomMapperTemplate extends AbstractTemplate {
  
    @Override
    public String getTemplatePath() {
        return "/templates/mapper.java.ftl";
    }
  
    @Override
    public String getFileName(String tableName) {
        return String.format("%sMapper.java", tableName);
    }
}

3. 生成器使用示例

@Component
public class CodeGenerator {
  
    public void generate() {
        // 数据库连接配置
        String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8";
        String username = "root";
        String password = "123456";
      
        // 代码生成器
        FastAutoGenerator.create(url, username, password)
            // 全局配置
            .globalConfig(builder -> {
                builder.author("作者名") // 设置作者
                    .enableSwagger() // 开启swagger模式
                    .fileOverride() // 覆盖已生成文件
                    .outputDir("D://"); // 指定输出目录
            })
            // 包配置
            .packageConfig(builder -> {
                builder.parent("com.example") // 设置父包名
                    .moduleName("system") // 设置模块名
                    .entity("entity") // 设置entity包名
                    .mapper("mapper") // 设置mapper包名
                    .xml("mapper.xml") // 设置mapper xml包名
                    .service("service") // 设置service包名
                    .serviceImpl("service.impl") // 设置service impl包名
                    .controller("controller"); // 设置controller包名
            })
            // 策略配置
            .strategyConfig(builder -> {
                builder.addInclude("user") // 设置需要生成的表名
                    .addTablePrefix("t_") // 设置过滤表前缀
                    // Entity策略配置
                    .entityBuilder()
                        .enableLombok() // 开启lombok
                        .enableTableFieldAnnotation() // 开启生成实体时生成字段注解
                        .logicDeleteColumnName("deleted") // 逻辑删除字段名
                        .versionColumnName("version") // 乐观锁字段名
                        .naming(NamingStrategy.underline_to_camel) // 命名策略
                    // Mapper策略配置
                    .mapperBuilder()
                        .enableMapperAnnotation() // 开启@Mapper注解
                        .enableBaseResultMap() // 启用BaseResultMap生成
                        .enableBaseColumnList() // 启用BaseColumnList生成
                    // Service策略配置
                    .serviceBuilder()
                        .formatServiceFileName("%sService") // 格式化service接口文件名称
                        .formatServiceImplFileName("%sServiceImpl") // 格式化service实现类文件名称
                    // Controller策略配置
                    .controllerBuilder()
                        .enableRestStyle() // 开启生成@RestController
                        .enableHyphenStyle(); // 开启驼峰转连字符
            })
            // 模板配置
            .templateConfig(builder -> {
                builder.entity("/templates/entity.java")
                    .mapper("/templates/mapper.java")
                    .xml("/templates/mapper.xml")
                    .service("/templates/service.java")
                    .serviceImpl("/templates/serviceImpl.java")
                    .controller("/templates/controller.java");
            })
            // 执行
            .execute();
    }
}

面试高频点

1. 基础概念

Q1: MyBatis-Plus和MyBatis的区别?

A:

  • MyBatis: 需要手写SQL,提供基础的ORM功能
  • MyBatis-Plus: 在MyBatis基础上增强,提供通用CRUD操作,减少重复代码
  • 关系: MyBatis-Plus是MyBatis的增强工具,完全兼容MyBatis
Q2: MyBatis-Plus的核心特性有哪些?

A:

  • 无侵入: 只做增强不做改变
  • CRUD操作: 内置通用Mapper、通用Service
  • 条件构造器: 使用Lambda表达式编写查询条件
  • 主键策略: 支持多种主键生成策略
  • 分页插件: 基于MyBatis物理分页
  • 代码生成器: 快速生成各模块代码
Q3: MyBatis-Plus支持哪些主键策略?

A:

public enum IdType {
    AUTO,           // 数据库自增
    NONE,           // 无状态
    INPUT,          // 用户输入
    ASSIGN_ID,      // 分配ID (默认雪花算法)
    ASSIGN_UUID,    // 分配UUID
    /** @deprecated */
    @Deprecated
    ID_WORKER,      // 全局唯一ID (idWorker)
    /** @deprecated */
    @Deprecated
    ID_WORKER_STR,  // 字符串全局唯一ID (idWorker 的字符串表示)
    /** @deprecated */
    @Deprecated
    UUID            // 全局唯一ID (UUID)
}

2. 条件构造器

Q4: QueryWrapper和LambdaQueryWrapper的区别?

A:

  • QueryWrapper: 使用字符串列名,类型不安全,但灵活性高
  • LambdaQueryWrapper: 使用Lambda表达式,类型安全,编译时检查,推荐使用
// QueryWrapper示例
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_name", "张三")
    .ge("age", 18)
    .like("email", "@gmail.com");

// LambdaQueryWrapper示例
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getUserName, "张三")
    .ge(User::getAge, 18)
    .like(User::getEmail, "@gmail.com");
Q5: 如何实现动态查询条件?

A:

public List<User> getUsers(String userName, Integer minAge, Integer maxAge) {
    return this.list(new LambdaQueryWrapper<User>()
        .like(StringUtils.isNotBlank(userName), User::getUserName, userName)
        .ge(minAge != null, User::getAge, minAge)
        .le(maxAge != null, User::getAge, maxAge)
        .orderByDesc(User::getCreateTime));
}

3. 分页插件

Q6: MyBatis-Plus分页插件的原理是什么?

A:

  • 原理: 基于MyBatis的Interceptor拦截器,在SQL执行前动态修改SQL语句
  • 流程: 拦截StatementHandler.prepare方法 → 获取分页参数 → 构建分页SQL → 替换原始SQL
  • 特点: 物理分页,性能好,支持多种数据库
Q7: 如何配置分页插件?

A:

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

4. 性能优化

Q8: MyBatis-Plus有哪些性能优化点?

A:

  • 批量操作: 使用saveBatch、updateBatchById等方法
  • 分页查询: 合理设置分页大小,避免深度分页
  • 条件构造: 使用Lambda表达式,避免字符串拼接
  • 缓存策略: 合理使用MyBatis缓存
  • SQL优化: 避免N+1查询问题
Q9: 如何处理N+1查询问题?

A:

// 错误示例:N+1查询
List<User> users = userService.list();
for (User user : users) {
    List<Order> orders = orderService.list(new LambdaQueryWrapper<Order>()
        .eq(Order::getUserId, user.getId()));
    user.setOrders(orders);
}

// 正确示例:批量查询
List<User> users = userService.list();
List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
List<Order> allOrders = orderService.list(new LambdaQueryWrapper<Order>()
    .in(Order::getUserId, userIds));

// 按用户ID分组
Map<Long, List<Order>> orderMap = allOrders.stream()
    .collect(Collectors.groupingBy(Order::getUserId));

// 设置用户订单
users.forEach(user -> user.setOrders(orderMap.get(user.getId())));

5. 高级特性

Q10: 如何实现逻辑删除?

A:

// 实体类配置
@TableLogic
@TableField("deleted")
private Integer deleted;

// 配置文件中设置
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

// 使用
userService.removeById(1L); // 实际执行UPDATE语句,设置deleted=1
userService.list(); // 查询时自动添加WHERE deleted=0条件
Q11: 如何实现乐观锁?

A:

// 实体类配置
@Version
@TableField("version")
private Integer version;

// 配置乐观锁插件
@Bean
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
    return new OptimisticLockerInnerInterceptor();
}

// 使用
User user = userService.getById(1L);
user.setUserName("新用户名");
userService.updateById(user); // 自动检查version字段,防止并发更新

最佳实践

1. 实体类设计

@Data
@TableName("user")
@EqualsAndHashCode(callSuper = false)
public class User extends BaseEntity {
  
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
  
    @TableField(value = "user_name", fill = FieldFill.INSERT)
    private String userName;
  
    @TableField("email")
    private String email;
  
    @TableField("age")
    private Integer age;
  
    @TableField(value = "create_time", fill = FieldFill.INSERT)
    private LocalDateTime createTime;
  
    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
  
    @TableLogic
    @TableField("deleted")
    private Integer deleted;
  
    @Version
    @TableField("version")
    private Integer version;
  
    // 不映射到数据库的字段
    @TableField(exist = false)
    private List<Order> orders;
}

2. 字段自动填充

@Component
public class MetaObjectHandler implements com.baomidou.mybatisplus.core.handlers.MetaObjectHandler {
  
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "deleted", Integer.class, 0);
        this.strictInsertFill(metaObject, "version", Integer.class, 1);
    }
  
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

3. 异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {
  
    @ExceptionHandler(MybatisPlusException.class)
    public Result<String> handleMybatisPlusException(MybatisPlusException e) {
        log.error("MyBatis-Plus异常: {}", e.getMessage(), e);
        return Result.error("数据库操作异常: " + e.getMessage());
    }
  
    @ExceptionHandler(DataIntegrityViolationException.class)
    public Result<String> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
        log.error("数据完整性异常: {}", e.getMessage(), e);
        return Result.error("数据完整性约束违反");
    }
}

4. 性能监控

@Component
public class SqlPerformanceInterceptor implements Interceptor {
  
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
      
        try {
            return invocation.proceed();
        } finally {
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
          
            if (duration > 1000) { // 超过1秒的SQL记录警告
                log.warn("SQL执行时间过长: {}ms", duration);
            }
          
            log.info("SQL执行时间: {}ms", duration);
        }
    }
}

总结

1. 学习路径图

MyBatis-Plus学习
基础阶段
进阶阶段
高级阶段
实战应用
基本CRUD操作
条件构造器
分页插件
代码生成器
逻辑删除
乐观锁
源码分析
性能优化
自定义插件
SpringBoot集成
项目实战
问题排查

2. 核心优势

优势说明
开发效率减少重复代码,提高开发速度
类型安全Lambda表达式,编译时检查
性能优化内置分页插件,批量操作
扩展性强插件机制,支持自定义扩展
学习成本低基于MyBatis,学习曲线平缓

3. 适用场景

场景说明
快速开发新项目快速搭建,减少重复工作
CRUD操作单表操作频繁的业务场景
分页查询需要分页展示的数据列表
代码生成标准化的增删改查功能

4. 注意事项

注意点说明
性能考虑避免N+1查询,合理使用批量操作
事务管理复杂业务逻辑需要手动管理事务
SQL优化复杂查询建议手写SQL
版本兼容注意MyBatis版本兼容性

5. 学习建议

通过系统学习MyBatis-Plus,可以:

  • 提高开发效率 - 减少重复代码编写
  • 增强代码质量 - 类型安全,减少错误
  • 掌握ORM框架 - 深入理解MyBatis原理
  • 提升技术能力 - 为高级开发打下基础

实践建议: 在实际项目中多应用MyBatis-Plus,通过实践加深理解,逐步掌握其高级特性和最佳实践。记住:工具是手段,理解原理才是根本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值