能省事”。SpringBoot+MyBatis-Plus:开发效率提升10倍!

《能省事"。SpringBoot+MyBatis-Plus:开发效率提升10倍!》

我是小坏,今天咱们聊点实在的。你还在手写增删改查吗?还在写那些重复的Mapper XML吗?还在为分页头疼吗?试试MyBatis-Plus,让你少写80%的代码!

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

一、开发者的真实痛点

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

看看这些场景你熟不熟

场景1:新项目开始,建表、建实体、建Mapper、建Service、建Controller...一套下来几十个文件。

场景2:写个条件查询,XML里写一堆if标签:

xml 复制代码
<select id="findUsers" resultType="User">
    SELECT * FROM user WHERE 1=1
    <if test="name != null">AND name like #{name}</if>
    <if test="age != null">AND age = #{age}</if>
    <if test="status != null">AND status = #{status}</if>
</select>

场景3:分页查询,每写一次都要:

java 复制代码
// 手写分页SQL
String sql = "SELECT * FROM user LIMIT " + (page-1)*size + "," + size;
// 再查总数
String countSql = "SELECT COUNT(*) FROM user";

用MyBatis-Plus能解决啥

  • 自动生成基础代码
  • 条件查询不用写XML
  • 分页一行代码搞定
  • 内置逻辑删除、字段填充
  • 支持多租户数据隔离

二、3分钟快速上手

2.1 加个依赖

xml 复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

2.2 写个配置

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

yaml 复制代码
# application.yml
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 打印SQL日志
  global-config:
    db-config:
      logic-delete-field: deleted  # 全局逻辑删除字段名
      logic-delete-value: 1        # 逻辑已删除值
      logic-not-delete-value: 0    # 逻辑未删除值

2.3 定义实体

java 复制代码
@Data
@TableName("user")  // 指定表名
public class User {
    @TableId(type = IdType.AUTO)  // 主键自增
    private Long id;
    
    private String name;
    private Integer age;
    private String email;
    
    @TableLogic  // 逻辑删除字段
    private Integer deleted;
    
    @TableField(fill = FieldFill.INSERT)  // 插入时自动填充
    private LocalDateTime createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)  // 插入和更新时填充
    private LocalDateTime updateTime;
}

2.4 写个Mapper(最简单版)

java 复制代码
@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 继承BaseMapper就有了所有基础CRUD方法
    // 不用写XML,不用写SQL
}

2.5 马上就能用

java 复制代码
@RestController
@RequestMapping("/users")
public class UserController {
    
    @Autowired
    private UserMapper userMapper;
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        // 直接查询
        return userMapper.selectById(id);
    }
    
    @PostMapping
    public String addUser(@RequestBody User user) {
        // 直接插入
        userMapper.insert(user);
        return "添加成功";
    }
}

三、核心功能:解放生产力

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

3.1 代码生成器(效率提升80%)

以前 :手动创建实体、Mapper、Service、Controller 现在:一键生成所有文件

java 复制代码
public class CodeGenerator {
    public static void main(String[] args) {
        AutoGenerator generator = new AutoGenerator();
        
        // 数据库配置
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/test");
        dataSourceConfig.setUsername("root");
        dataSourceConfig.setPassword("123456");
        generator.setDataSource(dataSourceConfig);
        
        // 全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
        globalConfig.setAuthor("小坏");
        globalConfig.setOpen(false);  // 生成后不打开文件夹
        generator.setGlobalConfig(globalConfig);
        
        // 包配置
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setParent("com.example.demo");
        packageConfig.setEntity("entity");
        packageConfig.setMapper("mapper");
        packageConfig.setService("service");
        packageConfig.setController("controller");
        generator.setPackageInfo(packageConfig);
        
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("user", "order", "product");  // 要生成的表
        strategy.setNaming(NamingStrategy.underline_to_camel);  // 下划线转驼峰
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);  // 使用Lombok
        strategy.setRestControllerStyle(true);  // REST风格Controller
        generator.setStrategy(strategy);
        
        // 执行生成
        generator.execute();
    }
}

运行后生成

  • User.java(实体类)
  • UserMapper.java(Mapper接口)
  • UserService.java(Service接口)
  • UserServiceImpl.java(Service实现)
  • UserController.java(Controller)

3.2 条件构造器(告别XML)

