【mybatisPlus详解】

mybatisPlus详解

一、MyBatis-Plus 简介

核心定位与优势

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

  • 无侵入性:引入MyBatis-Plus不会对现有MyBatis工程产生影响,可以平滑迁移

  • 低损耗:启动时自动注入基本CURD,性能基本无损耗

  • 强大的CRUD:内置通用Mapper和Service,少量配置即可实现大部分单表操作

  • 丰富的功能:提供条件构造器、分页插件、代码生成器等实用工具

适用场景

  • 快速开发CRUD应用:减少约90%的CRUD代码

  • 管理系统开发:简单的增删改查场景

  • 微服务架构:配合代码生成器快速生成各服务基础代码

  • 现有MyBatis项目优化:可平滑集成,逐步替换简单CRUD

二、基础配置与集成

1. 依赖配置

xml

java 复制代码
<!-- Maven 依赖 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.0</version>
</dependency>
  1. Spring Boot 配置
    yaml

application.yml

java 复制代码
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 开启SQL日志
  global-config:
    db-config:
      id-type: assign_id  # 主键生成策略(雪花算法)
  1. 实体类配置
    java
    // 实体类示例
java 复制代码
@Data
@TableName("sys_user")  // 指定表名
public class User {
    @TableId(type = IdType.ASSIGN_ID)  // 主键策略:雪花算法
    private Long id;
    
    @TableField("user_name")  // 字段映射(非必须,默认开启驼峰转换)
    private String userName;
    
    private Integer age;
    private String email;
    
    @TableField(exist = false)  // 非数据库字段
    private String temporaryData;
}

三、Mapper CRUD 接口详解

  1. BaseMapper 基础接口
    java
java 复制代码
// Mapper接口需继承BaseMapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 无需编写XML,即可获得基础CRUD方法
}
  1. 插入操作
    java
java 复制代码
@SpringBootTest
class InsertTests {
    
    @Autowired
    private UserMapper userMapper;
    
    @Test
    void testInsert() {
        User user = new User();
        user.setUserName("张三");
        user.setAge(25);
        user.setEmail("zhangsan@example.com");
        
        int result = userMapper.insert(user);
        System.out.println("影响行数: " + result);
        System.out.println("生成ID: " + user.getId()); // 自动回填ID
    }
}
  1. 删除操作
    java
java 复制代码
@SpringBootTest
class DeleteTests {
    
    @Autowired
    private UserMapper userMapper;
    
    @Test
    void testDeleteById() {
        // 根据ID删除
        int result = userMapper.deleteById(1475754982694199298L);
        System.out.println("删除结果: " + result);
    }
    
    @Test
    void testDeleteBatchIds() {
        // 批量删除
        List<Long> idList = Arrays.asList(1L, 2L, 3L);
        int result = userMapper.deleteBatchIds(idList);
        System.out.println("批量删除结果: " + result);
    }
    
    @Test
    void testDeleteByMap() {
        // 根据条件删除
        Map<String, Object> conditionMap = new HashMap<>();
        conditionMap.put("user_name", "张三");
        conditionMap.put("age", 25);
        
        int result = userMapper.deleteByMap(conditionMap);
        System.out.println("条件删除结果: " + result);
    }
}
  1. 更新操作
    java
java 复制代码
@SpringBootTest
class UpdateTests {
    
    @Autowired
    private UserMapper userMapper;
    
    @Test
    void testUpdateById() {
        User user = new User();
        user.setId(1L);
        user.setUserName("李四");
        user.setEmail("lisi@example.com");
        
        int result = userMapper.updateById(user);
        System.out.println("更新结果: " + result);
    }
}
  1. 查询操作
    java
java 复制代码
@SpringBootTest
class SelectTests {
    
    @Autowired
    private UserMapper userMapper;
    
    @Test
    void testSelectById() {
        User user = userMapper.selectById(1L);
        System.out.println("查询结果: " + user);
    }
    
    @Test
    void testSelectBatchIds() {
        List<Long> idList = Arrays.asList(1L, 2L, 3L);
        List<User> users = userMapper.selectBatchIds(idList);
        users.forEach(System.out::println);
    }
    
    @Test
    void testSelectByMap() {
        Map<String, Object> conditionMap = new HashMap<>();
        conditionMap.put("user_name", "张三");
        conditionMap.put("age", 25);
        
        List<User> users = userMapper.selectByMap(conditionMap);
        users.forEach(System.out::println);
    }
    
    @Test
    void testSelectList() {
        // 查询所有数据
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }
}

四、Service CRUD 接口详解

  1. Service层配置
    java
java 复制代码
// Service接口
public interface UserService extends IService<User> {
    // 可扩展自定义方法
}
java 复制代码
// Service实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> 
    implements UserService {
    // 已获得所有基础CRUD方法
}
  1. Service CRUD 操作
    java
