Java项目开发中普遍使用Mybatis,它是是一款优秀的持久层框架,可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO为数据库中的记录。但原生的Mybatis在业务开发时需要手动创建数据库表映射类(POJO)、Mapper、xml,并且完全需要手动编写xml中的sql逻辑,会给研发无形中带来很多工作量。本次分享Mybatis相关工具以提高开发效率。
0、创建mysql库表
数据库db:testdb,
table:t_employee,
建表DDL:
CREATE TABLE `t_employee` ( `id` bigint NOT NULL AUTO_INCREMENT, `employee_name` varchar ( 100 ) NOT NULL , `age` int NOT NULL COMMENT '员工年龄' , `height` decimal ( 10 , 2 ) NOT NULL COMMENT '身高,单位cm' , `employee_no` varchar ( 100 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '工号' , `state` tinyint( 1 ) NOT NULL DEFAULT '1' COMMENT '生效状态 0-失效 1-生效' , `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' , `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' , PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
1、Mybatis-Genarator
Mybatis-Generator是 MyBatis 提供的一个代码生成工具,可以帮助我们生成数据库表对应的持久化对象(Model)、操作数据库的接口(dao)、简单 SQL 的 mapper。(官方地址:mybatis.org/generator/i…
- 提高开发效率:开发人员无需手动编写大量重复的数据库访问代码,节省了大量时间,能够将更多精力投入到核心业务逻辑的实现上。
- 减少人为错误:自动生成的代码遵循统一的规范和格式,相比手动编写,大大降低了因人为疏忽导致的语法错误、逻辑错误等问题。
- 易于维护:当数据库表结构发生变化时,只需重新运行 MBG,即可快速更新对应的 Java 实体类、Mapper 接口和 XML 映射文件,保证代码与数据库的一致性,减少维护成本。
- 规范代码风格:在团队开发中,MBG 生成的代码风格统一,有助于提高代码的可读性和可维护性,减少因个人代码风格差异带来的沟通成本和潜在问题。
1.1 引入Generator
在spring-boot项目pom中引入MBG相关依赖
<!-- MyBatis Generator核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- 数据库驱动依赖,这里以MySQL为例 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
maven插件,以此可以使用maven命令行来运行generator插件
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.lwk.demo.mbg.MbgApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.1</version>
<configuration>
<!-- 这里需要配置运行配置的路径-->
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite> <!-- 是否覆盖旧文件 -->
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
MyBatis-Generator 需要定义一个 xml 配置文件,来声明生成代码时的各项配置,例如上边定义在src/main/resources/generatorConfig.xml下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 一个数据库一个context,targetRuntime除此之外还有 MyBatis3Simple -->
<context id="MyBatis3Context" targetRuntime="MyBatis3">
<!-- 可配置一些增强插件 -->
<!--为实体类生成 toString() 方法,包含所有字段信息 -->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<!--生成 equals() 和 hashCode() 方法,基于主键字段实现 -->
<plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"/>
<!--使实体类实现 java.io.Serializable 接口,支持对象序列化 -->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<!--为 Mapper 接口添加支持分页的方法,如 selectByExampleWithRowbounds()-->
<plugin type="org.mybatis.generator.plugins.RowBoundsPlugin"/>
<!-- 去除注释与时间戳 -->
<commentGenerator>
<!-- 不生成所有注释,默认为 false -->
<property name="suppressAllComments" value="true" />
<!-- 生成的注释中不包含时间信息,默认为 false -->
<property name="suppressDate" value="true" />
<!-- 是否添加数据库表中字段的注释,默认为 false -->
<property name="addRemarkComments" value="true" />
</commentGenerator>
<!-- JDBC连接 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/testdb?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&serverTimezone=Asia/Shanghai"
userId="root"
password="1234567890">
<!--只读取testdb下的表,否则会读取到mysql的user表,8.0以上驱动需配置 -->
<property name="nullCatalogMeansCurrent" value="true"/>
</jdbcConnection>
<!-- mysql到Java中的类型映射,如果不配置,则使用默认的转换规则-->
<javaTypeResolver>
<!-- 是否强制使用 BigDecimal;默认为 false,把 mysql中DECIMAL和NUMERIC类型映射为Integer;设置为true时,DECIMAL和NUMERIC类型映射为java.math.BigDecimal-->
<property name="forceBigDecimals" value="true"/>
<!--默认false
false,将所有 JDBC 的时间类型解析为 java.util.Date
true,将 JDBC 的时间类型按如下规则解析
DATE -> java.time.LocalDate
TIME -> java.time.LocalTime
TIMESTAMP -> java.time.LocalDateTime
TIME_WITH_TIMEZONE -> java.time.OffsetTime
TIMESTAMP_WITH_TIMEZONE -> java.time.OffsetDateTime
-->
<property name="useJSR310Types" value="false"/>
</javaTypeResolver>
<!-- 实体类路径 -->
<javaModelGenerator targetPackage="com.lwk.demo.mbg.entity" targetProject="src/main/java">
<!-- 是否让schema作为包的后缀,默认为false -->
<property name="enableSubPackages" value="true"/>
<!-- 设置在 getter 方法中,是否对 String 类型的字段调用 trim() 方法;默认为 false -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- XML 文件路径 -->
<sqlMapGenerator targetPackage="testdb" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- Mapper 接口路径 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.lwk.demo.mbg.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 生成表的配置 -->
<table tableName="t_employee" domainObjectName="User" >
<!-- 若表中有text类型,则可以将映射为string类型,默认会生成xxxWithBLOBs类 -->
<!-- <columnOverride column="comment" jdbcType="VARCHAR" />-->
<!-- tinyint(1)默认映射为Boolean类型,tinyint(4)默认映射Byte类型,若需要则可以修改Integer -->
<!-- <columnOverride column="state" jdbcType="Integer" />-->
<!-- 是否生成Example类,用于条件查询 -->
<!--<property name="generateExampleClass" value="true"/>-->
<!-- 是否生成带有BLOB字段的实体类 -->
<!--<property name="useActualColumnNames" value="false"/>-->
</table>
</context>
</generatorConfiguration>
-
<properties>标签用于引入外部属性文件,如generator.properties,在该文件中可以配置数据库连接的相关信息,如jdbc.driver=com.mysql.cj.jdbc.Driver、jdbc.url=jdbc:mysql://``localhost:3306/mydb、jdbc.username=root、jdbc.password=123456,这样可以使配置文件更加简洁,同时方便在不同环境下切换数据库连接配置。 -
<context>标签定义了一个代码生成的上下文环境,id属性用于唯一标识该上下文,targetRuntime属性指定了生成的代码所遵循的 MyBatis 运行时版本,上面使用MyBatis3,此外还有Mybatis3Simple等。 -
<commentGenerator>标签用于配置注释生成器的相关属性,如是否抑制所有注释、注释中日期的格式等。 -
<jdbcConnection>标签配置数据库连接信息,通过引用外部属性文件中的值来设置数据库驱动类、连接 URL、用户名和密码。 -
<javaTypeResolver>标签用于配置 Java 类型解析器,forceBigDecimals属性设置是否强制将数据库中的 DECIMAL 和 NUMERIC 类型映射为 Java 的 BigDecimal 类型,false表示按照默认规则进行映射。 -
<javaModelGenerator>标签指定生成的 Java 实体类的包名和输出目录,enableSubPackages属性表示是否根据数据库表的 schema 生成子包,trimStrings属性表示是否在生成的实体类中对字符串类型的属性进行去空格处理。 -
<sqlMapGenerator>标签指定生成的 SQL 映射文件的包名和输出目录,同样支持enableSubPackages属性。 -
<javaClientGenerator>标签指定生成的 Mapper 接口的包名和输出目录,type属性指定生成的 Mapper 接口的类型,XMLMAPPER表示生成基于 XML 映射的 Mapper 接口。 -
<table>标签用于配置要生成代码的具体数据库表,tableName属性指定数据库中的表名,domainObjectName属性指定生成的实体类的名称,通过配置<property>子标签可以进一步控制生成代码的一些特性,如是否生成带有 BLOB 字段的实体类、是否生成 Example 类等。
1.2 运行Generator
引入generator插件后,maven管理会显示mybatis-generator插件,点击运行
或者执行命令mvn mybatis-generator:generate,运行完成后会生成实体类(Model)、mapper类和xml文件,结构如下
直接使用即可
public class EmployeeDao {
@Autowired
private EmployeeModelMapper employeeModelMapper;
public void add(EmployeeModel employeeModel) {
employeeModelMapper.insert(employeeModel);
}
public void delete(Long id) {
employeeModelMapper.deleteByPrimaryKey(id);
}
public void updateById(EmployeeModel employeeModel) {
employeeModelMapper.updateByPrimaryKeySelective(employeeModel);
}
public void updateBySelective() {
EmployeeModelExample example = new EmployeeModelExample();
Criteria criteria = example.createCriteria();
criteria.andAgeBetween(40, 45);
EmployeeModel update = new EmployeeModel();
update.setState(false);
employeeModelMapper.updateByExampleSelective(update, example);
}
public EmployeeModel queryById(Long id) {
return employeeModelMapper.selectByPrimaryKey(id);
}
public List<EmployeeModel> query(String employeeName, List<Integer> ageList, boolean state) {
EmployeeModelExample example = new EmployeeModelExample();
Criteria criteria = example.createCriteria();
if (!StringUtils.isEmpty(employeeName)) {
criteria.andEmployeeNameLike(employeeName);
}
criteria.andAgeIn(ageList);
criteria.andStateEqualTo(state);
example.setOrderByClause("id desc");
return employeeModelMapper.selectByExample(example);
}
}
根据generator自动生成代码列出了常用的简单增删改查操作,但同时也缺少一些常用的方法,如批量插入、upsert操作、分页等功能,因此需要借助第三方插件来实现。
1.3 第三方generator plugin
官方自带的generator插件功能依然有限,我们可以引入第三方插件功能实现功能增强,这里推荐 itfsw/mybatis-generator-plugin。(github:github.com/itfsw/mybat…
pom中引入相关jar包和插件配置
<dependency>
<groupId>com.itfsw</groupId>
<artifactId>mybatis-generator-plugin</artifactId>
<version>1.3.10</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite> <!-- 是否覆盖旧文件 -->
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>com.itfsw</groupId>
<artifactId>mybatis-generator-plugin</artifactId>
<version>1.3.10</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
generation中可以加入自己期望的插件,这里暂时只引入了分页插件、builder、lombok、batchInsert和upsert插件
<!-- 分页插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.LimitPlugin">
<!-- 通过配置startPage影响Example中的page方法开始分页的页码,默认分页从0开始 -->
<property name="startPage" value="0" />
</plugin>
<!-- 官方自带的分页插件 -->
<plugin type="org.mybatis.generator.plugins.RowBoundsPlugin" />
<!-- 数据Model构建插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.ModelBuilderPlugin" />
<!-- 批量插入插件 以下两个插件需配合使用 实现类似于insertSelective插入列-->
<plugin type="com.itfsw.mybatis.generator.plugins.ModelColumnPlugin" />
<plugin type="com.itfsw.mybatis.generator.plugins.BatchInsertPlugin" />
<!-- 存在即更新插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.UpsertPlugin" />
<!-- Lombok插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.LombokPlugin">
<!-- @Data 默认开启,同时插件会对子类自动附加@EqualsAndHashCode(callSuper = true),@ToString(callSuper = true) -->
<property name="@Data" value="true" />
<property name="@Builder" value="false" />
<property name="@AllArgsConstructor" value="false" />
<property name="@NoArgsConstructor" value="false" />
</plugin>
依然是原来的表,为了和之前的区分开,这里改了个实体类名字User,运行generator后生成文件
项目中可使用这些方法
public void batchAdd(List<User> userList) {
userMapper.batchInsert(userList);
}
public void upsert(User user) {
userMapper.upsert(user);
}
public List<User> queryPage() {
UserExample example = new UserExample();
UserExample.Criteria criteria = example.createCriteria();
criteria.andAgeBetween(25, 35);
example.setOrderByClause("id desc");
// 查询前10条
example.limit(10);
// 10-20条记录
example.limit(10, 20);
// 第2页,每页10条
example.page(1, 10);
return userMapper.selectByExample(example);
}
以上MBG使用简洁方便,也不需要替换Mybatis框架,对于项目组件升级十分友好,但依然存在功能缺陷,例如无法应对分表时动态查询(user_2024, user_2025)等,依然需要手动修改mapper和xml文件,可以使用功能更强大的mybatis-plus。
2、Mybatis-Plus
MyBatis-Plus 是基于 MyBatis 框架的一个增强工具,主要目的是简化 MyBatis 的开发过程,提供更加简洁、方便的 CRUD 操作。它是在保留 MyBatis 强大功能的基础上,通过封装和优化一些常见操作来提高开发效率。(官方地址:baomidou.com/introduce/)
-
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
-
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
-
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
-
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
-
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
-
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
-
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
-
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
-
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
-
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
-
内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
-
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
2.1 引入Mybatis-Plus
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.12</version>
</dependency>
<!-- 代码自动生成插件 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
2.2 运行MBP
编写自动生成代码的执行器
package com.lwk.demo.mbp;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig.Builder;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.keywords.MySqlKeyWordsHandler;
import java.util.Collections;
public class CodeGenerator {
private static final String JDBC_URL = "jdbc:mysql://localhost:3306/testdb?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&serverTimezone=Asia/Shanghai";
private static final String USR_NAME = "root";
private static final String PASSWORD = "1234567890";
private static final String PACKAGE_NAME = "com.lwk.demo.mbp";
public static void main(String[] args) {
String projectPath = System.getProperty("user.dir");
Builder datasourceBuilder = new Builder(JDBC_URL, USR_NAME, PASSWORD)
.dbQuery(new MySqlQuery())
.typeConvert(new MySqlTypeConvert())
.keyWordsHandler(new MySqlKeyWordsHandler());
FastAutoGenerator fastAutoGenerator = FastAutoGenerator.create(datasourceBuilder);
fastAutoGenerator.globalConfig(builder -> builder
.disableOpenDir()
.author("")
.dateType(DateType.ONLY_DATE) // 日期设为Date
.outputDir(projectPath + "/src/main/java")); // 输出目录
// 包管理
fastAutoGenerator.packageConfig(builder ->
builder.parent(PACKAGE_NAME)
.entity("entity")
.mapper("mapper") // mapper包名
.service("repository") // service包名
.serviceImpl("repository.impl") // service实现类包名
// .controller("controller")
.pathInfo(Collections.singletonMap(OutputFile.xml, projectPath + "/src/main/resources/mapper"))); // xml文件path
// 表管理
fastAutoGenerator.strategyConfig(builder ->
builder.enableCapitalMode()
.enableSkipView()
.disableSqlFilter()
.addInclude("t_employee") // 目标表
.addTablePrefix("t_")); // 过滤表前缀;
// 实体类配置
fastAutoGenerator.strategyConfig(builder ->
builder.entityBuilder()
.enableChainModel()
.enableTableFieldAnnotation()
.naming(NamingStrategy.underline_to_camel)
.columnNaming(NamingStrategy.underline_to_camel)
.idType(IdType.AUTO)
.enableLombok()
.enableTableFieldAnnotation()
.enableFileOverride()
.formatFileName("%sEntity"));
// // controller配置
// fastAutoGenerator.strategyConfig(builder -> builder
// .controllerBuilder()
// .enableRestStyle());
// service配置
fastAutoGenerator.strategyConfig(builder -> builder.serviceBuilder()
.formatServiceFileName("I%sRepository")
.formatServiceImplFileName("%sRepositoryImpl"));
// mapper配置
fastAutoGenerator.strategyConfig(builder ->
builder.mapperBuilder()
.enableBaseResultMap() // 开启xml文件的BaseResultMap
.enableBaseColumnList() // 开启xml文件的BaseColumnList
.formatMapperFileName("%sMapper")
.formatXmlFileName("%sMapper"));
fastAutoGenerator.execute();
}
}
执行运行后,会自动生成代码结构:
暂时无法在飞书文档外展示此内容
暂时无法在飞书文档外展示此内容
暂时无法在飞书文档外展示此内容
暂时无法在飞书文档外展示此内容
暂时无法在飞书文档外展示此内容
2.3 CRUD用法
@Autowired
private IEmployeeRepository employeeRepository;
public boolean insert(EmployeeEntity entity) {
return employeeRepository.save(entity);
}
// 批量插入
public boolean batchAdd(List<EmployeeEntity> entityList, int partitionSize) {
return employeeRepository.saveBatch(entityList, partitionSize);
}
// 单条upsert
public boolean upsert(EmployeeEntity entity) {
return employeeRepository.saveOrUpdate(entity);
}
// 批量upsert
public boolean batchUpsert(List<EmployeeEntity> entityList, int partitionSize) {
return employeeRepository.saveOrUpdateBatch(entityList, partitionSize);
}
// 普通更新
public void update() {
// 1
UpdateWrapper<EmployeeEntity> wrapper = new UpdateWrapper<>();
wrapper.between("age", 30, 40).set("height", new BigDecimal(180));
employeeRepository.update(wrapper);
// 2
EmployeeEntity update = new EmployeeEntity();
update.setEmployeeName("ddf");
wrapper = new UpdateWrapper<>();
wrapper.eq("id", 10L);
employeeRepository.update(update, wrapper);
}
// lambda更新
public void lambdaUpdate() {
UpdateWrapper<EmployeeEntity> updateWrapper = new UpdateWrapper<>();
LambdaUpdateWrapper<EmployeeEntity> updateLambda = updateWrapper.lambda();
updateLambda.eq(EmployeeEntity::getEmployeeNo, "1123").set(EmployeeEntity::getAge, 34);
employeeRepository.update(updateWrapper);
}
// 普通查询
public List<EmployeeEntity> query() {
List<EmployeeEntity> result = new ArrayList<>();
// 1
result = employeeRepository.list();
// 2
QueryWrapper<EmployeeEntity> wrapper = new QueryWrapper<>();
wrapper.eq("employee_no", "123")
.between("age", 20, 30)
.in("id", new ArrayList<>(100))
.notLike("employee_name", "%23%")
.orderByDesc("id")
.last("limit 10");
result = employeeRepository.list(wrapper);
return result;
}
// lambda查询
public List<EmployeeEntity> lambdaQuery() {
LambdaQueryWrapper<EmployeeEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(EmployeeEntity::getEmployeeNo, "1234");
return employeeRepository.getBaseMapper().selectList(queryWrapper);
}
// 分页查询
public List<EmployeeEntity> listPage() {
Page<EmployeeEntity> page = new Page<>(1, 10);
QueryWrapper<EmployeeEntity> wrapper = new QueryWrapper<>();
wrapper.between("age", 20, 30);
Page<EmployeeEntity> resultPage = employeeRepository.page(page, wrapper);
List<EmployeeEntity> records = resultPage.getRecords();
long total = resultPage.getTotal();
return records;
}
// 分组查询
public List<Map<String, Object>> groupSelect() {
QueryWrapper<EmployeeEntity> wrapper = new QueryWrapper<>();
wrapper.select("age", "count(*) as count")
.groupBy("age")
.having("count(*) > 3");
List<Map<String, Object>> maps = employeeRepository.getBaseMapper().selectMaps(wrapper);
return maps;
}
// 子查询
public List<EmployeeEntity> queryBySql() {
QueryWrapper<EmployeeEntity> wrapper = new QueryWrapper<>();
wrapper.eq("id", "select max(id) from t_employee");
// SELECT * FROM t_employee WHERE id = (select MAX(id) from t_employee)
return employeeRepository.getBaseMapper().selectList(wrapper);
}
注意:mbp从3.5.9PaginationInnerInterceptor 已分离出来。如需使用,则需单独引入 mybatis-plus-jsqlparser 依赖,并且3.5.9 及以上版本开始使用 Java 11 编译,若想兼容Java 8,jsqlparser可使用以下版本
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
<version>3.5.9</version>
</dependency>
同时配置分页拦截器
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 如果配置多个插件, 切记分页最后添加
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
2.4 动态查询分表
项目中可能会有这样的场景,需要查询分表,例如以年为维度的表t_user_2024, t_user_2025,根据传入不同的表后缀查询不同的表,这样就需要动态修改sql语句,MBP也支持动态修改表名。
创建一个动态处理表名的处理类,实现MBP自带插件中TableNameHandler接口即可
package com.lwk.demo.mbp;
import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
import java.util.Arrays;
import java.util.List;
public class DynamicTableNameHandler implements TableNameHandler {
private static final ThreadLocal<String> SUFFIX = new ThreadLocal<>();
List<String> tableNames;
// 指定哪些表需要分表
public DynamicTableNameHandler(String... tableNames) {
this.tableNames = Arrays.asList(tableNames);
}
@Override
public String dynamicTableName(String sql, String tableName) {
if (tableNames.contains(tableName)) {
return tableName + "_" + SUFFIX.get();
}
return tableName;
}
public static void setTableSuffix(String suffix) {
SUFFIX.set(suffix);
}
public static void removeTableSuffix() {
SUFFIX.remove();
}
}
将此动态表名处理类接入mbp拦截器中
package com.lwk.demo.mbp;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
dynamicTableNameInnerInterceptor.setTableNameHandler(new DynamicTableNameHandler("t_employee"));
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
return interceptor;
}
}
使用时手动设置表后缀名
public List<EmployeeEntity> query(String suffix) {
DynamicTableNameHandler.setTableSuffix(suffix);
return employeeRepository.list();
}
MBP也提供其它丰富使用的功能,大家有兴趣可移步官网查看学习。
3、Mytbatis-flex
对标MBP的工具,官网:mybatis-flex.com/ ,这里不多做介绍,提供更加丰富的应用功能,但开源时间不长(2023开源),未经过真实生产环境检验。
性能对比:
MyBatis-Flex 的查询单条数据的速度,大概是 MyBatis-Plus 的 5 ~ 10+ 倍。
MyBatis-Flex 的查询 10 条数据的速度,大概是 MyBatis-Plus 的 5~10 倍左右。
Mybatis-Flex 的分页查询速度,大概是 Mybatis-Plus 的 5~10 倍左右。
Mybatis-Flex 的数据更新速度,大概是 Mybatis-Plus 的 5~10+ 倍。