以前 :写一堆if标签的XML 现在:链式调用,可读性强

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    // 复杂条件查询
    public List<User> findUsers(String name, Integer minAge, Integer maxAge, Integer status) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        
        if (StringUtils.isNotBlank(name)) {
            wrapper.like("name", name);  // name like '%?%'
        }
        if (minAge != null) {
            wrapper.ge("age", minAge);  // age >= ?
        }
        if (maxAge != null) {
            wrapper.le("age", maxAge);  // age <= ?
        }
        if (status != null) {
            wrapper.eq("status", status);  // status = ?
        }
        
        wrapper.orderByDesc("create_time");
        return userMapper.selectList(wrapper);
    }
    
    // 更简洁的Lambda写法(推荐)
    public List<User> findUsersLambda(String name, Integer age) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        
        wrapper.like(StringUtils.isNotBlank(name), User::getName, name)
               .eq(age != null, User::getAge, age)
               .orderByDesc(User::getCreateTime);
        
        return userMapper.selectList(wrapper);
    }
}

3.3 分页插件(一行代码搞定)

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

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

// 使用
@Service
public class UserService {
    
    public Page<User> getUsersByPage(int pageNum, int pageSize, String keyword) {
        Page<User> page = new Page<>(pageNum, pageSize);
        
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(keyword), User::getName, keyword);
        
        return userMapper.selectPage(page, wrapper);
    }
}

3.4 自动填充(减少重复代码)

java 复制代码
@Component
public class MyMetaObjectHandler implements 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, "createBy", String.class, getCurrentUsername());
    }
    
    @Override
    public void updateFill(MetaObject metaObject) {
        // 更新时自动填充
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
        this.strictUpdateFill(metaObject, "updateBy", String.class, getCurrentUsername());
    }
}

3.5 逻辑删除(数据不真删)

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

java 复制代码
// 实体类字段加上注解
@TableLogic
private Integer deleted;

// 使用时自动过滤已删除数据
List<User> users = userMapper.selectList(null);  // 只查 deleted=0 的数据

// 删除变成更新
userMapper.deleteById(1L);  // 实际上执行:UPDATE user SET deleted=1 WHERE id=1

// 想查被删除的数据
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("deleted", 1);
List<User> deletedUsers = userMapper.selectList(wrapper);

四、高级功能:解决业务痛点

4.1 多租户数据隔离

java 复制代码
@Configuration
public class MybatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        
        // 多租户插件
        TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor();
        tenantInterceptor.setTenantLineHandler(new TenantLineHandler() {
            @Override
            public Expression getTenantId() {
                // 从当前上下文中获取租户ID
                String tenantId = TenantContext.getCurrentTenantId();
                return new StringValue(tenantId);
            }
            
            @Override
            public String getTenantIdColumn() {
                return "tenant_id";  // 租户字段名
            }
            
            @Override
            public boolean ignoreTable(String tableName) {
                // 忽略不需要租户隔离的表
                return tableName.startsWith("sys_");  // 系统表不隔离
            }
        });
        
        interceptor.addInnerInterceptor(tenantInterceptor);
        return interceptor;
    }
}

4.2 乐观锁(防并发修改)

java 复制代码
// 实体类添加版本字段
@Version
private Integer version;

// 更新时自动带版本号
User user = userMapper.selectById(1L);
user.setName("新名字");
userMapper.updateById(user);  // 自动带上 version 条件
// SQL: UPDATE user SET name=?, version=? WHERE id=? AND version=?

4.3 枚举类型处理

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

java 复制代码
// 定义枚举
@Getter
public enum UserStatus {
    ENABLED(1, "启用"),
    DISABLED(0, "禁用"),
    LOCKED(2, "锁定");
    
    private final Integer code;
    private final String desc;
    
    UserStatus(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }
}

// 实体类使用
@TableField(value = "status", typeHandler = EnumTypeHandler.class)
private UserStatus status;

// 查询时直接使用枚举
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getStatus, UserStatus.ENABLED);

4.4 字段加解密

java 复制代码
// 自定义TypeHandler
public class EncryptTypeHandler extends BaseTypeHandler<String> {
    
    private final Encryptor encryptor = new AESEncryptor();
    
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) {
        // 存数据库时加密
        ps.setString(i, encryptor.encrypt(parameter));
    }
    
    @Override
    public String getNullableResult(ResultSet rs, String columnName) {
        // 取数据时解密
        String value = rs.getString(columnName);
        return value != null ? encryptor.decrypt(value) : null;
    }
}

