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>
- 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 # 主键生成策略(雪花算法)
- 实体类配置
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 接口详解
- BaseMapper 基础接口
java
java
// Mapper接口需继承BaseMapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 无需编写XML,即可获得基础CRUD方法
}
- 插入操作
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
}
}
- 删除操作
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);
}
}
- 更新操作
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);
}
}
- 查询操作
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 接口详解
- Service层配置
java
java
// Service接口
public interface UserService extends IService<User> {
// 可扩展自定义方法
}
java
// Service实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService {
// 已获得所有基础CRUD方法
}
- 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);
}
}
- 复杂查询方法
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);
}
}
五、条件构造器详解
- 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);
}
}
- 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);
}
}
- 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);
}
}
六、高级功能
- 分页插件配置与使用
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);
};
}
}
- 多数据源支持
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();
}
}
八、最佳实践与注意事项
- 性能优化建议
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);
}
}
- 复杂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)是更好的选择,类型安全且可读性强。