MyBatisPlus从零到一:快速入门与核心功能详解(2)
二、核心功能
刚才的案例中都是以 id 为条件的简单 CRUD,一些复杂条件的 SQL 语句就要用到一些更高级的功能了。
2.1 条件构造器:
除了新增以外,修改、删除、查询的 SQL 语句都需要指定 where 条件。因此 BaseMapper 中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。
参数中的Wrapper
就是条件构造的抽象类,其下有很多默认实现,继承关系如图:
Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:(下面的这些方法中,一般来说,第一个参数是 boolean 类型的,是用来做出动态 SQL 中 if 标签同样的效果,如果为 true,表示该条件会添加到 SQL 中,如果为 false,就不会在 SQL 中显示该条件。)
而 QueryWrapper 在 AbstractWrapper 的基础上拓展了一个 select 方法,允许指定查询字段:
而 UpdateWrapper 在 AbstractWrapper 的基础上拓展了一个 set 方法,允许指定 SQL 中的 SET 部分:
2.1.1 QueryWrapper:
@Test void testQueryWrapper() { //1. 构造查询条件 QueryWrapper<User> queryWrapper = new QueryWrapper<User>() .select("id","username","info","balance") .like("username", "o") .ge("balance", 1000); //2. 进行查询 List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println); }
更新:更新用户名为 jack(Mysql 中,是不区分大小写的) 的用户的余额为 2000,代码如下:
@Test void testUpdateByQueryWrapper(){ //1. 构造更新条件 //1.1 构造更新参数 User user = new User(); user.setBalance(2000); //1.2 构造更新条件 QueryWrapper<User> queryWrapper = new QueryWrapper<User>() .eq("username","jack"); //2. 进行更新 userMapper.update(user, queryWrapper); }
2.1.2 UpdateWrapper:
UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4)
SET 的赋值结果是基于字段现有值的,这个时候就要利用 UpdateWrapper 中的 setSql 功能了:
@Test void testUpdateWrapper(){ //1. 构造更新条件 UpdateWrapper<User> updateWrapper = new UpdateWrapper<User>() .setSql("balance = balance - 200") .in("id",List.of(1L,2L,4L)); //2. 进行更新 userMapper.update(null, updateWrapper); }
2.1.3 LambdaQueryWrapper || LambdaUpdateWrapper:
无论是 QueryWrapper 还是 UpdateWrapper 在构造条件的时候都需要写死字段名称,这在编程规范中显然是不推荐的。
那怎么样才能不写字段名,又能知道字段名呢?
一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给 MybatisPlus,它就能计算出对应的变量名,从而知道字段名。而传递方法可以使用 JDK8 中的方法引用和Lambda表达式。 因此 MybatisPlus 又提供了一套基于Lambda 的 Wrapper ,包含两个:
LambdaQueryWrapper
LambdaUpdateWrapper
分别对应 QueryWrapper 和 UpdateWrapper。
LambdaQueryWrapper 的使用演示如下:
@Test void testLambdaQueryWrapper() { //1. 构造查询条件 LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>() .select(User::getId, User::getUsername, User::getInfo, User::getBalance) .like(User::getUsername, "o") .ge(User::getBalance, 1000); //2. 进行查询 List<User> users = userMapper.selectList(lambdaQueryWrapper); users.forEach(System.out::println); }
LambdaUpdateWrapper 的使用演示如下:(需要注意的是,如果是使用 setSql 的话,是不能使用 Lambda 表达式的,如果需要达成在原来的基础上进行扣减,且不使用 setSql 的话,可以从 Java 代码的业务逻辑出发,先查出当前 user 的信息,在对该 user 的余额进行扣减,再更新回去即可,实现起来并不难,所以下面演示的代码就简单的进行赋值)。
@Test void testLambdaUpdateWrapper() { // 1. 构造更新条件 LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<User>() .set(User::getBalance, 1000) .in(User::getId, List.of(1L, 2L, 3L)); //2. 进行更新 userMapper.update(null, lambdaUpdateWrapper); }
2.2 自定义 SQL:
这种写法在某些企业也是不允许的,因为 SQL 语句最好都维护在持久层,而不是业务层。 就当前案例来说,由于条件是 in 语句,只能将SQL 写在 Mapper.xml 文件,利用 foreach 来生成动态 SQL。 这实在是太麻烦了。假如查询条件更复杂,动态 SQL 的编写也会更加复杂。
所以,MybatisPlus 提供了自定义 SQL 功能,可以让我们利用 Wrapper 生成查询条件,再结合 Mapper.xml 编写 SQL。这样的做法算是综合了一下利弊,将 where 之前的 sql 语句放在 Mapper 层,进行编写,where 后面的语句在业务层进行编写,虽然还是不符合规范,但是较之前全部 sql 都放在业务层,也算是有了提升,MybatisPlus 的优势在于编写 where 条件非常的方便,使用 xml 文件进行动态 sql 编写,太繁琐了。
2.2.1 基本用法:
@Test void testCustomWrapper(){ //1. 构造 Wrapper 条件 QueryWrapper<User> queryWrapper = new QueryWrapper<User>() .in("id", List.of(1L, 2L, 4L)); //2. 进行扣除余额 userMapper.deductBalanceByIds(queryWrapper,200); } // Mapper 层 void deductBalanceByIds(@Param("ew") QueryWrapper<User> queryWrapper, @Param("balance") int balance); // xml 文件 <update id="deductBalanceByIds"> update user set balance = balance - #{balance} ${ew.customSqlSegment} </update>
注意:@Param(“ew”) QueryWrapper queryWrapper,中 @Param(“ew”) 是固定写法,${ew.customSqlSegment} 也是固定写法。
2.2.2 多表关联:
理论上来讲 MyBatisPlus 是不支持多表查询的,不过我们可以利用 Wrapper 中自定义条件,结合自定义 SQL 来实现多表查询的效果。例如,我们要查询出所有收货地址在北京的并且用户 id 在1、2、4之中的用户,要是自己基于 mybatis 实现 SQL,大概是这样的:
<select id="queryUserByIdAndAddr" resultType="User"> SELECT * FROM user u INNER JOIN address a ON u.id = a.user_id WHERE u.id <foreach collection="ids" separator="," item="id" open="IN (" close=")"> #{id} </foreach> AND a.city = #{city} </select>
// VO 实体类 @Data public class UserVO extends User { private String city; } // Test 测试类 @Test void testCustomJoinWrapper(){ //1. 构造 Wrapper 条件 QueryWrapper<UserVO> queryWrapper = new QueryWrapper<UserVO>() .in("u.id",List.of(1L,2L,4L)) .eq("a.city","北京"); //2. 进行查询 List<UserVO> users = userMapper.queryUserAndAddressByWrapper(queryWrapper); } // Mapper 层代码 List<UserVO> queryUserAndAddressByWrapper(@Param("ew") QueryWrapper<UserVO> queryWrapper); // xml 文件中的对应代码 <select id="queryUserAndAddressByWrapper" resultType="com.gobeyye.mp.domain.vo.UserVO"> select u.*,a.city from user u inner join address a on u.id = a.user_id ${ew.customSqlSegment} </select>
本文系转载,版权归原作者所有,如若侵权请联系我们进行删除!
云掣基于多年在运维领域的丰富时间经验,编写了《云运维服务白皮书》,欢迎大家互相交流学习:
《云运维服务白皮书》下载地址:https://fs80.cn/v2kbbq
想了解更多大数据运维托管服务、数据库运维托管服务、应用系统运维托管服务的的客户,欢迎点击云掣官网沟通咨询:https://yunche.pro/?t=shequ