// 实体类使用
@TableField(value = "phone", typeHandler = EncryptTypeHandler.class)
private String phone;

五、实战:订单系统CRUD

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

5.1 传统写法 vs MyBatis-Plus写法

传统写法(100+行代码)

java 复制代码
// OrderMapper.java(接口)
List<Order> findByCondition(OrderQuery query);
int countByCondition(OrderQuery query);

// OrderMapper.xml(XML,50+行)
<select id="findByCondition" resultType="Order">
    SELECT * FROM orders WHERE 1=1
    <if test="userId != null">AND user_id = #{userId}</if>
    <if test="status != null">AND status = #{status}</if>
    <if test="startTime != null">AND create_time >= #{startTime}</if>
    <if test="endTime != null">AND create_time <= #{endTime}</if>
    ORDER BY create_time DESC
    LIMIT #{offset}, #{limit}
</select>

<select id="countByCondition" resultType="int">
    SELECT COUNT(*) FROM orders WHERE 1=1
    <!-- 重复一遍条件判断 -->
</select>

// OrderService.java(业务逻辑,30+行)
public PageResult<Order> queryOrders(OrderQuery query) {
    int total = orderMapper.countByCondition(query);
    List<Order> list = orderMapper.findByCondition(query);
    return new PageResult<>(total, list);
}

MyBatis-Plus写法(20行代码)

java 复制代码
@Service
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    public Page<Order> queryOrders(OrderQuery query) {
        // 构造查询条件
        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(query.getUserId() != null, Order::getUserId, query.getUserId())
               .eq(query.getStatus() != null, Order::getStatus, query.getStatus())
               .ge(query.getStartTime() != null, Order::getCreateTime, query.getStartTime())
               .le(query.getEndTime() != null, Order::getCreateTime, query.getEndTime())
               .orderByDesc(Order::getCreateTime);
        
        // 分页查询(自动统计总数)
        Page<Order> page = new Page<>(query.getPageNum(), query.getPageSize());
        return orderMapper.selectPage(page, wrapper);
    }
    
    // 统计各状态订单数量
    public Map<String, Long> countByStatus() {
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.select("status, COUNT(*) as count")
               .groupBy("status");
        
        List<Map<String, Object>> list = orderMapper.selectMaps(wrapper);
        // 转换结果...
    }
}

5.2 复杂查询示例

java 复制代码
// 1. 联表查询
public List<OrderVO> getOrderWithUser(Long userId) {
    QueryWrapper<Order> wrapper = new QueryWrapper<>();
    wrapper.select("o.*, u.name as userName")
           .eq("o.user_id", userId)
           .eq("o.deleted", 0)
           .orderByDesc("o.create_time");
    
    return orderMapper.selectOrderWithUser(wrapper);
}

// 自定义Mapper方法
@Select("SELECT o.*, u.name as userName FROM orders o " +
        "LEFT JOIN user u ON o.user_id = u.id " +
        "${ew.customSqlSegment}")  // 使用Wrapper条件
List<OrderVO> selectOrderWithUser(@Param(Constants.WRAPPER) QueryWrapper<Order> wrapper);

// 2. 批量操作
public void batchUpdateStatus(List<Long> orderIds, Integer status) {
    UpdateWrapper<Order> wrapper = new UpdateWrapper<>();
    wrapper.in("id", orderIds)
           .set("status", status)
           .set("update_time", LocalDateTime.now());
    
    orderMapper.update(null, wrapper);  // 批量更新
}

// 3. 存在则更新,不存在则插入
public void saveOrUpdateOrder(Order order) {
    // 自动判断:有id则更新,无id则插入
    orderMapper.insertOrUpdate(order);
}

六、避坑指南

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

坑1:字段名不对应

java 复制代码
// ❌ 错误:数据库是user_name,实体是userName,但没配置映射
private String userName;

// ✅ 正确1:使用@TableField指定
@TableField("user_name")
private String userName;

// ✅ 正确2:配置全局下划线转驼峰
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true

坑2:分页失效

java 复制代码
// ❌ 错误:没配置分页插件
public List<User> getUsers() {
    Page<User> page = new Page<>(1, 10);
    return userMapper.selectPage(page, null);  // 不会分页!
}

