MyBatisPlus详解(三)lambdaQuery、lambdaUpdate、批量新增、代码生成、Db静态工具、逻辑删除

07-11 1562阅读

文章目录

  • 前言
  • 2 核心功能
    • 2.3 Service接口
      • 2.3.3 Lambda
        • 2.3.3.1 lambdaQuery
        • 2.3.3.2 lambdaUpdate
        • 2.3.4 批量新增
        • 3 扩展功能
          • 3.1 代码生成
          • 3.2 静态工具
            • 3.2.1 基本用法
            • 3.2.2 代码实例
            • 3.3 逻辑删除

              前言

              MyBatisPlus详解系列文章:

              MyBatisPlus详解(一)项目搭建、@TableName、@TableId、@TableField注解与常见配置

              MyBatisPlus详解(二)条件构造器Wrapper、自定义SQL、Service接口

              2 核心功能

              2.3 Service接口

              2.3.3 Lambda

              IService接口中还提供了Lambda功能来简化复杂查询及更新功能。

              2.3.3.1 lambdaQuery

              例如,要实现一个根据复杂条件查询用户信息的接口,接口文档如下:

              接口请求方式请求路径请求参数返回值
              根据条件查询用户列表GET/user/listUserQueryList

              查询条件UserQuery的字段如下:

              • username:用户名关键字,可以为空
              • status:用户状态,可以为空
              • minBalance:最小余额,可以为空
              • maxBalance:最大余额,可以为空

                上述条件有可能为空,因此在查询时需要做判断。

                首先定义一个查询实体类UserQuery:

                // com.star.learning.pojo.UserQuery
                @Data
                public class UserQuery {
                    private String username;
                    private Integer status;
                    private Integer minBalance;
                    private Integer maxBalance;
                }
                

                接着在UserController类中编写一个list()方法:

                // com.star.learning.controller.UserController
                @GetMapping("/list")
                public List list(UserQuery userQuery) {
                    // 1.组织条件
                    String username = userQuery.getUsername();
                    Integer status = userQuery.getStatus();
                    Integer minBalance = userQuery.getMinBalance();
                    Integer maxBalance = userQuery.getMaxBalance();
                    System.out.println("根据条件查询用户列表 => " + userQuery);
                    LambdaQueryWrapper wrapper = new QueryWrapper().lambda()
                            .like(username != null, User::getUsername, username)
                            .eq(status != null, User::getStatus, status)
                            .ge(minBalance != null, User::getBalance, minBalance)
                            .le(maxBalance != null, User::getBalance, maxBalance);
                    // 2.查询用户
                    List users = userService.list(wrapper);
                    System.out.println("查询结果 => " + userQuery);
                    return users;
                }
                

                在上述代码进行组织条件时,使用了username != null这样的判断语句,它的效果就和Mapper文件的标签一样,只有当条件成立时才会添加这个查询条件,从而实现动态查询。

                调用/user/list?username=o&minBalance=500,控制台打印信息如下:

                根据条件查询用户列表 => UserQuery(username=o, status=null, minBalance=500, maxBalance=null)
                ==>  Preparing: SELECT id,username,password,phone,info,status,balance,create_time,update_time FROM t_user WHERE (username LIKE ? AND balance >= ?)
                ==> Parameters: %o%(String), 500(Integer)
                 [User(id=3, username=Hope, password=123, phone=13900112222, info={"age": 25, "intro": "上进青年", "gender": "male"}, status=1, balance=19800, createTime=2024-04-21T10:13:35, updateTime=2024-04-21T18:48:28)]
                

                可见,在上述SQL语句中只有username和minBalance是查询条件,其余两个字段均没有作为查询条件。

                上述代码还可以继续简化,我们无需通过new的方式来创建Wrapper,而是直接调用lambdaQuery方法:

                // com.star.learning.controller.UserController
                @GetMapping("/list")
                public List list(UserQuery userQuery) {
                    // 1.组织条件
                    String username = userQuery.getUsername();
                    Integer status = userQuery.getStatus();
                    Integer minBalance = userQuery.getMinBalance();
                    Integer maxBalance = userQuery.getMaxBalance();
                    System.out.println("根据条件查询用户列表 => " + userQuery);
                    // 2.查询用户
                    List users = userService.lambdaQuery()
                            .like(username != null, User::getUsername, username)
                            .eq(status != null, User::getStatus, status)
                            .ge(minBalance != null, User::getBalance, minBalance)
                            .le(maxBalance != null, User::getBalance, maxBalance)
                            .list();
                    System.out.println("查询结果 => " + users);
                    return users;
                }
                

                可以发现,lambdaQuery方法中除了可以构建条件,还可以在链式编程的最后添加一个list(),告诉MP调用结果需要是一个List集合。

                再次调用/user/list?username=o&minBalance=500接口,执行结果是一致的。

                MybatisPlus会根据链式编程的最后一个方法来判断最终的返回结果,除了使用list()返回集合结果,还可以使用one()返回1个结果,使用count()返回计数结果。

                2.3.3.2 lambdaUpdate

                与lambdaQuery()方法类似,IService中的lambdaUpdate()方法可以非常方便的实现复杂更新业务。

                例如有这样一个需求:根据id修改用户余额时,如果扣减后余额为0,则将用户status修改为冻结状态(2)。

                也就是说,在扣减用户余额时,需要对用户剩余余额做出判断,如果发现剩余余额为0,则应该将status修改为2,这就是说update语句的set部分是动态的。

                修改UserServiceImpl实现类中的deductBalance()方法:

                // com.star.learning.service.impl.UserServiceImpl
                @Override
                public void deductBalance(Long userId, Integer money) {
                    // 1.查询用户
                    User user = getById(userId);
                    System.out.println(user);
                    // 2.判断用户状态
                    if (user == null || user.getStatus() == 2) {
                        throw new RuntimeException("用户状态异常");
                    }
                    // 3.判断用户余额
                    if (user.getBalance()  
                

                调用/user/4/deduction/400接口,控制台打印信息如下:

                扣减id=4的用户的余额400
                ==>  Preparing: SELECT id,username,password,phone,info,status,balance,create_time,update_time FROM t_user WHERE id=?
                ==> Parameters: 4(Long)
                  Preparing: UPDATE t_user SET balance=?,status=? WHERE (id = ? AND balance = ?)
                ==> Parameters: 0(Integer), 2(Integer), 4(Long), 400(Integer)
                
                    long b = System.currentTimeMillis();
                    for (int i = 1; i 
                        userService.save(buildUser(i));
                    }
                    long e = System.currentTimeMillis();
                    System.out.println("耗时:" + (e - b));
                }
                private User buildUser(int i) {
                    User user = new User();
                    user.setUsername("user_" + i);
                    user.setPassword("123");
                    user.setPhone("" + (18688190000L + i));
                    user.setBalance(2000);
                    user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
                    return user;
                }
                
                    List
                        userList.add(buildUser(i));
                        // 每1000条插入一次数据
                        if(i % 1000 == 0) {
                            userService.saveBatch(userList);
                            userList.clear();
                        }
                    }
                    long e = System.currentTimeMillis();
                    System.out.println("耗时:" + (e - b));
                }
                
                    // 利用Db实现根据ID查询
                    User user = Db.getById(1L, User.class);
                    System.out.println(user);
                    System.out.println("==========");
                    // 利用Db实现复杂条件查询
                    List= ?)
                ==> Parameters: %o%(String), 1000(Integer)
                  Preparing: UPDATE t_user SET balance=? WHERE (username = ?)
                ==> Parameters: 2000(Integer), Rose(String)
                
                    // ...
                    /**
                     * 收货地址列表
                     */
                    @TableField(exist = false)
                    private List
                    // User user = userService.getById(userId);
                    // System.out.println("根据id查询用户 = " + user);
                    // 基于自定义Service方法查询
                    User user = userService.queryUserAndAddressById(userId);
                    System.out.println("根据id查询用户及其收货地址信息 => " + user);
                    return user;
                }
                
                • 3)在IService接口中定义queryUserAndAddressById()方法,并在UserServiceImpl类中具体实现:
                  // com.star.learning.service.IUserService
                  User queryUserAndAddressById(Long userId);
                  
                  // com.star.learning.service.impl.UserServiceImpl
                  @Override
                  public User queryUserAndAddressById(Long userId) {
                      // 1.查询用户
                      User user = getById(userId);
                      if (user == null) {
                          return null;
                      }
                      // 2.使用Db来查询收货地址
                      List addressList = Db.lambdaQuery(Address.class)
                              .eq(Address::getUserId, userId)
                              .list();
                      // 3.处理返回
                      user.setAddressList(addressList);
                      return user;
                  }
                  
                  • 4)调用/user/1接口,控制台打印信息如下:
                    ==>  Preparing: SELECT id,username,password,phone,info,status,balance,create_time,update_time FROM t_user WHERE id=?
                    ==> Parameters: 1(Long)
                      Preparing: SELECT id,user_id,province,city,town,mobile,street,contact,is_default,notes,deleted FROM t_address WHERE (user_id = ?)
                    ==> Parameters: 1(Long)
                     User(id=1, username=Jack, password=123, phone=13900112224, info={"age": 20, "intro": "佛系青年", "gender": "male"}, status=1, balance=1600, createTime=2024-04-22T19:40:36, updateTime=2024-04-22T19:40:36, addressList=[Address(id=2, userId=1, province=北京, city=北京, town=朝阳区, mobile=13700221122, street=修正大厦, contact=Jack, isDefault=false, notes=null, deleted=false), Address(id=3, userId=1, province=上海, city=上海, town=浦东新区, mobile=13301212233, street=航头镇航头路, contact=Jack, isDefault=true, notes=null, deleted=false)])
                    

                    可见,在查询地址时,采用了Db类的静态方法,避免了注入AddressService,从而减少了循环依赖的风险。

                    3.3 逻辑删除

                    对于一些比较重要的数据,往往会采用逻辑删除的方案,即在表中添加一个字段标记数据是否被删除,当删除数据时把标记置为true,当查询时过滤掉标记为true的数据。

                    但是一旦采用逻辑删除,查询和删除逻辑就会变得更加复杂。为此,MybatisPlus添加了对逻辑删除的支持。

                    但是要注意,只有MybatisPlus生成的SQL语句才支持自动的逻辑删除,自定义SQL需要自己手动处理逻辑删除。

                    例如,在t_address表及其对应的实体Address类中有一个逻辑删除字段deleted,默认值为0:

                    MyBatisPlus详解(三)lambdaQuery、lambdaUpdate、批量新增、代码生成、Db静态工具、逻辑删除

                    要开启MyBatisPlus的逻辑删除功能,需要在application.yml中配置逻辑删除字段:

                    # src/main/resources/application.yaml
                    mybatis-plus:
                      global-config:
                        db-config:
                          logic-delete-field: deleted # 全局逻辑删除的实体字段名
                          logic-delete-value: 1 # 逻辑已删除值(默认为 1)
                          logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
                    

                    接下来编写测试代码:

                    @Test
                    public void testDeleteByLogic() {
                        addressService.removeById(1);
                    }
                    

                    执行以上单元测试,控制台打印信息如下:

                    ==>  Preparing: UPDATE t_address SET deleted=1 WHERE id=? AND deleted=0
                    ==> Parameters: 1(Integer)
                    
                        List  Preparing: SELECT id,user_id,province,city,town,mobile,street,contact,is_default,notes,deleted FROM t_address WHERE deleted=0
                    ==> Parameters: 
                    
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]