MyBatisPlus从零到一:快速入门与核心功能详解(3)

米饭20小时前行业资讯10

2.3 Service 接口:

MybatisPlus 不仅提供了 BaseMapper,还提供了通用的 Service 接口及默认实现,封装了一些常用的 service 模板方法。


通用接口为IService,默认实现为ServiceImpl,其中封装的方法可以分为以下几类:


save:新增

remove:删除

update:更新

get:查询单个结果

list:查询集合结果

count:计数

page:分页查询

2.3.1 CRUD:

这里带着大家一起看看 Mybatis plus 提供的方法都有什么,并解释一些常用的方法。


新增:

image-20241109151016086

  • save是新增单个元素

  • saveBatch是批量新增

  • saveOrUpdate是根据id判断,如果数据存在就更新,不存在则新增

  • saveOrUpdateBatch是批量的新增或修改

  • 删除:

  • image-20241109151106235

  • removeById:根据id删除

  • removeByIds:根据id批量删除

  • removeByMap:根据Map中的键值对为条件删除

  • remove(Wrapper<T>):根据Wrapper条件删除

  • removeBatchByIds:也是根据 id 批量进行删除,但是删除的实现方式不一样(removeByIds:使用一条 SQL 语句,配合 in 子句一次性执行删除操作,性能比较高。removeBatchByIds:会生成多条的 SQL 语句,每条 SQL 语句逐个执行,性能比 removeByIds 差,但是适用于数据量非常大的时候,可以有效避免一条 SQL 语句过长的情况)。

    修改:

  • image-20241109151933564

  • updateById:根据id修改

  • update(Wrapper<T>):根据UpdateWrapper修改,Wrapper中包含set和where部分

  • update(T,Wrapper<T>):按照T内的数据修改与Wrapper匹配到的数据

  • updateBatchById:根据id批量修改

  • Get:

  • image-20241109152108760

  • getById:根据id查询1条数据

  • getOne(Wrapper<T>):根据Wrapper查询1条数据

  • getBaseMapper:获取Service内的BaseMapper的子类,某些时候需要直接调用Mapper内的自定义SQL时可以用这个方法获取到Mapper


  • List:
      image-20241109152322943
        • listByIds:根据id批量查询

        • list(Wrapper<T>):根据Wrapper条件查询多条数据

        • list():查询所有

        Count:

        image-20241109152348078


        • count():统计所有数量

        • count(Wrapper<T>):统计符合Wrapper条件的数据数量

        getBaseMapper:

        当我们在 service 中要调用 Mapper 中自定义 SQL 时,就必须获取 service 对应的 Mapper,就可以通过这个方法:

        image-20241109152710289

        2.3.2 基本用法:

        由于Service中经常需要定义与业务有关的自定义方法,因此我们不能直接使用IService,而是自定义Service接口,然后继承IService的拓展方法,由于都是接口,继承了之后,根据 Java 语法,需要我们自己实现,这显然不符合 Mybatis plus 的创建初衷,于是 Mybatis plus 自己提供了一个 IService 的实现类 ServiceImpl,我们自己的实现类只要继承该实现类,就可以直接使用方法了。


        上面的这段文字可以抽象成下面的这张图片:

        image-20241109153358606

        // UserService 的接口
        public interface IUserService extends IService<User> {
        }
        
        // UserService 的实现类
        @Service
        public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService{
        }

        由于这部分的代码调用演示和上面的都差不多,无非就是一个是在 Controller 层调用,一个是在 Service 层进行调用,所以这里就不再进行演示。


        2.3.3 Lambda:

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


        下面通过两个两个案例分别学习 lambdaQuery() 和 lambdaUpdate()。


        案例一:


        实现一个根据复杂条件查询用户的接口,查询条件如下:


        name:用户名关键字,可以为空

        status:用户状态,可以为空

        minBalance:最小余额,可以为空

        maxBalance:最大余额,可以为空

        代码如下:

        // UserQuery 实体
        @Data
        @ApiModel(description = "用户查询条件实体")
        public class UserQuery {
            @ApiModelProperty("用户名关键字")
            private String name;
            @ApiModelProperty("用户状态:1-正常,2-冻结")
            private Integer status;
            @ApiModelProperty("余额最小值")
            private Integer minBalance;
            @ApiModelProperty("余额最大值")
            private Integer maxBalance;
        }
        
        // Controller 层的代码
        @GetMapping("/list")
            @ApiOperation("通过 UserQuery 查询用户")
            public List<User> queryUser(UserQuery query) {
                //1. 准备工作
                String username = query.getName();
                Integer status = query.getStatus();
                Integer minBalance = query.getMinBalance();
                Integer maxBalance = query.getMaxBalance();
        
                //2. 构造条件并进行查询
                List<User> 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();
                //3. 返回结果
                return users;
            }

        可以发现 lambdaQuery 方法中除了构建条件,还需要在链式编程的最后添加一个list(),这是在告诉 MybatisPlus,我们的调用结果需要是一个 list 集合。这里不仅可以用list(),可选的方法有:


        .one():最多1个结果

        .list():返回集合结果

        .count():返回计数结果

        MybatisPlus 会根据链式编程的最后一个方法来判断最终的返回结果。


        案例二:


        根据 id 修改用户余额,如果扣减后余额为 0,则将用户 status 修改为冻结状态。


        代码实现如下:

        @Transactional
        @Override
        public void deductBalance(Long id, Integer money) {
            //1. 检查用户是否存在
            User user = this.getById(id);
            if(user == null){
                throw new RuntimeException("用户不存在");
            }
            //2. 检查用户状态
            if(user.getStatus() == 2){
                throw new RuntimeException("用户状态异常");
            }
            //3. 检查用户余额是否足够
            if(user.getBalance() - money < 0){
                throw new RuntimeException("用户余额不足");
            }
            // 剩余的金额
            int newMoney = user.getBalance() - money;
            //4. 扣减余额
            this.lambdaUpdate()
                    .set(User::getBalance,newMoney)
                    .set(newMoney == 0, User::getStatus,2)// 动态判断是否要更新状态
                    .eq(User::getId,id)
                    .eq(User::getBalance,user.getBalance())// 乐观锁,防止在多线程的情况下出现异常。
                    .update();
        }

        2.3.4 批量新增:

        IService 中的批量新增功能使用起来非常方便,但有一点注意事项,我们先来测试一下。

        MybatisPlus 的批处理(插入 10 万条数据):

        @Test
        void testSaveBatch() {
            // 准备10万条数据
            List<User> list = new ArrayList<>(1000);
            long b = System.currentTimeMillis();
            for (int i = 1; i <= 100000; i++) {
                list.add(buildUser(i));
                // 每1000条批量插入一次
                if (i % 1000 == 0) {
                    userService.saveBatch(list);
                    list.clear();
                }
            }
            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\"}");
            user.setCreateTime(LocalDateTime.now());
            user.setUpdateTime(user.getCreateTime());
            return user;
        }

        执行最终耗时如下:

        image-20241109180510879

        MybatisPlus的批处理是基于PrepareStatement的预编译模式,然后批量提交,最终在数据库执行时还是会有多条 insert 语句,逐条插入数据。SQL 类似这样:

        Preparing: INSERT INTO user ( username, password, phone, info, balance, create_time, update_time ) VALUES ( ?, ?, ?, ?, ?, ?, ? )
        Parameters: user_1, 123, 18688190001, "", 2000, 2023-07-01, 2023-07-01
        Parameters: user_2, 123, 18688190002, "", 2000, 2023-07-01, 2023-07-01
        Parameters: user_3, 123, 18688190003, "", 2000, 2023-07-01, 2023-07-01

        而如果想要得到最佳性能,最好是将多条 SQL 合并为一条,像这样:

        INSERT INTO user ( username, password, phone, info, balance, create_time, update_time )
        VALUES 
        (user_1, 123, 18688190001, "", 2000, 2023-07-01, 2023-07-01),
        (user_2, 123, 18688190002, "", 2000, 2023-07-01, 2023-07-01),
        (user_3, 123, 18688190003, "", 2000, 2023-07-01, 2023-07-01),
        (user_4, 123, 18688190004, "", 2000, 2023-07-01, 2023-07-01);

        这要怎么进行转化呢?

        MySQL的客户端连接参数中有这样的一个参数:rewriteBatchedStatements。顾名思义,就是重写批处理的statement语句。

        这个参数的默认值是 false,我们需要修改连接参数,将其配置为 true。

        spring:
          datasource:
            url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
            driver-class-name: com.mysql.cj.jdbc.Driver
            username: root
            password: root

        再次测试插入10万条数据,可以发现速度有非常明显的提升:

        image-20241109181407054

        本文系转载,版权归原作者所有,如若侵权请联系我们进行删除!  

        云掣基于多年在运维领域的丰富时间经验,编写了《云运维服务白皮书》,欢迎大家互相交流学习:

        《云运维服务白皮书》下载地址:https://fs80.cn/v2kbbq

        想了解更多大数据运维托管服务、数据库运维托管服务、应用系统运维托管服务的的客户,欢迎点击云掣官网沟通咨询:https://yunche.pro/?t=shequ