java 复制代码
@SpringBootTest
class ServiceTests {
    
    @Autowired
    private UserService userService;
    
    @Test
    void testSave() {
        User user = new User();
        user.setUserName("王五");
        user.setAge(30);
        
        boolean result = userService.save(user);
        System.out.println("保存结果: " + result);
    }
    
    @Test
    void testSaveBatch() {
        List<User> userList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            User user = new User();
            user.setUserName("用户" + i);
            user.setAge(20 + i);
            userList.add(user);
        }
        
        boolean result = userService.saveBatch(userList);
        System.out.println("批量保存结果: " + result);
    }
    
    @Test
    void testGetById() {
        User user = userService.getById(1L);
        System.out.println("查询结果: " + user);
    }
    
    @Test
    void testUpdate() {
        User user = new User();
        user.setId(1L);
        user.setUserName("更新后的名字");
        
        boolean result = userService.updateById(user);
        System.out.println("更新结果: " + result);
    }
    
    @Test
    void testRemove() {
        boolean result = userService.removeById(1L);
        System.out.println("删除结果: " + result);
    }
    
    @Test
    void testList() {
        List<User> userList = userService.list();
        userList.forEach(System.out::println);
    }
}
  1. 复杂查询方法
    java
java 复制代码
@SpringBootTest
class ComplexQueryTests {
    
    @Autowired
    private UserService userService;
    
    @Test
    void testGetOne() {
        // 查询一条记录,如果找到多条会抛出异常
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_name", "张三");
        
        User user = userService.getOne(queryWrapper, true); // 第二个参数为是否抛出异常
        System.out.println("查询结果: " + user);
    }
    
    @Test
    void testCount() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age", 20); // 年龄大于20
        
        long count = userService.count(queryWrapper);
        System.out.println("统计结果: " + count);
    }
    
    @Test
    void testPage() {
        // 分页查询
        Page<User> page = new Page<>(1, 10); // 当前页,每页大小
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("id");
        
        Page<User> result = userService.page(page, queryWrapper);
        System.out.println("总记录数: " + result.getTotal());
        System.out.println("总页数: " + result.getPages());
        result.getRecords().forEach(System.out::println);
    }
}

五、条件构造器详解

  1. QueryWrapper 基础用法
    java
java 复制代码
@SpringBootTest
class QueryWrapperTests {
    
    @Autowired
    private UserMapper userMapper;
    
    @Test
    void testBasicQuery() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        
        // 等值查询
        queryWrapper.eq("user_name", "张三")
                   .ge("age", 18)  // 大于等于
                   .le("age", 65)  // 小于等于
                   .like("email", "@example.com")  // 模糊查询
                   .isNotNull("create_time");  // 不为空
        
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
    @Test
    void testComplexQuery() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        
        // 复杂条件: (age > 18 AND age < 30) OR (user_name LIKE '%张%')
        queryWrapper.and(wrapper -> wrapper.gt("age", 18).lt("age", 30))
                   .or(wrapper -> wrapper.like("user_name", "张"));
        
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
    @Test
    void testSelectFields() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        
        // 指定查询字段
        queryWrapper.select("id", "user_name", "age")
                   .eq("age", 25)
                   .orderByDesc("id");
        
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
}
  1. Lambda 条件构造器
    java
java 复制代码
@SpringBootTest
class LambdaWrapperTests {
    
    @Autowired
    private UserMapper userMapper;
    
    @Test
    void testLambdaQuery() {
        LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
        
        // 使用Lambda表达式,避免硬编码字段名
        lambdaWrapper.eq(User::getUserName, "张三")
                    .ge(User::getAge, 18)
                    .between(User::getCreateTime, 
                            LocalDateTime.now().minusDays(7), 
                            LocalDateTime.now());
        
        List<User> users = userMapper.selectList(lambdaWrapper);
        users.forEach(System.out::println);
    }
    
    @Test
    void testLambdaWithService() {
        List<User> users = userService.lambdaQuery()
            .eq(User::getAge, 25)
            .like(User::getUserName, "张")
            .list();
            
        users.forEach(System.out::println);
    }
}
  1. UpdateWrapper 更新构造器
    java
java 复制代码
@SpringBootTest
class UpdateWrapperTests {
    
    @Autowired
    private UserMapper userMapper;
    
    @Test
    void testUpdateWithWrapper() {
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        
        // 设置更新内容和条件
        updateWrapper.set("age", 30)
                    .set("update_time", LocalDateTime.now())
                    .eq("user_name", "张三")
                    .ge("age", 25);
        
        int result = userMapper.update(null, updateWrapper);
        System.out.println("更新影响行数: " + result);
    }
    
    @Test
    void testLambdaUpdate() {
        boolean result = userService.lambdaUpdate()
            .set(User::getAge, 35)
            .set(User::getUpdateTime, LocalDateTime.now())
            .eq(User::getUserName, "张三")
            .update();
            
        System.out.println("Lambda更新结果: " + result);
    }
}