// ✅ 正确:配置分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
    return interceptor;
}

坑3:逻辑删除字段类型

java 复制代码
// ❌ 错误:用String类型,但配置的是Integer
@TableLogic
private String deleted;  // 配置是1/0,这里是String

// ✅ 正确:类型匹配
@TableLogic
private Integer deleted;  // 用Integer
// 或
@TableLogic
private Boolean deleted;  // 用Boolean,配置true/false

七、性能优化建议

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

7.1 查询优化

java 复制代码
// 只查询需要的字段
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.select(User::getId, User::getName, User::getAge)  // 只查3个字段
       .eq(User::getStatus, 1);

7.2 批量操作

java 复制代码
// 批量插入(性能提升10倍)
List<User> userList = new ArrayList<>();
// ...添加数据
userMapper.insertBatchSomeColumn(userList);  // 批量插入方法

// 批量更新
List<Long> ids = Arrays.asList(1L, 2L, 3L);
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.in("id", ids)
       .set("status", 2);
userMapper.update(null, wrapper);

7.3 缓存使用

java 复制代码
@Cacheable(value = "user", key = "#id")
public User getUserById(Long id) {
    return userMapper.selectById(id);
}

@CacheEvict(value = "user", key = "#user.id")
public void updateUser(User user) {
    userMapper.updateById(user);
}

八、今日要点总结

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

  1. 代码生成器:新项目、新表一键生成所有代码
  2. 条件构造器:告别XML,链式调用更清晰
  3. 分页插件:一行代码搞定分页查询
  4. 自动填充:创建时间、更新时间自动维护
  5. 逻辑删除:数据不真删,便于恢复和审计
  6. 高级功能:多租户、乐观锁、枚举处理
  7. 性能优化:字段选择、批量操作、缓存

对比一下

功能 传统MyBatis MyBatis-Plus 效率提升
基础CRUD 写XML,4个方法 继承BaseMapper,0行代码
条件查询 XML里写if标签 链式调用 3倍
分页查询 写2个SQL,手动计算 selectPage一行代码 5倍
逻辑删除 每个查询加where条件 @TableLogic自动过滤 10倍
字段填充 每个方法里set 自动填充处理器 8倍

九、思考题

场景:你要开发一个电商后台管理系统,有50张表

  1. 如何快速开始开发?
  2. 如何实现统一的数据权限(不同人看不同数据)?
  3. 如何优化大批量数据导出的性能?
  4. 如何实现操作日志自动记录?

评论区聊聊你的方案,明儿咱们讲WebSocket。


零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

明天预告:《SpringBoot+WebSocket:在线聊天室从0到1》

今日福利:关注后回复"MP代码生成",获取完整代码生成器配置和模板文件。


运营小贴士:

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

💡 互动

  1. 你现在用的是什么ORM框架?有什么痛点?
  2. 投票:你会尝试MyBatis-Plus吗?
  3. 留言分享你的效率提升技巧

🎯 转发话术: "少写80%的代码!MyBatis-Plus让CRUD开发快到飞起!"

🎁 福利

  1. 留言区抽3人送《MyBatis-Plus实战教程》
  2. 转发截图送企业级代码生成器模板
相关推荐
小楼v2 小时前
构建高效AI工作流:Java生态的LangGraph4j框架详解
java·后端·工作流·langgraph4j
爱笑的源码基地2 小时前
智慧校园电子班牌系统源码:基于Java+SpringBoot+Vue等技术开发的数字化管理平台,智慧班牌云平台源码
spring boot·智慧校园·电子班牌·源码·数字化·智慧班牌·智能化
jvstar2 小时前
JNI 面试题及答案
java
虾说羊2 小时前
JVM 高频面试题全解析
java·开发语言·jvm
雨中飘荡的记忆2 小时前
MyBatis SQL解析模块详解
java·mybatis
czlczl200209252 小时前
Spring Cache 全景指南
java·后端·spring
undsky2 小时前
【RuoYi-SpringBoot3-Pro】:MyBatis-Plus 集成
spring boot·后端·mybatis
thulium_2 小时前
SpringBoot3 配置嵌入Servlet容器
spring boot·servlet
invicinble2 小时前
透视IDEA,IDEA认识到什么程度算精通
java·ide·intellij-idea