MyBatis-Plus
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上进行了扩展和优化,提供了更多便捷的功能和特性,大大提高了开发效率。
安装与配置
- 引入依赖
在项目的pom.xml
文件中添加 MyBatis-Plus 的依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
- 配置数据源
在配置文件(如application.yml
)中配置数据库连接信息:
spring:
datasource:
url: jdbc:mysql://localhost:3306/db_name
username: root
password: 123456
- 配置 MyBatis-Plus
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
实体类
- 注解使用
@TableName
:指定表名。@TableId
:标识主键。@TableField
:设置字段属性。
- 示例
@TableName("user")
public class User {
@TableId("id")
private Long id;
@TableField("name")
private String name;
// 省略 getter 和 setter 方法
}
Mapper 接口
继承 BaseMapper
接口即可获得基本的 CRUD 方法。
public interface UserMapper extends BaseMapper<User> {}
条件构造器(Wrapper)
MyBatis-Plus 为我们提供了强大的条件构造器,用于构建各种复杂的查询、更新和删除条件,避免了手动拼接 SQL 字符串的繁琐和易错性。
-
QueryWrapper(查询条件构造器):主要用于构建查询条件,可通过一系列方法添加不同的条件。常用方法如下:
-
allEq(Map params)
:全部相等(或个别isNull
)。它接受一个 Map,其中键是数据库字段名,值是要匹配的值。如果值为null
,则根据null2IsNull
参数的设置来决定是否作为IS NULL
条件。例如:Map<String, Object> params = new HashMap<>(); params.put("name", "小明"); params.put("age", null); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.allEq(params, false);
-
eq(R column, Object val)
:等于。用于添加字段等于某个值的条件。例如:queryWrapper.eq("name", "小明");
-
ne(R column, Object val)
:不等于。 -
gt(R column, Object val)
:大于。 -
ge(R column, Object val)
:大于等于。 -
lt(R column, Object val)
:小于。 -
le(R column, Object val)
:小于等于。 -
in(R column, Collection<?> coll)
:字段在给定的集合中。例如:queryWrapper.in("age", Arrays.asList(1, 2, 3));
-
notIn(R column, Collection<?> coll)
:字段不在给定的集合中。 -
inSql(R column, String inValue)
:字段IN
(SQL 语句)。例如:queryWrapper.inSql("age", "select age from other_table where some_condition");
-
notInSql(R column, String inValue)
:字段NOT IN
(SQL 语句)。 -
groupBy(R... columns)
:分组。可以指定多个字段进行分组。 -
orderByAsc(R... columns)
:按照指定字段升序排序。 -
orderByDesc(R... columns)
:按照指定字段降序排序。 -
orderBy(boolean isAsc, R... columns)
:更灵活的排序方式,可指定排序顺序和字段。 -
having(String sqlHaving, Object... params)
:HAVING
子句条件。 -
func(Consumer<AbstractWrapper> consumer)
:用于在出现if...else
情况下调用不同方法且保持条件构造器链不断。例如:queryWrapper.func(i -> if (true) { i.eq("id", 1); } else { i.ne("id", 1); });
-
or()
:拼接OR
条件。可以与其他条件方法嵌套使用,如queryWrapper.eq("name", "小明").or().eq("age", 18);
-
and(Consumer<AbstractWrapper> consumer)
:AND
嵌套条件。例如:queryWrapper.eq("name", "小明").and(i -> i.eq("age", 18));
-
apply(String applySql, Object... params)
:拼接 SQL 片段。注意该方法可用于数据库函数,动态入参的params
对应前面applySql
内部的{index}
部分,这样不会有 SQL 注入风险。例如:queryWrapper.apply("DATE_FORMAT(create_time, '%Y-%m-%d') = '2023-07-03'");
-
last(String lastSql)
:无视优化规则直接拼接到 SQL 的最后。例如:queryWrapper.last("LIMIT 1");
-
exists(String existsSql)
:拼接EXISTS
(SQL 语句)。 -
notExists(String notExistsSql)
:拼接NOT EXISTS
(SQL 语句)。
-
-
UpdateWrapper(更新条件构造器):与 QueryWrapper 类似,主要用于构建更新条件。例如:
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("id", 1).set("name", "新名字");
-
LambdaQueryWrapper / LambdaUpdateWrapper:这是基于 lambda 表达式的条件构造器,使用起来更加简洁和类型安全。例如:
LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery(); lambdaQueryWrapper.like(User::getName, "字母").lt(User::getAge, 18);
在使用条件构造器时,需要注意以下几点:
- 方法中的参数
R column
表示数据库字段,当R
具体类型为String
时则为数据库字段名(字段名是数据库关键字的需要用转义符包裹),而不是实体类数据字段名。 - 几乎所有的条件构造函数都提供了
condition
参数用于实现动态 SQL,即根据该参数判断是否返回true
,如果返回false
,则该条件不成立。 - 入参中出现的
Map
或List
为空时,对应的条件不会加入到最终生成的 SQL 中。
IService
IService 接口提供了许多常用的方法,方便进行数据的增删改查等操作,减少了重复代码的编写。
-
IService 介绍:IService 里面已经提供了很多常用方法,例如批量保存、批量更新等。只需要配置好直接调用即可。以下是一些常见的方法示例:
default boolean save(T entity)
:保存单个实体对象。default boolean saveBatch(Collection<T> entityList)
:批量保存实体对象列表。boolean saveBatch(Collection<T> entityList, int batchSize)
:批量保存实体对象列表,可指定批量大小。default boolean saveOrUpdateBatch(Collection<T> entityList)
:批量保存或更新实体对象列表。
-
IService 使用配置:
-
创建相应的 Mapper 接口,继承
BaseMapper
。例如:@Repository("sysUserMapper") public interface SysUserMapper extends BaseMapper<SysUser> { }
-
创建 Service 类,继承
ServiceImpl
,并指定对应的 Mapper 和实体类型。例如:@Component public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> { }
-
-
IService 调用示例:
public void insert() { List<SysUser> sysUsers = new ArrayList<>(); for (int i = 1; i < 100; i++) { SysUser sysUser = new SysUser(); sysUser.setUsername("长伞"); sysUser.setPassword("123456"); sysUsers.add(sysUser); } sysUserService.saveBatch(sysUsers); }
此外,IService 还提供了其他方法,如getOne
方法可根据条件获取单个实体,如果结果不止一条则会抛出异常,也可通过传递第二个参数false
来默认取第一条结果;saveOrUpdateBatch
方法用于批量新增或修改等。同时,基于 lambda 表达式也可以进行查询、修改和删除等操作,例如:
lambdaQuery
:List<User> list = userService.lambdaQuery().eq(User::getAge, 18).list();
lambdaUpdate
:boolean update = userService.lambdaUpdate().eq(User::getAge, 18).set(User::getAge, 31).update();
lambdaRemove
:boolean remove = userService.lambdaUpdate().eq(User::getAge, 18).remove();
分页查询
MyBatis-Plus 中的分页查询功能通常需要使用分页插件来实现,它支持物理分页,返回部分数据,占用内存小,能够获取数据库最新的状态,适用于数据量较大、数据更新频繁的场景。
配置分页插件:首先创建一个 MybatisPlusConfig 配置类,添加一个返回 PaginationInterceptor 对象的方法,并在 Spring Boot 启动类或配置类上使用 @Bean
注解将其注册为一个 Bean。
@Configuration
public class MyBatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
分页查询方法:MyBatis-Plus 的 Wrapper 提供了两种分页查询的方式,在 Mapper 接口中提供了相应的方法。
IPage selectPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper)
:根据 entity 条件和分页参数进行查询,并返回一个 IPage 对象。IPage<T> selectMapsPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper)
:与上一个方法类似,但返回的是包含查询结果映射为 Map 的 IPage 对象。
使用示例如下:
@Test
public void selectByPage() {
QueryWrapper wrapper = new QueryWrapper();
wrapper.like("name", "xxx").lt("age", 40);
Page page = new Page<>(1, 2);
// IPage<User> userIPage = userMapper.selectPage(page, wrapper);
IPage<Map<String, Object>> mapIPage = userMapper.selectMapsPage(page, wrapper);
System.out.println("总页数: " + mapIPage.getPages());
System.out.println("总记录数: " + mapIPage.getTotal());
List<Map<String, Object>> records = mapIPage.getRecords();
records.forEach(System.out::println);
}
不关心总记录数的分页查询:如果只需要查询数据,而不关心总记录数,可以在创建 Page 对象时传入第三个参数为 false。
Page page = new Page<>(1, 2, false);
自定义 SQL 分页查询:有时候需要进行多表连接查询或复杂的 SQL 语句分页查询。首先在 Mapper 文件中编写对应的分页查询接口,然后在 XML 中编写对应的 SQL 语句。如果想在自定义的 SQL 语句中使用 Wrapper 查询条件构造器,需要在 Mapper 接口中添加参数,以及在 XML 中进行相应的配置。
Mapper 接口中的方法定义:
IPage selectMyPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper);
XML 中的 SQL 语句示例:
SELECT * FROM user ${ew.customSqlSegment}
测试方法示例:
@Test
public void selectByMyPage() {
QueryWrapper wrapper = new QueryWrapper();
wrapper.like("name", "雨").lt("age", 40);
Page page = new Page<>(1, 2);
IPage<Map<String, Object>> mapIPage = userMapper.selectMyPage(page, wrapper);
System.out.println("总页数: " + mapIPage.getPages());
System.out.println("总记录数: " + mapIPage.getTotal());
List<Map<String, Object>> records = mapIPage.getRecords();
records.forEach(System.out::println);
}
多表 SQL 分页查询:与单表分页查询类似,在 Mapper 接口中定义分页查询方法,传入分页参数,并在 XML 中编写相应的多表连接查询语句。
例如,有两张表 his_ipd_encounter
和 his_user
,根据用户的真实姓名进行多表分页查询的 Mapper 接口定义如下:
public interface UserMapper extends MyMapper {
IPage selectByHisName(IPage page);
}