【Mybatis-Plus】使用步骤 条件构造器 分页模型
文章目录
- Mybatis-Plus
- 使用步骤
- 条件构造器
- 分页模型
Mybatis-Plus
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
- 需要快速搭建 CRUD 接口的应用程序。
- 对于已有使用 MyBatis 的项目,希望通过最小改动获得更好的开发效率。
- 在不牺牲灵活性的前提下,寻求一种更高效的方式来处理常见的数据库操作。
- 需要简单而强大的分页、排序等高级功能。
- 关注安全性,尤其是防止 SQL 注入攻击的应用。
简介 | MyBatis-Plus
使用步骤
新建 SpringBoot 项目,项目结构如下:
1、Maven 依赖配置
<!-- Mybatis-Plus包 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
其他必要的包
<!-- MySQL驱动包 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- lombok包 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2、应用配置文件
配置 MyBatis-Plus 相关属性,如类型别名包路径、控制台打印 SQL 语句、是否开启驼峰映射等
mybatis-plus:
type-aliases-package: com.hz.pojo #类型别名所在的包
#控制台打印sql语句
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: false # 关闭驼峰映射
3、实体类定义
创建一个实体类 Provider
,对应数据库中的 smbms_provider
表
使用 MyBatis-Plus 注解来指定主键策略和字段映射
@Data
@TableName("smbms_provider")
public class Provider implements Serializable {
// 数据库自增 ID 作为主键
@TableId(value = "id", type = IdType.AUTO)
private Integer id; //id
private String proCode; //供应商编码
// 映射到数据库字段 proName as pname
@TableField("proName")
private String pname; //供应商名称
private String proDesc; //供应商描述
private String proContact; //供应商联系人
private String proPhone; //供应商电话
private String proAddress; //供应商地址
private String proFax; //供应商传真
private Integer createdBy; //创建者
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date creationDate; //创建时间
private Integer modifyBy; //更新者
private Date modifyDate;//更新时间
// 忽略此字段
@TableField(exist = false)
private String abc; //冗余字段
}
4、SpringBoot 应用启动类
定义应用程序的入口点,并扫描 Mapper 接口的位置
@SpringBootApplication
@MapperScan("com.hz.mapper")
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
ProviderMapper 继承 BaseMapper<Provider> 并生成单元测试(快捷键 Alt + Enter)
public interface ProviderMapper extends BaseMapper<Provider> {}
@SpringBootTest
public class ProviderMapperTest {
@Resource
private ProviderMapper providerMapper;
@Test
public void findById(){
// Provider provider = providerMapper.selectById(1);
providerMapper.selectList(null);
}
}
查看控制台 SQL 代码及查询结果
JDBC Connection [HikariProxyConnection@529893402 wrapping com.mysql.cj.jdbc.ConnectionImpl@17b016ac] will not be managed by Spring
==> Preparing: SELECT id,proCode,proName AS pname,proDesc,proContact,proPhone,proAddress,proFax,createdBy,creationDate,modifyBy,modifyDate FROM smbms_provider
==> Parameters:
<== ......
去到 BaseMapper
的源码里
/**
* Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
* <p>这个 Mapper 支持 id 泛型</p>
*
* @author hubin
* @since 2016-01-23
*/
public interface BaseMapper<T> extends Mapper<T> {...}
接口覆盖了大部分单表操作的常用方法:
插入操作
insert
插入一个实体对象到数据库中。
删除操作
deleteById
根据主键 ID 删除对应的数据库记录deleteByMap
根据传入的列名和列值对删除记录delete
根据条件构造器删除记录deleteBatchIds
根据主键 ID 集合批量删除记录
更新操作
updateById
根据主键 ID 更新实体对象update
根据条件构造器更新实体对象
查询操作
selectById
根据主键 ID 查询实体对象selectBatchIds
根据主键 ID 集合批量查询实体对象列表selectByMap
根据列名和列值对查询实体对象列表selectOne
根据条件构造器查询一条记录selectCount
根据条件构造器查询符合条件的记录数selectList
根据条件构造器查询实体对象列表selectMaps
根据条件构造器查询记录,结果以Map<String, Object>
形式返回selectObjs
根据条件构造器查询记录,但只返回第一个字段的值
分页查询操作
<E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);
根据条件构造器进行分页查询,返回实体对象列表<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
根据条件构造器进行分页查询,结果以Map<String, Object>
形式返回
加入 Service 层接口及实现类,项目结构改变如下:
5、服务层
定义服务层接口 ProviderService
继承 IService<Provider>
,并在实现类 ProviderServiceImpl
中扩展 ServiceImpl<ProviderMapper, Provider>
public interface ProviderService extends IService<Provider> {}
@Service
public class ProviderServiceImpl extends ServiceImpl<ProviderMapper, Provider> implements ProviderService {}
BaseMapper
是 MyBatis-Plus 提供的一个基础映射器接口,它直接对应于 MyBatis 的 Mapper 接口。它主要用于定义和实现单表的基础 CRUD 操作。BaseMapper
的方法大多数是直接对应 SQL 语句的,如 insert
、deleteById
、updateById
等。
IService
是 MyBatis-Plus 提供的一个服务接口,它定义了一系列的业务服务方法。它主要用于实现业务逻辑,可以包含单表或多表的复杂操作。IService
通常会使用 BaseMapper
来执行底层的数据库操作。
去到 IService
和 ServiceImpl
的源码里
/**
* 顶级 Service
*
* @author hubin
* @since 2018-06-23
*/
public interface IService<T> {...}
/**
* IService 实现类( 泛型:M 是 mapper 对象,T 是实体 )
*
* @author hubin
* @since 2018-06-23
*/
@SuppressWarnings("unchecked")
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {...}
IService
接口中一些关键方法:
插入操作
boolean save(T entity)
插入一个实体对象到数据库boolean saveBatch(Collection<T> entityList)
批量插入实体对象集合boolean saveOrUpdate(T entity)
依据实体ID存在与否,插入或更新实体
删除操作
boolean removeById(Serializable id)
根据主键ID删除记录boolean removeByMap(Map<String, Object> columnMap)
根据条件Map删除记录boolean remove(Wrapper<T> queryWrapper)
根据条件包装器删除记录boolean removeByIds(Collection<?> list)
根据ID集合批量删除记录
更新操作
boolean updateById(T entity)
根据实体主键更新记录boolean update(T entity, Wrapper<T> updateWrapper)
根据条件和实体更新记录boolean updateBatchById(Collection<T> entityList)
根据实体集合批量更新记录
查询操作
T getById(Serializable id)
根据主键ID查询实体List<T> listByIds(Collection<? extends Serializable> idList)
根据ID集合查询实体列表List<T> listByMap(Map<String, Object> columnMap)
根据条件Map查询实体列表
6、编写单元测试
通过 QueryWrapper
及其内置的方法构建复杂查询条件
@SpringBootTest
public class ProviderServiceImplTest {
@Resource
private ProviderService providerService;
@Test
public void find() {
// 创建 QueryWrapper 实例
QueryWrapper<Provider> queryWrapper = new QueryWrapper<>();
// 选择要查询的列
queryWrapper.select("id", "proCode", "proDesc", "proName");
// 构建查询条件
// 注意:MyBatis-Plus 的 like 和 eq 方法可以传递 condition 参数实现动态 SQL
queryWrapper.like("proDesc", "深圳");
queryWrapper.eq("proCode", "GZ_GYs002");
// 添加排序条件
queryWrapper.orderByDesc("creationDate");
// 执行查询并获取结果列表
List<Provider> providerList = providerService.list(queryWrapper);
}
}
providerService.list(new QueryWrapper<Provider>()
.select("id","proCode","proDesc","proName")
.like(true,"proDesc","深圳")
.eq(true,"proCode","GZ_GYS002")
.orderByDesc("creationDate"));
执行结果如下:
JDBC Connection [HikariProxyConnection@1478493964 wrapping com.mysql.cj.jdbc.ConnectionImpl@34208baa] will not be managed by Spring
==> Preparing: SELECT id,proCode,proDesc,proName FROM smbms_provider WHERE (proDesc LIKE ? AND proCode = ?) ORDER BY creationDate DESC
==> Parameters: %深圳%(String), GZ_GYs002(String)
<== Total: 0
条件构造器
MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。
在 MyBatis-Plus 中,Wrapper 类是构建查询和更新条件的核心工具。以下是主要的 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 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题。
UpdateWrapper
@Test
public void update(){
Provider provider = new Provider();
provider.setId(22L);
provider.setPname("张三公司");
providerService.updateById(provider);
}
根据供应商ID id 修改供应商名称 pname
@Test
public void update(){
Provider provider = new Provider();
provider.setPname("张三公司");
providerService.update(provider,
new UpdateWrapper<Provider>().eq("proCode","zhangsan"));
}
根据供应商编码 proCode 修改供应商名称 pname
LambdaQueryWrapper
@Test
public void find(){
LambdaQueryWrapper<Provider> providerLambdaQueryWrapper = Wrappers.lambdaQuery();
providerLambdaQueryWrapper.select(Provider::getPname,Provider::getId);
providerLambdaQueryWrapper.eq(Provider::getPname,"张三公司");
providerService.list(providerLambdaQueryWrapper);
}
根据供应商名称 pname 查询指定字段
分页模型
使用简单分页模型
Page<T>
实现接口 IPage<T>
,是用来表示分页数据的通用工具类。
public class Page<T> implements IPage<T> {
private static final long serialVersionUID = 8545996863226528798L;
protected List<T> records;
protected long total;
protected long size;
protected long current;
protected List<OrderItem> orders;
protected boolean optimizeCountSql;
protected boolean isSearchCount;
protected boolean hitCount;
protected String countId;
protected Long maxLimit;
public Page() {
this.records = Collections.emptyList(); // 查询数据列表
this.total = 0L; // 默认总记录数为0
this.size = 10L; // 默认每页显示10条记录
this.current = 1L; // 默认当前页是第1页
this.orders = new ArrayList(); // 表示没有排序规则
this.optimizeCountSql = true;
this.isSearchCount = true;
this.hitCount = false;
}
......
}
@Configuration
public class MybatisPlusConfig {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
return interceptor;
}
}
-
MybatisPlusInterceptor
对象是 MyBatis-Plus 用于拦截 SQL 执行的拦截器。 -
PaginationInnerInterceptor
是 MyBatis-Plus 提供的分页插件。 -
DbType.MYSQL
指定了数据库类型为 MySQL,这样分页插件会根据 MySQL 的特性生成相应的分页 SQL。
@Test
public void findSize(){
Page<Provider> page = new Page<>(2,5);
providerService.page(page,null);
List<Provider> providers = page.getRecords();//获得分页数据
System.out.println("总记录数: " + page.getTotal());//总记录数
System.out.println("当前页: " + page.getCurrent());//当前页
System.out.println("总页数: " + page.getPages());//总页数
System.out.println("是否有上一页: " + page.hasPrevious());//是否有上一页
System.out.println("是否有下一页: " + page.hasNext());//是否有下一页
}
创建 Page
对象并指定当前页码(2)和每页显示的记录数(5);调用 providerService.page(page, null)
方法执行分页查询,其中第一个参数是分页对象,第二个参数是查询条件;从Page
对象中获取分页数据和相关信息。