六、高级功能

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

// 分页使用示例
@SpringBootTest
class PageTests {
    
    @Autowired
    private UserMapper userMapper;
    
    @Test
    void testPageQuery() {
        // 第一页,每页10条
        Page<User> page = new Page<>(1, 10);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("id");
        
        Page<User> result = userMapper.selectPage(page, queryWrapper);
        
        System.out.println("总记录数: " + result.getTotal());
        System.out.println("总页数: " + result.getPages());
        System.out.println("当前页: " + result.getCurrent());
        System.out.println("每页大小: " + result.getSize());
        
        result.getRecords().forEach(System.out::println);
    }
    
    @Test
    void testCustomPage() {
        Page<User> page = new Page<>(1, 5);
        
        // 自定义分页查询
        Page<User> result = userMapper.selectPage(page, 
            Wrappers.<User>lambdaQuery()
                .ge(User::getAge, 18)
                .orderByAsc(User::getAge));
        
        result.getRecords().forEach(System.out::println);
    }
}

2. 逻辑删除配置

yaml

application.yml

java 复制代码
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted  # 逻辑删除字段名
      logic-delete-value: 1  # 删除值
      logic-not-delete-value: 0  # 未删除值

java

java 复制代码
// 实体类配置
@Data
@TableName("sys_user")
public class User {
    // 其他字段...
    
    @TableLogic  // 逻辑删除注解
    private Integer deleted;
}
java 复制代码
// 使用示例:删除操作会自动变为更新deleted字段
@Test
void testLogicDelete() {
    // 实际执行的是:UPDATE sys_user SET deleted = 1 WHERE id = ? AND deleted = 0
    boolean result = userService.removeById(1L);
    System.out.println("逻辑删除结果: " + result);
    
    // 查询时会自动加上 deleted = 0 条件
    User user = userService.getById(1L); // 返回null,因为已被逻辑删除
}

3. 自动填充功能

java

java 复制代码
// 实体类配置
@Data
@TableName("sys_user")
public class User {
    // 其他字段...
    
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

自动填充处理器

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());
    }
    
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

4. 枚举类型处理

java

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

实体类使用枚举

java 复制代码
@Data
@TableName("sys_user")
public class User {
    // 其他字段...
    
    @EnumValue  // 标记数据库存储的是code值
    private UserStatus status;
}

配置枚举包扫描

java 复制代码
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() {
        return properties -> {
            properties.getConfiguration().setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class);
        };
    }
}
  1. 多数据源支持
    java
java 复制代码
// 多数据源配置示例
@Configuration
@MapperScan(basePackages = "com.example.mapper.primary", 
           sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {
    
    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DruidDataSourceBuilder.create().build();
    }
    
    @Bean
    @Primary
    public SqlSessionFactory primarySqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(primaryDataSource());
        return sqlSessionFactory.getObject();
    }
}

七、代码生成器

java

java 复制代码
// 代码生成器配置
public class CodeGenerator {
    
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator generator = new AutoGenerator();
        
        // 全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
        globalConfig.setAuthor("YourName");
        globalConfig.setOpen(false);
        globalConfig.setFileOverride(true);
        generator.setGlobalConfig(globalConfig);
        
        // 数据源配置
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/test?useSSL=false");
        dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
        dataSourceConfig.setUsername("root");
        dataSourceConfig.setPassword("password");
        generator.setDataSource(dataSourceConfig);
        
        // 包配置
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setParent("com.example");
        packageConfig.setModuleName("system");
        packageConfig.setEntity("entity");
        packageConfig.setMapper("mapper");
        packageConfig.setService("service");
        packageConfig.setController("controller");
        generator.setPackageInfo(packageConfig);
        
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setInclude("user", "role", "menu"); // 要生成的表
        generator.setStrategy(strategy);
        
        // 执行生成
        generator.execute();
    }
}

八、最佳实践与注意事项

  1. 性能优化建议
    java
java 复制代码
// 批量操作示例
@SpringBootTest
class BatchOperationTests {
    
    @Autowired
    private UserService userService;
    
    @Test
    void testBatchInsert() {
        List<User> userList = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            User user = new User();
            user.setUserName("batch_user_" + i);
            user.setAge(20 + (i % 30));
            userList.add(user);
        }
        
        // 分批插入,每批100条
        boolean result = userService.saveBatch(userList, 100);
        System.out.println("批量插入结果: " + result);
    }
    
    @Test
    void testBatchUpdate() {
        List<User> userList = userService.list();
        userList.forEach(user -> user.setAge(user.getAge() + 1));
        
        // 分批更新
        boolean result = userService.updateBatchById(userList, 100);
        System.out.println("批量更新结果: " + result);
    }
}
  1. 复杂SQL处理
    java
