Bootstrap

掌握 MyBatis-Plus Wrapper:构建的高效数据库操作

MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。它提供了一种简洁的方式来处理数据库操作,尤其是通过其 Wrapper 类和 LambdaWrapper 类,可以非常方便地构建动态 SQL 查询。

Wrapper

Wrapper 是一个通用的查询构建器接口,常见于一些 ORM 框架或数据库操作库中。它提供了一种灵活的方式来构建查询条件,用于执行数据库操作。

具体的实现类如 QueryWrapperUpdateWrapperLambdaQueryWrapper,它们都实现了 Wrapper 接口,并提供了不同的查询条件构建方式。

使用 Wrapper 的优点是可以规范化和简化代码,使得查询条件的构建变得更加清晰、可读性更强。

Wrapper 体系结构

image-20220521092812125

  • Wrapper :最顶层的抽象类,作为所有条件构造器的基础。

    • AbstractWrapper :实现 Wrapper 接口,用于构建查询或更新条件,生成 SQL 的 WHERE 子句。

      • QueryWrapper:用于构建查询条件。

      • UpdateWrapper:用于构建更新条件。

      • AbstractLambdaWrapper:使用 Lambda 表达式实现条件构建,提供类型安全的 API。

        • LambdaQueryWrapper:用于 Lambda 语法使用的查询Wrapper

        • LambdaUpdateWrapper:用于 Lambda 风格的更新条件构建。

Wrapper 常用方法

Wrapper 接口通常具有一系列方法,用于构建相应的查询条件,如 eqnelikeorderBy 等。这些方法用于指定查询条件中的字段、操作符和值,以实现精确的数据过滤和排序。

  • eq(column, value):指定等于条件,将字段(column)与给定的值(value)进行比较,相等则返回 true。 示例:wrapper.eq("name", "John")

  • ne(column, value):指定不等于条件,将字段(column)与给定的值(value)进行比较,不相等则返回 true。

  • like(column, value):指定模糊匹配条件,将字段(column)与给定的值(value)进行模糊匹配,返回匹配成功的结果。示例:wrapper.like("title", "%keyword%")

  • gt(column, value):指定大于条件,将字段(column)与给定的值(value)进行比较,大于则返回 true。

  • lt(column, value):指定小于条件,将字段(column)与给定的值(value)进行比较,小于则返回 true。

  • between(column, value1, value2):指定范围条件,将字段(column)与给定的两个值(value1和value2)进行比较,位于范围内则返回 true。示例:wrapper.between("price", 100, 200)

  • orderBy(column, isAsc):指定排序条件,按照字段(column)进行升序(isAsc为true)或降序(isAsc为false)排序。
    示例:wrapper.orderBy("create_time", true)

  • orderByAsc(column):指定按照字段(column)进行升序排序。

  • orderByDesc(column):指定按照字段(column)进行降序排序。

Wrapper 子类

QueryWrapper

QueryWrapper 是一个常见的 Wrapper 接口的实现类,用于构建查询条件的对象。它通常用于在进行数据库查询时指定查询条件,排序规则等。

组装查询条件

执行 SQL:

SELECT uid AS id,username AS name,age,email,is_deleted FROM t_user 
WHERE is_deleted=0 AND 
	(username LIKE ? AND 
		 age BETWEEN ? AND ? AND 
			 email IS NOT NULL)