相关文章

Linux 配置MySQL环境(三)

Linux 配置MySQL环境(三)

一、下载1. 官网下载MySQL官网:https://www.mysql.com/进入官网之后点击 DOWNLOADS进入页面 ( 在这里我们选择社区版) ,点击 MySQL Community (G...

微服务保护和分布式事务(3)

微服务保护和分布式事务(3)

1.3 请求限流:在簇点链路后面点击流控按钮,即可对其做限流配置:在弹出的菜单中填写:这样就把查询购物车列表这个簇点资源的流量限制在了每秒 6 个,也就是最大 QPS 为 6。1.4 线程隔离:限流可...

CentOS7 yum安装报错“Could not resolve host: mirrorlist.centos.org; Name or service not known“之解决办法(换源)

CentOS7 yum安装报错“Could not resolve host: mirrorlist.centos.org; Name or service not known“之解决办法(换源)

yum安装出现错误如下:yum install -y wget && wget -O install.sh https://download.bt.cn/install/install...

【Docker】1.Docker的前身LXC

【Docker】1.Docker的前身LXC

LXC简介:Linux Containers ,一种操作系统层虚拟化技术,为Linux内核容器功能的一个用户空间接口。将应用软件系统打包成一个软件容器内涵应用软件本身的代码,操作系统核心和库...

windows10家庭版禁用Device/Credential Guard解决方案

windows10家庭版禁用Device/Credential Guard解决方案

背景(禁用的原因)在安装Windows版的docker后,由于Windows版本自带虚拟机,折腾完后却发现原来安装的VMware Workstation Pro和Oracle VM VirtualBo...

Spring AOP 实战指南:从入门到精通(1)

Spring AOP 实战指南:从入门到精通(1)

Spring 框架有两大核心 IoC,AOP。在前面我们已经学习过了 IoC 的相关知识,今天就让我们开始 AOP 的学习。一、AOP 概述Aspect Oriented Programming(面向...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。