java 复制代码
// 自定义SQL方法
public interface UserMapper extends BaseMapper<User> {
    
    // 自定义查询方法
    @Select("SELECT u.*, d.department_name " +
            "FROM sys_user u " +
            "LEFT JOIN sys_department d ON u.department_id = d.id " +
            "WHERE u.age > #{minAge}")
    List<User> selectUsersWithDepartment(@Param("minAge") Integer minAge);
    
    // 自定义分页查询
    IPage<User> selectUserPageWithDepartment(Page<User> page, @Param("userName") String userName);
}

// 对应的XML配置(在resources/mapper/UserMapper.xml中)
<select id="selectUserPageWithDepartment" resultType="com.example.entity.User">
    SELECT u.*, d.department_name 
    FROM sys_user u 
    LEFT JOIN sys_department d ON u.department_id = d.id 
    WHERE u.user_name LIKE CONCAT('%', #{userName}, '%')
</select>

总结

MyBatis-Plus通过提供丰富的功能和简洁的API,极大地提升了开发效率。关键优势包括:

  • 开发效率:减少约90%的CRUD代码

  • 维护性:统一的代码风格和最佳实践

  • 灵活性:既可以使用MP的快捷方法,也可以编写自定义SQL

  • 安全性:内置SQL注入防护和逻辑删除等功能

对于复杂业务场景,建议:

简单CRUD使用MP提供的方法

复杂查询使用Wrapper条件构造器

特别复杂的多表关联使用自定义SQL

合理使用代码生成器减少重复工作

mybatisPlus查询指定字段

方法一:使用QueryWrapper + select指定字段

java

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    public List<String> getNamesByAgeGreaterThan30() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
            .select("name")  // 只查询name字段
            .gt("age", 30);  // age > 30
        
        List<User> users = userMapper.selectList(queryWrapper);
        return users.stream()
                   .map(User::getName)
                   .collect(Collectors.toList());
    }
}

方法二:使用LambdaQueryWrapper(推荐)

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    public List<String> getNamesByAgeGreaterThan30() {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper
            .select(User::getName)  // 只选择name字段
            .gt(User::getAge, 30);  // age > 30
        
        List<User> users = userMapper.selectList(lambdaQueryWrapper);
        return users.stream()
                   .map(User::getName)
                   .collect(Collectors.toList());
    }
}

方法三:自定义Mapper方法(最高效)

在UserMapper中定义自定义方法:

java 复制代码
public interface UserMapper extends BaseMapper<User> {
    
    @Select("SELECT name FROM user WHERE age > 30")
    List<String> selectNamesByAgeGreaterThan30();
    
    // 或者使用参数化的方式
    @Select("SELECT name FROM user WHERE age > #{age}")
    List<String> selectNamesByAgeGreaterThan(@Param("age") Integer age);
}

然后在Service中调用:

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    public List<String> getNamesByAgeGreaterThan30() {
        return userMapper.selectNamesByAgeGreaterThan30();
        // 或者
        // return userMapper.selectNamesByAgeGreaterThan(30);
    }
}

实体类定义

java 复制代码
@Data
@TableName("user")
public class User {
    @TableId
    private Long id;
    private String name;
    private Integer age;
}

推荐方案

推荐使用方法三(自定义Mapper方法),因为:

性能最优,直接返回字符串列表

代码最简洁

避免不必要的对象转换

SQL清晰易懂

如果你坚持使用Wrapper方式,方法二(LambdaQueryWrapper)是更好的选择,类型安全且可读性强。

相关推荐
2301_797604244 小时前
d41:MyBatisPlus入门,注解,配置,条件构造器,自定义SQL,IService
sql·mybatis
ZhengEnCi4 小时前
Java_Object 数组完全指南-从入门到精通的多类型数据存储利器
java·后端
starxg4 小时前
bkhtmltopdf - 高性能 HTML 转 PDF 工具(代替 wkhtmltopdf)
java·pdf·html·wkhtmltopdf·htmltopdf
尘下吹霜4 小时前
【鉴权架构】SpringBoot + Sa-Token + MyBatis + MySQL + Redis 实现用户鉴权、角色管理、权限管理
spring boot·mysql·mybatis
it技术4 小时前
[入门精通] TLjava高薪扩展训练VIP系列179G69
java
ZhengEnCi4 小时前
ObjectUtils.isEmpty 完全指南-从入门到精通的 Java 空值判断利器
java·后端
FreeBuf_5 小时前
Happy DOM曝CVSS 9.4严重RCE漏洞,PoC已公开(CVE-2025-61927)
java·c语言·c++·python·php
自由的疯5 小时前
优雅的代码java
java·后端·面试
一只小风华~5 小时前
Vue Router 导航守卫
java·前端·javascript·vue.js·笔记·html