public void test01(){
// 查询用户名包含a,年龄在20到30之间,邮箱信息不为null的用户信息
QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
queryWrapper.like("username","a")
	.between("age",20,30)
	.isNotNull("email");

List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
组装排序条件

执行 SQL:

SELECT uid AS id,username AS name,age,email,is_deleted 
FROM t_user 
WHERE is_deleted=0 
ORDER BY age DESC, id ASC
public void test02(){
  // 查询用户信息,按照年龄的降序排序,若年龄相同,则按照id升序排序
  QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
			.orderByDesc("age")
			.orderByAsc("id");
  
  List<User> users = userMapper.selectList(queryWrapper);
  users.forEach(System.out::println);
}
组装删除条件

执行 SQL:

UPDATE t_user 
SET is_deleted=1 
WHERE is_deleted=0 AND (email IS NULL)
public void test03(){
  //删除邮箱地址为null的用户信息
  QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
  queryWrapper.isNull("email");
  
  int result = userMapper.delete(queryWrapper);
  System.out.println(result > 0 ? "删除成功!" : "删除失败!");
  System.out.println("受影响的行数为:" + result);
}
组装 select 子句

借助 QueryWrapper 的 select() 方法,组装 select 子句

执行 SQL:

SELECT username,age,email FROM t_user WHERE is_deleted=0
public void test06(){
  //查询用户的用户名、年龄、邮箱信息
  QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
  queryWrapper.select("username","age","email");
  
  List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
  maps.forEach(System.out::println);
}
实现子查询

借助 QueryWrapper 的 inSql() 方法,内置 SQL 语句实现子查询

执行 SQL:

SELECT uid AS id,user_name AS name,age,email,is_deleted 
FROM t_user 
WHERE is_deleted=0 AND 
	(uid IN (select uid from t_user where uid <= 100))
public void test07(){
  // 查询id小于等于100的用户信息
  QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
  queryWrapper.inSql("uid", "select uid from t_user where uid <= 100");
  List<User> list = userMapper.selectList(queryWrapper);
  list.forEach(System.out::println);
}
UpdateWrapper

UpdateWrapper 是用于构建更新操作的条件对象,通常用于在进行数据库更新操作时指定更新的条件。和 QueryWrapper 类似,UpdateWrapper 也是一个 Wrapper 接口的实现类,不仅拥有 QueryWrapper 的组装条件功能,还提供了 set 方法进行修改对应条件的数据库信息

条件的优先级

执行 SQL:

UPDATE t_user 
SET user_name=?, email=? 
WHERE is_deleted=0 AND 
	(age > ? AND user_name LIKE ? OR email IS NULL)
public void test04(){
  //将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改
  UpdateWrapper<User> updateWrapper = new UpdateWrapper<User>();
  updateWrapper.gt("age",20)
  .like("username","a")
  .or()
  .isNull("email");
  
  User user = new User();
  user.setName("Oz");
  user.setEmail("[email protected]");

  int result = userMapper.update(user, updateWrapper);
  System.out.println(result > 0 ? "修改成功!" : "修改失败!");
  System.out.println("受影响的行数为:" + result);
}

执行SQL:

UPDATE t_user
SET username=?, email=? 
WHERE is_deleted=0 AND 
	(username LIKE ? AND (age > ? OR email IS NULL))
public void test05(){
  //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
  UpdateWrapper<User> updateWrapper = new UpdateWrapper<User>();
  updateWrapper.like("username","a")
			  .and(i->i.gt("age",20)
			  .or()
			  .isNull("email"));

  User user = new User();
  user.setName("Vz7797");
  user.setEmail("[email protected]");

  int result = userMapper.update(user, updateWrapper);
  System.out.println(result > 0 ? "修改成功!" : "修改失败!");
  System.out.println("受影响的行数为:" + result);
}
LambdaQueryWrapper

LambdaQueryWrapper 是一种通过 Lambda 表达式构建查询条件的工具类,它是基于 MyBatis-Plus 框架的 QueryWrapper 的增强版。通过使用 LambdaQueryWrapper,可以使用简洁而类型安全的 Lambda 表达式,通过 getter 获取属性值来构建查询条件,避免了硬编码字符串的方式,提高了代码的可读性和维护性。

public void test11(){
String username = "a";
Integer ageBegin = null;
Integer ageEnd = 30;

LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>();
queryWrapper.like(StringUtils.isNotBlank(username), 
				  User::getName, username)
	.ge(ageBegin != null, User::getAge, ageBegin)
	.le(ageEnd != null, User::getAge, ageEnd);
	
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
LambdaUpdateWrapper

LambdaUpdateWrapper 是 MyBatis-Plus 框架提供的用于通过 Lambda 表达式构建更新条件的工具类。类似于 LambdaQueryWrapper 用于构建查询条件,LambdaUpdateWrapper 则用于构建更新操作的条件,可以通过 Lambda 表达式更直观地指定更新操作的字段和条件。

public void test12(){
//将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<User>();
updateWrapper.like(User::getName, "a")
	.and(i -> i.gt(User::getAge, 20)
	.or()
	.isNull(User::getEmail))
	.set(User::getName, "小黑")
	.set(User::getEmail,"[email protected]");
int result = userMapper.update(null, updateWrapper);
System.out.println("result:"+result);
}

condition

在真正开发的过程中,组装条件是常见的功能,而这些条件数据来源于用户输入,是可选的,因此我们在组装这些条件时,必须先判断用户是否选择了这些条件,若选择则需要组装该条件,若没有选择则一定不能组装,以免影响 SQL 执行的结果

if 判断

执行SQL:

SELECT uid AS id,user_name AS name,age,email,is_deleted 
FROM t_user 
WHERE is_deleted=0 AND (user_name LIKE ? AND age <= ?)
public void test09(){
   String username = "a";
   Integer ageBegin = null;
   Integer ageEnd = 30;
   QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
   
   if(StringUtils.isNotBlank(username)){
//isNotBlank判断某个字符创是否不为空字符串、不为null、不为空白符
	   queryWrapper.like("user_name", username);
   }
   if(ageBegin != null){
	   queryWrapper.ge("age", ageBegin);
   }
   if(ageEnd != null){
	   queryWrapper.le("age", ageEnd);
   }
   
   List<User> list = userMapper.selectList(queryWrapper);
   list.forEach(System.out::println);
}
condition 判断

可以使用带 condition 参数的重载方法构建查询条件,简化代码的编写。

eq(condition,column, value):指定等于条件,满足 condition 后(即为 true),将字段(column)与给定的值(value)进行比较,相等则返回 true。

public void test10(){
  String username = "a";
  Integer ageBegin = null;
  Integer ageEnd = 30;
  
  QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
  queryWrapper.like(StringUtils.isNotBlank(username), "user_name", username)
	  .ge(ageBegin != null, "age", ageBegin)
	  .le(ageEnd != null, "age", ageEnd);
  List<User> list = userMapper.selectList(queryWrapper);
  list.forEach(System.out::println);
}

自定义 SQL

可以利用 MyBatisPlus 的 Wrapper 来构建复杂的 Where 条件,然后自己定义 SQL 语句中剩下的部分。

SELECT status, COUNT(id) As total
FROM tb user
<if test="name != null"> AND username LIKE #{name}</if>
<where>
<if test= "uids != null"> 
	AND id IN
	<foreach collection="ids" 
		open="(" close=")" 
		item="id" 
		separator=",">#{id}
	</foreach>
</if>
</where>
GROUP BY status

如上查询中,SELECT 语句中包含特殊字段,在 Mybatis Plus 中不方便定义,因此在这种复杂的业务环境中,将 where 条件的构建交给 Mybatis Plus,剩余部分由我们自定义 SQL 完成。将构造好的 where 条件传递到 mapper 层,并将二者在 mapper.xml 中完成最终 SQL 语句的拼接:

  1. 在 mapper 中自定义方法,方法参数中用 @Param 注解声明 wrapper 变量名称,必须是ew
void updateBalanceByIds(@Param("ew")LambdaQueyWrapper<User>wrapper,
						@Param("amount")int amount);
  1. 基于 Wrapper 构建 where 条件
List<Long> ids = List.of(1L,2L4L);
int amount = 200;

// 1.构建条件
LambdaQueryWrapper<User>wrapper = new LambdaQueryWrapper<User>().in(User::getId,ids);

// 2.自定义SQL方法调用
userMapper.updateBalanceByIds(wrapper,amount)
  1. 自定义 SQL,并使用 ${ew.customSqlSegment} 拼接 Wrapper 条件,Mybatis Plus 将自动解析并拼接条件
<update id="updateBalanceByIds">
	UPDATE tb user 
	SET balance = balance - #{amount} ${ew.customSqlSegment}
</update>

MyBatis-Plus 的 Wrapper 系统为开发者提供了灵活且强大的 SQL 动态构建机制,极大地简化了数据库操作的编码工作,尤其是在处理复杂查询和更新场景时。

;