MyBatis-Plus:让数据库操作飞起来的神器
一、MyBatis-Plus 是什么?
在 Java 开发的世界里,数据库操作是不可或缺的一部分。MyBatis 作为一款优秀的持久层框架,深受开发者喜爱,而 MyBatis-Plus 则是在 MyBatis 基础上诞生的强大增强工具。它就像是给 MyBatis 这位 "武林高手" 配备了一套超级装备,让开发变得更加高效和便捷。
MyBatis-Plus 秉持着 "只做增强不做改变" 的设计理念 ,这意味着当你引入它到项目中时,就像给原有的 MyBatis 系统轻轻地披上一层 "魔法披风",不会对现有工程产生任何侵入性影响。它保留了 MyBatis 的所有优点,比如灵活的 SQL 自定义、强大的映射功能等,同时又添加了许多实用的新特性,让开发人员从繁琐的基础代码编写中解脱出来。
想象一下,在传统的数据库操作中,我们常常需要为每一个基本的增删改查(CRUD)操作编写大量重复的 SQL 语句,这不仅耗时费力,还容易出错。而 MyBatis-Plus 内置了通用的 Mapper 和 Service,通过少量的配置,就能轻松实现单表大部分的 CRUD 操作 。就好比有了一个万能的 "数据库操作助手",只要告诉它你的需求,它就能快速准确地完成任务,大大提高了开发效率。
例如,在查询用户信息时,以往我们可能需要编写复杂的 SQL 语句来构建查询条件,而现在使用 MyBatis-Plus,借助它强大的条件构造器,只需通过简单的代码就能实现各种复杂查询,代码量大幅减少,而且可读性更强。 它还支持 Lambda 形式调用,让我们在编写查询条件时,无需再担心字段写错,就像有了一个智能的代码检查器,时刻为我们的代码质量保驾护航。
二、为什么选择 MyBatis-Plus?
(一)原生 MyBatis 的痛点
在 MyBatis-Plus 出现之前,原生 MyBatis 在开发中虽然有着灵活的 SQL 定制能力,但也暴露出不少让人头疼的问题 。
大量重复代码就是其中之一,在使用原生 MyBatis 时,每一个数据库表都需要开发者手动编写对应的 Mapper 接口和 XML 映射文件,实现增删改查(CRUD)操作。想象一下,一个项目中如果有十几个甚至几十个表,那光是这些基础的 CRUD 代码就得花费大量的时间和精力去编写,而且这些代码结构相似,只是针对不同表的字段和表名有所变化,这无疑是一种重复劳动,不仅降低开发效率,还增加了维护成本。
XML 文件臃肿也是原生 MyBatis 难以避免的问题。即使是非常简单的查询操作,也需要在 XML 文件中编写完整的 SQL 语句。随着项目的不断迭代和功能的增加,XML 文件会越来越大,内容也越来越复杂,这使得代码的可读性和可维护性大幅下降。修改一个小功能,可能就需要在冗长的 XML 文件中艰难地找到对应的 SQL 语句,稍有不慎还可能影响其他功能。
分页功能繁琐同样困扰着开发者,在原生 MyBatis 中实现分页,需要手动编写分页 SQL 语句,不同数据库的分页语法还存在差异,比如 MySQL 使用 LIMIT 关键字实现分页,而 Oracle 则需要使用 ROWNUM 伪列。这就意味着开发者需要针对不同数据库编写不同的分页代码,大大增加了开发难度和工作量。此外,还需要手动处理分页参数,如当前页码、每页显示条数等,容易出现参数传递错误或计算错误的情况。
还有就是条件查询麻烦,当进行动态条件查询时,原生 MyBatis 需要在代码中编写大量的 if - else 判断语句来拼接 SQL 条件。这不仅使得代码变得冗长复杂,可读性差,而且还容易出现 SQL 注入风险,对系统的安全性构成威胁。例如,当根据用户输入的条件进行查询时,如果没有对用户输入进行严格的过滤和转义,恶意用户可能会通过输入特殊字符来篡改 SQL 语句,获取或修改不应该访问的数据。
(二)MyBatis-Plus 优势
MyBatis-Plus 的出现,就像是为这些问题量身定制的解决方案,完美地解决了原生 MyBatis 的诸多痛点 。
它的无侵入性是一大亮点,正如前面所说,它秉持 "只做增强不做改变" 的理念,在引入到项目中时,不会对原有的 MyBatis 代码结构和配置产生任何影响,就像给项目轻轻地披上一层 "隐形披风",既能享受它带来的强大功能,又无需担心兼容性问题 。
强大的 CRUD 操作让开发效率大幅提升。MyBatis-Plus 内置了通用的 Mapper 和 Service,只要让自定义的 Mapper 接口继承 BaseMapper,就可以自动拥有单表的大部分 CRUD 方法,无需再编写大量重复的 SQL 语句和 XML 映射文件。这就好比有了一个 "超级代码生成器",一键就能生成常用的数据库操作代码,让开发者从繁琐的基础代码编写中解放出来,专注于业务逻辑的实现。
在条件查询方面,MyBatis-Plus 提供了强大的条件构造器,如 QueryWrapper 和 LambdaQueryWrapper 。使用它们,开发者可以通过简单的方法调用轻松构建各种复杂的查询条件,而无需编写 SQL 语句。LambdaQueryWrapper 更是支持 Lambda 表达式,通过方法引用获取实体类字段,避免了手动输入字段名可能出现的拼写错误,同时也让代码更加简洁、易读。比如,想要查询年龄大于 18 岁且姓名中包含 "张" 的用户,使用 LambdaQueryWrapper 只需短短几行代码就能实现:
java
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.gt(User::getAge, 18).like(User::getName, "张");
List<User> userList = userMapper.selectList(wrapper);
MyBatis-Plus 的内置分页插件也十分实用,只需要简单配置,就能实现物理分页功能。它会自动在 SQL 语句中添加分页相关的关键字和参数,适配不同的数据库,开发者无需再手动编写分页 SQL 和处理分页参数,大大简化了分页操作的实现过程 。比如,要实现查询第 2 页,每页显示 10 条数据,代码如下:
java
Page<User> page = new Page<>(2, 10);
Page<User> result = userMapper.selectPage(page, null);
此外,MyBatis-Plus 还拥有丰富的插件体系,支持逻辑删除、自动填充、乐观锁、多数据源等功能。逻辑删除可以在不真正删除数据的情况下,将数据标记为已删除状态,避免数据误删,同时也方便数据的回溯和统计;自动填充功能可以在插入或更新数据时,自动填充一些公共字段,如创建时间、更新时间等,减少手动赋值的操作;乐观锁插件则通过版本号机制解决了并发更新时的数据一致性问题,确保在高并发场景下数据的准确性 。
三、快速上手 MyBatis-Plus
(一)环境搭建
以 Spring Boot 项目为例,搭建 MyBatis-Plus 开发环境的步骤并不复杂 。
首先创建一个 Spring Boot 项目,可以使用 IDEA 的 Spring Initializr 快速创建,也可以通过 Maven Archetype 手动创建。在创建过程中,记得勾选 Spring Web 依赖,这将为项目提供 Web 开发支持。
创建好项目后,在pom\.xml文件中添加 MyBatis-Plus 依赖。如果还需要连接数据库,比如 MySQL,还需要添加 MySQL 驱动依赖。如果想简化实体类的编写,使用 Lombok 也是个不错的选择,它能通过注解自动生成 getter、setter、equals、hashCode 和 toString 等方法 。以下是相关依赖配置示例:
xml
<dependencies>
<!-- Spring Boot 核心启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis-Plus 启动器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.7</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok 简化实体类编写 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
添加完依赖后,点击pom\.xml文件右上角的 Maven 图标刷新依赖,确保所有依赖都成功下载。
接着配置数据库连接,在src/main/resources目录下找到application\.yml文件(如果没有则新建一个),添加数据库连接相关配置 :
yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/your_database_name?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
username: your_username
password: your_password
记得将your\_database\_name、your\_username和your\_password替换为实际的数据库名、用户名和密码 。
最后,为了让 Spring 能够扫描到 MyBatis-Plus 的 Mapper 接口,需要在 Spring Boot 启动类上添加@MapperScan注解,并指定 Mapper 接口所在的包路径 :
java
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 替换为实际的Mapper接口包路径
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
(二)基本 CRUD 操作
完成环境搭建后,就可以使用 MyBatis-Plus 进行基本的 CRUD 操作了 。
假设我们有一个User实体类,对应数据库中的user表 :
java
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
这里使用了 Lombok 的@Data注解简化代码,同时通过@TableName注解指定实体类对应的数据库表名。
接下来创建UserMapper接口,让它继承BaseMapper\<User\>,这样就自动拥有了单表的大部分 CRUD 方法 :
java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
有了实体类和 Mapper 接口,就可以进行具体的 CRUD 操作了 。
插入操作 :使用insert方法将一个User对象插入到数据库中 :
java
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testInsert() {
User user = new User();
user.setName("张三");
user.setAge(20);
user.setEmail("zhangsan@example.com");
int result = userMapper.insert(user);
System.out.println("插入成功,影响行数:" + result);
System.out.println("自动生成的ID:" + user.getId());
}
}
运行测试方法后,会发现一条新的用户记录被插入到数据库中,并且user对象的id属性会自动填充为数据库生成的主键值,这得益于 MyBatis-Plus 默认的雪花算法策略生成 id 。
更新操作 :使用updateById方法根据id更新User对象的信息 :
java
@Test
public void testUpdate() {
User user = new User();
user.setId(1L); // 假设要更新id为1的用户
user.setName("李四");
user.setAge(22);
int result = userMapper.updateById(user);
System.out.println("更新成功,影响行数:" + result);
}
这里创建一个User对象,设置需要更新的id以及新的属性值,调用updateById方法即可完成更新操作,返回值为受影响的行数 。
删除操作 :可以使用deleteById方法根据id删除用户记录 :
java
@Test
public void testDelete() {
Long id = 1L; // 假设要删除id为1的用户
int result = userMapper.deleteById(id);
System.out.println("删除成功,影响行数:" + result);
}
调用deleteById方法并传入要删除的用户id,即可从数据库中删除对应的记录,返回值同样为受影响的行数 。
查询操作 :使用selectById方法根据id查询用户信息 :
java
@Test
public void testSelect() {
Long id = 1L; // 假设要查询id为1的用户
User user = userMapper.selectById(id);
System.out.println("查询结果:" + user);
}
调用selectById方法并传入id,即可从数据库中查询到对应的用户信息,并将结果封装成User对象返回 。
除了以上单个操作外,MyBatis-Plus 还提供了批量操作的方法,如deleteBatchIds用于批量删除,selectBatchIds用于批量查询等 ,使用方式与单个操作类似,只是传入的参数变为id的集合 。通过这些简单的方法调用,就能轻松完成数据库的基本 CRUD 操作,大大提高了开发效率 。
四、MyBatis-Plus 高级特性
(一)条件构造器(Wrapper)
在实际的数据库操作中,简单的 CRUD 往往无法满足复杂的业务需求,我们经常需要构建各种复杂的查询条件。MyBatis-Plus 的条件构造器(Wrapper)就像是一把 "万能钥匙",为我们打开了复杂查询的大门。
其中,QueryWrapper和UpdateWrapper是最常用的两个条件构造器 。QueryWrapper主要用于构建查询条件,而UpdateWrapper则用于构建更新条件。它们都支持链式调用,让我们可以像搭积木一样,轻松地组合出各种复杂的查询和更新逻辑 。
比如,我们要查询年龄大于 18 岁且姓名中包含 "张" 的用户,并且按照年龄降序排列 。使用QueryWrapper可以这样实现:
java
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 18)
.like("name", "张")
.orderByDesc("age");
List<User> userList = userMapper.selectList(wrapper);
在这段代码中,gt方法表示 "大于",like方法表示 "模糊查询",orderByDesc方法表示 "降序排列" 。通过链式调用,我们可以清晰地表达出复杂的查询条件,而且代码简洁易读 。
再比如,我们要将年龄大于 30 岁的用户的邮箱统一更新为 "new\_email@example.com" 。使用UpdateWrapper可以这样实现:
java
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("email", "new_email@example.com")
.gt("age", 30);
int result = userMapper.update(null, updateWrapper);
这里的set方法用于设置更新的字段和值,gt方法用于指定更新的条件 。通过UpdateWrapper,我们可以在不创建实体对象的情况下,直接设置更新字段和条件,实现高效的更新操作 。
除了QueryWrapper和UpdateWrapper,MyBatis-Plus 还提供了基于 Lambda 表达式的LambdaQueryWrapper和LambdaUpdateWrapper 。它们通过 Lambda 表达式引用实体类的属性,避免了硬编码字段名,进一步提高了代码的可读性和可维护性 。例如,上述查询年龄大于 18 岁且姓名中包含 "张" 的用户的代码,使用LambdaQueryWrapper可以写成:
java
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.gt(User::getAge, 18)
.like(User::getName, "张")
.orderByDesc(User::getAge);
List<User> userList = userMapper.selectList(wrapper);
这样,即使实体类的字段名发生变化,只要属性名不变,代码就无需修改,大大降低了维护成本 。
(二)分页查询
在处理大量数据时,分页查询是必不可少的功能 。MyBatis-Plus 提供了便捷的分页插件,让分页查询变得轻而易举 。
要使用分页功能,首先需要配置分页插件 。在 Spring Boot 项目中,可以通过创建一个配置类来实现:
java
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件,这里以MySQL数据库为例
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
在这个配置类中,我们创建了一个MybatisPlusInterceptor拦截器,并向其中添加了PaginationInnerInterceptor分页插件 。这样,MyBatis-Plus 就会自动在 SQL 语句中添加分页相关的逻辑 。
配置好分页插件后,就可以在代码中使用分页查询了 。例如,要查询第 2 页,每页显示 10 条用户数据 :
java
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testPage() {
// 构建分页对象,参数分别为当前页码和每页显示条数
Page<User> page = new Page<>(2, 10);
IPage<User> userPage = userMapper.selectPage(page, null);
// 获取当前页的数据列表
userPage.getRecords().forEach(System.out::println);
// 获取总记录数
System.out.println("总记录数:" + userPage.getTotal());
// 获取总页数
System.out.println("总页数:" + userPage.getPages());
}
}
在这段代码中,我们创建了一个Page对象,传入当前页码和每页显示条数 。然后调用userMapper\.selectPage方法,传入Page对象和查询条件(这里为null,表示查询所有数据) 。selectPage方法会返回一个IPage对象,通过它我们可以获取当前页的数据列表、总记录数和总页数等信息 。
通过 MyBatis-Plus 的分页插件,我们无需手动编写分页 SQL 语句,也无需关心不同数据库的分页语法差异,就能轻松实现高效的分页查询功能 。
(三)逻辑删除
在实际应用中,有时我们并不希望真正删除数据库中的数据,而是将其标记为已删除状态,以便在需要时进行数据回溯和统计分析 。这就是逻辑删除的作用 。
逻辑删除是指在数据库表中添加一个逻辑删除字段(通常为一个布尔类型、整数类型或枚举类型的字段),用来表示数据是否被删除 。当执行删除操作时,实际上是将该字段的值更新为表示已删除的状态,而不是真正从数据库中删除数据 。
在 MyBatis-Plus 中配置和使用逻辑删除非常简单 。首先,在数据库表中添加逻辑删除字段,例如deleted字段,类型为int,默认值为0,表示未删除;值为1表示已删除 。
然后,在实体类中添加对应的逻辑删除属性,并使用@TableLogic注解标识 :
java
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
// 逻辑删除字段
@TableLogic
private Integer deleted;
}
最后,在配置文件中进行全局逻辑删除配置(也可以在@TableLogic注解中单独配置) :
yaml
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值
logic-not-delete-value: 0 # 逻辑未删除值
完成上述配置后,MyBatis-Plus 会自动在查询和删除操作中处理逻辑删除 。例如,当执行userMapper\.deleteById\(id\)方法时,实际上执行的是更新操作,将deleted字段的值更新为1,而不是真正删除数据 。在查询时,MyBatis-Plus 会自动在 SQL 语句中添加deleted = 0的条件,只查询未被逻辑删除的数据 。
通过逻辑删除,我们可以有效地避免数据误删,同时满足业务对数据历史记录的需求 。
(四)自动填充
在数据库表设计中,通常会有一些公共字段,如创建时间、更新时间、创建人、更新人等 。每次插入或更新数据时,都需要手动为这些字段赋值,这不仅繁琐,还容易出错 。MyBatis-Plus 的自动填充功能可以很好地解决这个问题 。
自动填充功能允许我们在插入或更新数据时,自动为某些字段填充指定的值 。要使用自动填充功能,首先需要在实体类的字段上添加@TableField注解,并设置fill属性来指定填充时机 。例如,对于创建时间字段createTime,在插入数据时自动填充当前时间;对于更新时间字段updateTime,在更新数据时自动填充当前时间 :
java
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
// 创建时间,插入时自动填充
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
// 更新时间,更新时自动填充
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
}
然后,创建一个实现MetaObjectHandler接口的类,来实现自动填充的逻辑 :
java
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("开始插入填充...");
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("开始更新填充...");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
在这个类中,insertFill方法用于在插入数据时填充字段,updateFill方法用于在更新数据时填充字段 。strictInsertFill和strictUpdateFill方法是 MyBatis-Plus 提供的严格填充方法,确保填充字段在实体类中存在,若不存在则抛出异常 。
通过自动填充功能,我们可以大大简化代码,提高开发效率,同时保证数据的一致性和准确性 。
(五)乐观锁
在高并发环境下,当多个线程同时对数据库中的数据进行更新操作时,可能会出现数据更新冲突的问题 。例如,线程 A 和线程 B 同时读取了数据库中的一条数据,然后线程 A 对数据进行了更新并提交,接着线程 B 也对数据进行了更新并提交,由于线程 B 在更新时并不知道线程 A 已经对数据进行了修改,所以线程 B 的更新操作会覆盖线程 A 的更新结果,导致数据不一致 。
乐观锁是一种解决并发更新问题的机制,它假设在大多数情况下,并发操作不会发生冲突,只有在提交更新时才检查数据是否被其他事务修改过 。如果数据没有被修改过,则更新操作成功;如果数据已经被修改过,则更新操作失败,需要重新读取数据并进行更新 。
在 MyBatis-Plus 中配置和使用乐观锁也很简单 。首先,在实体类中添加一个版本号字段,例如version字段,并使用@Version注解标识 :
java
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
// 乐观锁版本号字段
@Version
private Integer version;
}
然后,在配置类中注册乐观锁插件 :
java
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
完成上述配置后,MyBatis-Plus 会在更新数据时自动检查版本号 。例如,当执行userMapper\.updateById\(user\)方法时,MyBatis-Plus 会生成类似于以下的 SQL 语句:
sql
UPDATE user SET name = '新名字', age = 25, version = version + 1 WHERE id = 1 AND version = 1;
在这个 SQL 语句中,version = 1用于确保数据在更新时未被其他事务修改 。如果当前数据的version值不是1,则更新操作不会成功,返回值为0 。
通过乐观锁机制,我们可以有效地避免高并发环境下的数据更新冲突问题,确保数据的一致性和完整性 。
五、实战案例:用户管理系统
(一)需求分析
为了更直观地展示 MyBatis-Plus 在实际项目中的应用,我们以一个简单的用户管理系统为例进行实战。这个用户管理系统需要具备以下基本功能:
-
用户注册:用户可以在系统中填写用户名、密码、邮箱等信息进行注册,系统会对用户输入的信息进行校验,确保信息的合法性和完整性,同时要检查用户名是否已被注册 。
-
用户登录:用户输入已注册的用户名和密码进行登录验证,系统会在数据库中查询对应的用户信息,验证密码是否正确,如果验证通过则允许用户登录 。
-
用户查询:支持根据用户 ID 查询单个用户的详细信息,也可以根据用户名进行模糊查询,获取符合条件的用户列表 。此外,还需要支持分页查询,以便在处理大量用户数据时,能够高效地展示数据 。
-
用户修改:已登录的用户可以修改自己的个人信息,如密码、邮箱等。管理员用户则可以修改任意用户的信息,包括用户名、角色等 。在修改时,要确保数据的一致性和安全性,例如修改密码时需要进行密码强度校验 。
-
用户删除:管理员用户有权限删除指定的用户。为了避免数据的误删,采用逻辑删除的方式,即只是将用户数据在数据库中标记为已删除,而不是真正从数据库中删除 。
(二)数据库设计
根据上述需求,我们设计一个名为user的用户表,其结构如下:
sql
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-正常',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除:0-未删除,1-已删除',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-
id:用户的唯一标识,使用自增长的bigint类型,确保每个用户都有一个唯一的 ID 。 -
username:用户名,varchar类型,长度为 50,不能为空,并且设置了唯一索引,保证用户名的唯一性 。 -
password:用户密码,varchar类型,长度为 100,用于存储加密后的用户密码,保障用户信息安全 。 -
email:用户邮箱,varchar类型,长度为 100,可为空,用于找回密码、接收系统通知等 。 -
phone:用户手机号,varchar类型,长度为 20,可为空,用于短信验证、账号绑定等 。 -
status:用户状态,tinyint类型,0 表示禁用,1 表示正常,用于控制用户是否能够登录系统 。 -
create\_time:用户创建时间,datetime类型,默认值为当前时间,记录用户注册的时间 。 -
update\_time:用户信息更新时间,datetime类型,默认值为当前时间,并且在数据更新时自动更新为当前时间,用于记录用户信息的最后修改时间 。 -
deleted:逻辑删除字段,tinyint类型,0 表示未删除,1 表示已删除,通过这个字段实现逻辑删除功能 。
(三)代码实现
接下来,我们结合 MyBatis-Plus 的功能,逐步实现用户管理系统的各个功能模块 。
1. 实体类 :首先创建User实体类,用于映射数据库中的user表 。使用 Lombok 的@Data注解简化代码,通过@TableName注解指定实体类对应的数据库表名,使用@TableId注解标识主键字段,并指定主键生成策略为自增 。同时,添加@TableField注解实现自动填充功能,使用@TableLogic注解实现逻辑删除功能 。
java
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private String email;
private String phone;
private Integer status;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableLogic
private Integer deleted;
}
2. Mapper 接口 :创建UserMapper接口,继承BaseMapper\<User\>,这样就自动拥有了单表的大部分 CRUD 方法 。
java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
3. Service 层 :创建UserService接口及其实现类UserServiceImpl 。在UserService接口中定义用户管理的业务方法,如注册、登录、查询、修改、删除等 。在UserServiceImpl中实现这些方法,通过调用UserMapper来操作数据库 。
UserService接口:
java
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.User;
public interface UserService extends IService<User> {
// 用户注册
boolean register(User user);
// 用户登录
User login(String username, String password);
// 根据ID查询用户
User getUserById(Long id);
// 根据用户名模糊查询用户列表
List<User> listUsersByUsername(String username);
// 更新用户信息
boolean updateUser(User user);
// 删除用户(逻辑删除)
boolean deleteUser(Long id);
}
UserServiceImpl实现类:
java
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public boolean register(User user) {
// 检查用户名是否已存在
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername, user.getUsername());
if (count(wrapper) > 0) {
return false;
}
// 对密码进行加密
String encryptedPassword = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
user.setPassword(encryptedPassword);
// 插入用户数据
return save(user);
}
@Override
public User login(String username, String password) {
// 对密码进行加密
String encryptedPassword = DigestUtils.md5DigestAsHex(password.getBytes());
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername, username)
.eq(User::getPassword, encryptedPassword)
.eq(User::getDeleted, 0);
return getOne(wrapper);
}
@Override
public User getUserById(Long id) {
return getById(id);
}
@Override
public List<User> listUsersByUsername(String username) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(User::getUsername, username)
.eq(User::getDeleted, 0);
return list(wrapper);
}
@Override
public boolean updateUser(User user) {
return updateById(user);
}
@Override
public boolean deleteUser(Long id) {
return removeById(id);
}
}
4. Controller 层 :创建UserController,用于处理 HTTP 请求,调用UserService的方法来实现用户管理的功能 。通过@RestController注解将该类标记为 RESTful 风格的控制器,使用@RequestMapping注解映射请求路径 。
java
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
// 用户注册
@PostMapping("/register")
public boolean register(@RequestBody User user) {
return userService.register(user);
}
// 用户登录
@PostMapping("/login")
public User login(@RequestParam String username, @RequestParam String password) {
return userService.login(username, password);
}
// 根据ID查询用户
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
// 根据用户名模糊查询用户列表
@GetMapping("/list")
public List<User> listUsersByUsername(@RequestParam String username) {
return userService.listUsersByUsername(username);
}
// 更新用户信息
@PutMapping
public boolean updateUser(@RequestBody User user) {
return userService.updateUser(user);
}
// 删除用户(逻辑删除)
@DeleteMapping("/{id}")
public boolean deleteUser(@PathVariable Long id) {
return userService.deleteUser(id);
}
// 分页查询用户列表
@GetMapping("/page")
public Page<User> pageUsers(@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size) {
Page<User> page = new Page<>(current, size);
return userService.page(page);
}
}
通过以上代码,我们利用 MyBatis-Plus 的强大功能,实现了一个简单但功能完整的用户管理系统 。这个系统涵盖了用户注册、登录、查询、修改和删除等核心功能,并且充分展示了 MyBatis-Plus 在简化数据库操作方面的优势 。无论是基础的 CRUD 操作,还是复杂的条件查询和分页功能,MyBatis-Plus 都能轻松应对,让开发变得更加高效和便捷 。