【mybatisPlus详解】
mybatisPlus详解
一、MyBatis-Plus 简介
核心定位与优势
MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。它的核心优势包括:
-
无侵入性:引入MyBatis-Plus不会对现有MyBatis工程产生影响,可以平滑迁移
-
低损耗:启动时自动注入基本CURD,性能基本无损耗
-
强大的CRUD:内置通用Mapper和Service,少量配置即可实现大部分单表操作
-
丰富的功能:提供条件构造器、分页插件、代码生成器等实用工具
适用场景
-
快速开发CRUD应用:减少约90%的CRUD代码
-
管理系统开发:简单的增删改查场景
-
微服务架构:配合代码生成器快速生成各服务基础代码
-
现有MyBatis项目优化:可平滑集成,逐步替换简单CRUD
二、基础配置与集成
1. 依赖配置
xml
<!-- Maven 依赖 -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version>
</dependency>
- Spring Boot 配置
yaml
application.yml
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启SQL日志global-config:db-config:id-type: assign_id # 主键生成策略(雪花算法)
- 实体类配置
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 接口详解
- BaseMapper 基础接口
java
// Mapper接口需继承BaseMapper
@Mapper
public interface UserMapper extends BaseMapper<User> {// 无需编写XML,即可获得基础CRUD方法
}
- 插入操作
java
@SpringBootTest
class InsertTests {@Autowiredprivate UserMapper userMapper;@Testvoid 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}
}
- 删除操作
java
@SpringBootTest
class DeleteTests {@Autowiredprivate UserMapper userMapper;@Testvoid testDeleteById() {// 根据ID删除int result = userMapper.deleteById(1475754982694199298L);System.out.println("删除结果: " + result);}@Testvoid testDeleteBatchIds() {// 批量删除List<Long> idList = Arrays.asList(1L, 2L, 3L);int result = userMapper.deleteBatchIds(idList);System.out.println("批量删除结果: " + result);}@Testvoid testDeleteByMap() {// 根据条件删除Map<String, Object> conditionMap = new HashMap<>();conditionMap.put("user_name", "张三");conditionMap.put("age", 25);int result = userMapper.deleteByMap(conditionMap);System.out.println("条件删除结果: " + result);}
}
- 更新操作
java
@SpringBootTest
class UpdateTests {@Autowiredprivate UserMapper userMapper;@Testvoid testUpdateById() {User user = new User();user.setId(1L);user.setUserName("李四");user.setEmail("lisi@example.com");int result = userMapper.updateById(user);System.out.println("更新结果: " + result);}
}
- 查询操作
java
@SpringBootTest
class SelectTests {@Autowiredprivate UserMapper userMapper;@Testvoid testSelectById() {User user = userMapper.selectById(1L);System.out.println("查询结果: " + user);}@Testvoid testSelectBatchIds() {List<Long> idList = Arrays.asList(1L, 2L, 3L);List<User> users = userMapper.selectBatchIds(idList);users.forEach(System.out::println);}@Testvoid 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);}@Testvoid testSelectList() {// 查询所有数据List<User> users = userMapper.selectList(null);users.forEach(System.out::println);}
}
四、Service CRUD 接口详解
- Service层配置
java
// Service接口
public interface UserService extends IService<User> {// 可扩展自定义方法
}
// Service实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {// 已获得所有基础CRUD方法
}
- Service CRUD 操作
java
@SpringBootTest
class ServiceTests {@Autowiredprivate UserService userService;@Testvoid testSave() {User user = new User();user.setUserName("王五");user.setAge(30);boolean result = userService.save(user);System.out.println("保存结果: " + result);}@Testvoid 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);}@Testvoid testGetById() {User user = userService.getById(1L);System.out.println("查询结果: " + user);}@Testvoid testUpdate() {User user = new User();user.setId(1L);user.setUserName("更新后的名字");boolean result = userService.updateById(user);System.out.println("更新结果: " + result);}@Testvoid testRemove() {boolean result = userService.removeById(1L);System.out.println("删除结果: " + result);}@Testvoid testList() {List<User> userList = userService.list();userList.forEach(System.out::println);}
}
- 复杂查询方法
java
@SpringBootTest
class ComplexQueryTests {@Autowiredprivate UserService userService;@Testvoid testGetOne() {// 查询一条记录,如果找到多条会抛出异常QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("user_name", "张三");User user = userService.getOne(queryWrapper, true); // 第二个参数为是否抛出异常System.out.println("查询结果: " + user);}@Testvoid testCount() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.gt("age", 20); // 年龄大于20long count = userService.count(queryWrapper);System.out.println("统计结果: " + count);}@Testvoid 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);}
}
五、条件构造器详解
- QueryWrapper 基础用法
java
@SpringBootTest
class QueryWrapperTests {@Autowiredprivate UserMapper userMapper;@Testvoid 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);}@Testvoid 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);}@Testvoid 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);}
}
- Lambda 条件构造器
java
@SpringBootTest
class LambdaWrapperTests {@Autowiredprivate UserMapper userMapper;@Testvoid 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);}@Testvoid testLambdaWithService() {List<User> users = userService.lambdaQuery().eq(User::getAge, 25).like(User::getUserName, "张").list();users.forEach(System.out::println);}
}
- UpdateWrapper 更新构造器
java
@SpringBootTest
class UpdateWrapperTests {@Autowiredprivate UserMapper userMapper;@Testvoid 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);}@Testvoid testLambdaUpdate() {boolean result = userService.lambdaUpdate().set(User::getAge, 35).set(User::getUpdateTime, LocalDateTime.now()).eq(User::getUserName, "张三").update();System.out.println("Lambda更新结果: " + result);}
}
六、高级功能
- 分页插件配置与使用
java
@Configuration
public class MybatisPlusConfig {/*** 分页插件配置*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
}// 分页使用示例
@SpringBootTest
class PageTests {@Autowiredprivate UserMapper userMapper;@Testvoid 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);}@Testvoid 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
mybatis-plus:global-config:db-config:logic-delete-field: deleted # 逻辑删除字段名logic-delete-value: 1 # 删除值logic-not-delete-value: 0 # 未删除值
java
// 实体类配置
@Data
@TableName("sys_user")
public class User {// 其他字段...@TableLogic // 逻辑删除注解private Integer deleted;
}
// 使用示例:删除操作会自动变为更新deleted字段
@Test
void testLogicDelete() {// 实际执行的是:UPDATE sys_user SET deleted = 1 WHERE id = ? AND deleted = 0boolean result = userService.removeById(1L);System.out.println("逻辑删除结果: " + result);// 查询时会自动加上 deleted = 0 条件User user = userService.getById(1L); // 返回null,因为已被逻辑删除
}
3. 自动填充功能
java
// 实体类配置
@Data
@TableName("sys_user")
public class User {// 其他字段...@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;
}
自动填充处理器
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}@Overridepublic void updateFill(MetaObject metaObject) {this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}
}
4. 枚举类型处理
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;}
}
实体类使用枚举
@Data
@TableName("sys_user")
public class User {// 其他字段...@EnumValue // 标记数据库存储的是code值private UserStatus status;
}
配置枚举包扫描
@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() {return properties -> {properties.getConfiguration().setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class);};}
}
- 多数据源支持
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@Primarypublic SqlSessionFactory primarySqlSessionFactory() throws Exception {MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();sqlSessionFactory.setDataSource(primaryDataSource());return sqlSessionFactory.getObject();}
}
七、代码生成器
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();}
}
八、最佳实践与注意事项
- 性能优化建议
java
// 批量操作示例
@SpringBootTest
class BatchOperationTests {@Autowiredprivate UserService userService;@Testvoid 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);}@Testvoid testBatchUpdate() {List<User> userList = userService.list();userList.forEach(user -> user.setAge(user.getAge() + 1));// 分批更新boolean result = userService.updateBatchById(userList, 100);System.out.println("批量更新结果: " + result);}
}
- 复杂SQL处理
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
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public List<String> getNamesByAgeGreaterThan30() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.select("name") // 只查询name字段.gt("age", 30); // age > 30List<User> users = userMapper.selectList(queryWrapper);return users.stream().map(User::getName).collect(Collectors.toList());}
}
方法二:使用LambdaQueryWrapper(推荐)
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public List<String> getNamesByAgeGreaterThan30() {LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.select(User::getName) // 只选择name字段.gt(User::getAge, 30); // age > 30List<User> users = userMapper.selectList(lambdaQueryWrapper);return users.stream().map(User::getName).collect(Collectors.toList());}
}
方法三:自定义Mapper方法(最高效)
在UserMapper中定义自定义方法:
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中调用:
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public List<String> getNamesByAgeGreaterThan30() {return userMapper.selectNamesByAgeGreaterThan30();// 或者// return userMapper.selectNamesByAgeGreaterThan(30);}
}
实体类定义
@Data
@TableName("user")
public class User {@TableIdprivate Long id;private String name;private Integer age;
}
推荐方案
推荐使用方法三(自定义Mapper方法),因为:
性能最优,直接返回字符串列表
代码最简洁
避免不必要的对象转换
SQL清晰易懂
如果你坚持使用Wrapper方式,方法二(LambdaQueryWrapper)是更好的选择,类型安全且可读性强。