Mybatis-Plus 查询操作详解
Mybatis-Plus 提供了丰富的查询操作,可以方便地进行单表的 CRUD 操作。以下是一些常用的查询操作:
1. 根据主键查询
使用 BaseMapper 提供的 selectById 方法可以根据主键查询记录,例如:
User user = userMapper.selectById(1);
2. 条件查询
2.1 使用 Lambda 表达式
Mybatis-Plus 支持使用 Lambda 表达式进行条件查询,例如:
List<User> userList = userMapper.selectList(Wrappers.<User>lambdaQuery()
.eq(User::getAge, 20)
.like(User::getName, "Tom"));
2.2 使用 QueryWrapper
也可以使用 QueryWrapper 进行条件查询,例如:
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 20).like("name", "Tom");
List<User> userList = userMapper.selectList(queryWrapper);
3. 分页查询
Mybatis-Plus 提供了分页插件,可以方便地进行分页查询,例如:
Page<User> page = new Page<>(1, 10); // 第一页,每页10条记录
IPage<User> userPage = userMapper.selectPage(page, Wrappers.<User>lambdaQuery().eq(User::getAge, 20));
List<User> userList = userPage.getRecords();
4. 自定义 SQL 查询
除了以上方法,还可以使用 @Select 注解或者 XML 文件中的 SQL 进行自定义查询,例如:
@Select("SELECT * FROM user WHERE age = #{age}")
List<User> selectByAge(@Param("age") Integer age);
其余见官方文档https://baomidou.com/guides/data-interface/#select
分页插件
MyBatis-Plus 的分页插件 PaginationInnerInterceptor 提供了强大的分页功能,支持多种数据库,使得分页查询变得简单高效。
配置方法
在 Spring Boot 项目中,你可以通过 Java 配置来添加分页插件:
@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
return interceptor;
}
}自定义 Mapper 方法中使用分页
你可以通过以下方式在 Mapper 方法中使用分页:
IPage<UserVo> selectPageVo(IPage<?> page, Integer state);
// 或者自定义分页类
MyPage selectPageVo(MyPage page);
// 或者返回 List
List<UserVo> selectPageVo(IPage<UserVo> page, Integer state);对应的 XML 配置:
<select id="selectPageVo" resultType="xxx.xxx.xxx.UserVo">
SELECT id,name FROM user WHERE state=#{state}
</select>如果返回类型是 IPage,则入参的 IPage 不能为 null。如果想临时不分页,可以在初始化 IPage 时 size 参数传入小于 0 的值。 如果返回类型是 List,则入参的 IPage 可以为 null,但需要手动设置入参的 IPage.setRecords(返回的 List)。 如果 XML 需要从 page 里取值,需要使用
page.属性获取。
其他注意事项
生成 countSql 时,如果 left join 的表不参与 where 条件,会将其优化掉。建议在任何带有 left join 的 SQL 中,都给表和字段加上别名。
在使用多个插件时,请将分页插件放到插件执行链的最后面,以避免 COUNT SQL 执行不准确的问题。
Page 类
Page 类继承了 IPage 类,实现了简单分页模型。如果你需要实现自己的分页模型,可以继承 Page 类或实现 IPage 类。
通过这些配置和使用方法,你可以轻松地在 MyBatis-Plus 中实现分页查询,提高应用的性能和用户体验。
Mybatis-Plus 删除操作详解
Mybatis-Plus 提供了丰富的删除操作,可以方便地进行单表的删除操作。以下是一些常用的删除操作:
1. 根据主键删除
使用 BaseMapper 提供的 deleteById 方法可以根据主键删除记录,例如:
int result = userMapper.deleteById(1);
2. 条件删除
2.1 使用 Lambda 表达式
Mybatis-Plus 支持使用 Lambda 表达式进行条件删除,例如:
int result = userMapper.delete(Wrappers.<User>lambdaQuery()
.eq(User::getAge, 20)
.like(User::getName, "Tom"));
2.2 使用 QueryWrapper
也可以使用 QueryWrapper 进行条件删除,例如:
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 20).like("name", "Tom");
int result = userMapper.delete(queryWrapper);
3. 物理删除与逻辑删除
Mybatis-Plus 支持物理删除和逻辑删除。通过在实体类中使用 @TableLogic 注解,可以实现逻辑删除。在进行删除操作时,Mybatis-Plus 会自动判断是否需要进行逻辑删除。
4. 自定义 SQL 删除
除了以上方法,还可以使用 @Delete 注解或者 XML 文件中的 SQL 进行自定义删除,例如:
@Delete("DELETE FROM user WHERE age = #{age}")
int deleteByAge(@Param("age") Integer age);
详见官方文档https://baomidou.com/guides/data-interface/#delete
逻辑删除
逻辑删除是指在数据库中不直接删除数据记录,而是通过标记某个字段的值来表示该记录已被删除。在 Mybatis-Plus 中,可以通过使用 @TableLogic 注解来实现逻辑删除。
逻辑删除的工作原理
MyBatis-Plus 的逻辑删除功能会在执行数据库操作时自动处理逻辑删除字段。以下是它的工作方式:
插入:逻辑删除字段的值不受限制。
查找:自动添加条件,过滤掉标记为已删除的记录。
更新:防止更新已删除的记录。
删除:将删除操作转换为更新操作,标记记录为已删除。
例如:
删除:
update user set deleted=1 where id = 1 and deleted=0查找:
select id,name,deleted from user where deleted=0
支持的数据类型
逻辑删除字段支持所有数据类型,但推荐使用 Integer、Boolean 或 LocalDateTime。如果使用 datetime 类型,可以配置逻辑未删除值为 null,已删除值可以使用函数如 now() 来获取当前时间。
使用方法
步骤 1: 配置全局逻辑删除属性
在 application.yml 中配置 MyBatis-Plus 的全局逻辑删除属性:
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除字段名
logic-delete-value: 1 # 逻辑已删除值
logic-not-delete-value: 0 # 逻辑未删除值步骤 2: 在实体类中使用 @TableLogic 注解
在实体类中,对应数据库表的逻辑删除字段上添加 @TableLogic 注解:
import com.baomidou.mybatisplus.annotation.TableLogic;
public class User {
// 其他字段...
@TableLogic
private Integer deleted;
}1. 如何处理插入操作?
方法一:在数据库中为逻辑删除字段设置默认值。
方法二:在插入数据前手动设置逻辑删除字段的值。
方法三:使用 MyBatis-Plus 的自动填充功能。
2. 删除接口自动填充功能失效怎么办?
方法一:使用
deleteById方法。方法二:使用
update方法,并使用UpdateWrapper.set(column, value)。方法三:使用
update方法,并使用UpdateWrapper.setSql("column=value")。
SQL分析与打印
MyBatis-Plus提供了SQL分析与打印的功能,通过集成p6spy组件,可以方便地输出SQL语句及其执行时长。本功能适用于MyBatis-Plus 3.1.0及以上版本。
p6spy简介
p6spy 是一个针对数据库访问进行拦截和记录的工具,它通过代理JDBC驱动程序来工作。这意味着你的应用程序可以像往常一样使用JDBC,而p6spy会在幕后记录所有的SQL语句及其执行时间。这对于开发和调试过程中的SQL优化非常有用。
p6spy不仅限于记录SQL日志,它还提供了一些高级功能,如:
慢SQL检测:通过配置
outagedetection和outagedetectioninterval,p6spy可以记录执行时间超过设定阈值的SQL语句。自定义日志格式:通过
logMessageFormat,你可以自定义SQL日志的输出格式,包括时间戳、执行时间、SQL语句等。日志输出控制:
appender配置项允许你选择日志输出到控制台、文件或日志系统。
p6spy是一个强大的工具,它为MyBatis-Plus用户提供了便捷的SQL分析与打印功能。通过合理配置,你可以在开发和测试阶段有效地监控和优化SQL语句。
依赖引入
<dependency>
<groupId>com.github.gavlyukovskiy</groupId>
<artifactId>p6spy-spring-boot-starter</artifactId>
<version>1.9.2</version>
</dependency>decorator:
datasource:
p6spy:
# 日志格式
log-format: "\ntime:%(executionTime) || sql:%(sql)\n"
# 自定义日志类
logging: custom
custom-appender-class: com.example.testinit.config.StdoutLoggerpublic class StdoutLogger extends com.p6spy.engine.spy.appender.StdoutLogger {
public void logText(String text) {
System.err.println(text);
}
}注意事项
driver-class-name应配置为p6spy提供的驱动类。url前缀应为jdbc:p6spy,后跟实际的数据库连接地址。如果打印的SQL为
null,请在excludecategories中增加commit。如果批量操作不打印SQL,请去除
excludecategories中的batch。对于批量操作打印重复的问题,请使用
MybatisPlusLogFactory(3.2.1新增)。请注意,该插件可能会带来性能损耗,不建议在生产环境中使用。
条件构造器详解
条件构造器是 Mybatis-Plus 中用于构建查询条件的重要工具,可以帮助用户灵活地构造复杂的查询条件。以下是关于条件构造器的详细解释:
主要的 Wrapper 类及其功能:
AbstractWrapper:这是一个抽象基类,提供了所有 Wrapper 类共有的方法和属性。它定义了条件构造的基本逻辑,包括字段(column)、值(value)、操作符(condition)等。所有的 QueryWrapper、UpdateWrapper、LambdaQueryWrapper 和 LambdaUpdateWrapper 都继承自 AbstractWrapper。
QueryWrapper:专门用于构造查询条件,支持基本的等于、不等于、大于、小于等各种常见操作。它允许你以链式调用的方式添加多个查询条件,并且可以组合使用
and和or逻辑。UpdateWrapper:用于构造更新条件,可以在更新数据时指定条件。与 QueryWrapper 类似,它也支持链式调用和逻辑组合。使用 UpdateWrapper 可以在不创建实体对象的情况下,直接设置更新字段和条件。
LambdaQueryWrapper:这是一个基于 Lambda 表达式的查询条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名。这种方式提高了代码的可读性和可维护性,尤其是在字段名可能发生变化的情况下。
LambdaUpdateWrapper:类似于 LambdaQueryWrapper,LambdaUpdateWrapper 是基于 Lambda 表达式的更新条件构造器。它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题。
// 使用 QueryWrapper 构造查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 20).like("name", "Tom");
List<User> userList = userMapper.selectList(queryWrapper);
// 使用 UpdateWrapper 构造更新条件
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("age", 30).eq("name", "Tom");
int result = userMapper.update(null, updateWrapper);
// 使用 LambdaQueryWrapper 构造查询条件
List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>()
.eq(User::getAge, 20)
.like(User::getName, "Tom"));
// 使用 LambdaUpdateWrapper 构造更新条件
int result = userMapper.update(null, new LambdaUpdateWrapper<User>()
.set(User::getAge, 30)
.eq(User::getName, "Tom"));
常用方法
条件构造器提供了丰富的方法来构建查询条件,常用的方法包括:
eq:等于ne:不等于gt:大于ge:大于等于lt:小于le:小于等于like:模糊查询in:包含在某个集合中notIn:不包含在某个集合中isNull:字段为 NULLisNotNull:字段不为 NULLorderBy:排序
复杂条件构造
条件构造器还支持复杂的条件构造,可以通过 and、or 等方法组合多个条件。例如:
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 20)
.and(i -> i.like("name", "Tom").or().like("name", "Jerry"));
List<User> userList = userMapper.selectList(queryWrapper);
在上述代码中,通过 and 方法组合了年龄为 20 且姓名包含 "Tom" 或 "Jerry" 的条件。
详见官方文档条件构造器 | MyBatis-Plus (baomidou.com)
代码生成器
安装
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.7</version>
</dependency>由于代码生成器用到了模板引擎,请自行引入您喜好的模板引擎。MyBatis-Plus Generator 支持如下模板引擎:
VelocityTemplateEngine(Default)
FreemarkerTemplateEngine
BeetlTemplateEngine
EnjoyTemplateEngine
如果您还想使用或适配其他模板引擎,可自行继承 AbstractTemplateEngine 并参考其他模板引擎实现自定义。
生成方式
代码生成器目前支持两种生成方式:
DefaultQuery (元数据查询)
优点: 根据通用接口读取数据库元数据相关信息,对数据库通用性较好。
缺点: 依赖数据库厂商驱动实现。
备注: 默认方式,部分类型处理可能不理想。
SQLQuery (SQL查询)
优点: 需要根据数据库编写对应表、主键、字段获取等查询语句。
缺点: 通用性不强,同数据库厂商不同版本可能会存在兼容问题(例如,H2数据库只支持1.X版本)。
备注: 后期不再维护。
快速生成
在 CodeGenerator 中的 main 方法中直接添加生成器代码,并进行相关配置,然后直接运行即可生成代码。
public static void main(String[] args) {
FastAutoGenerator.create("url", "username", "password")
.globalConfig(builder -> {
builder.author("baomidou") // 设置作者
.enableSwagger() // 开启 swagger 模式
.outputDir("D://"); // 指定输出目录
})
.dataSourceConfig(builder ->
builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.SMALLINT) {
// 自定义类型转换
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
})
)
.packageConfig(builder ->
builder.parent("com.baomidou.mybatisplus.samples.generator") // 设置父包名
.moduleName("system") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "D://")) // 设置mapperXml生成路径
)
.strategyConfig(builder ->
builder.addInclude("t_simple") // 设置需要生成的表名
.addTablePrefix("t_", "c_") // 设置过滤表前缀
)
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}交互式生成
交互式生成在运行之后,会提示您输入相应的内容,等待配置输入完整之后就自动生成相关代码。
public static void main(String[] args) {
FastAutoGenerator.create("url", "username", "password")
// 全局配置
.globalConfig((scanner, builder) -> builder.author(scanner.apply("请输入作者名称?")))
// 包配置
.packageConfig((scanner, builder) -> builder.parent(scanner.apply("请输入包名?")))
// 策略配置
.strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all")))
.entityBuilder()
.enableLombok()
.addTableFills(
new Column("create_time", FieldFill.INSERT)
)
.build())
// 使用Freemarker引擎模板,默认的是Velocity引擎模板
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
// 处理 all 情况
protected static List<String> getTables(String tables) {
return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
}Mybatis-Plus 基础(二)
https://blog.mykele.asia/archives/mybatis-plus-ji-chu-er
Comments