MybatisPlus service接口功能介绍

2025-06-12 03:50

本文主要是介绍MybatisPlus service接口功能介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《MybatisPlusservice接口功能介绍》:本文主要介绍MybatisPlusservice接口功能介绍,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友...

Service接口

基本用法

MyBATisPlus同时也提供了service接口,继承后一些基础的增删改查的service代码,也不需要去书写。

接口名为Iservice,而Iservice也继承了IRepository,这里提供的方法跟BaseMapper相比只多不少,整体还是分为增删改查这几大类。只不过查询的类型占大半。

首先先看新增:

MybatisPlus service接口功能介绍

save(T):接收一个泛型参数,

saveBatch():接收一个collection集合,用于批量新增。

saveOrUpdate():接受一个泛型参数,会进行判断该对象有无ID,,如果有则认为是一个Update操作,反之则为Insert操作,saveOrUpdateBatch():方法支持批量新增及更新。

再看删除操作:

MybatisPlus service接口功能介绍

removeById():只删除一个

removeByIds():批量删除,where条件后面用的是in关键字

修改操作:

MybatisPlus service接口功能介绍

剩下的都是查询操作:

将其分为以下几类:

如果只查一条数据,就调用get开头的方法:

MybatisPlus service接口功能介绍

查询多条数据则为list:

MybatisPlus service接口功能介绍

listByIds:传入一个id的集合,返回一个List集合

list():查询全部,或者基于Wrapper做复杂查询

查询数量就调用count开头的方法:

MybatisPlus service接口功能介绍

分页查询就调用page开头的方法:

MybatisPlus service接口功能介绍

MybatisPlus service接口功能介绍

在进行一些复杂查询时,就需要新建Wrapper,步骤较为繁琐,因此提供了LambdaQuery()方法,返回LambdaQueryChainWrapper,即链式编程Wrapper,调用该方法就可以直接基于LambdaWrapper做查询,不需要再次新建。

注意事项:

我们正常开发过程中,都是先编译service接口,在编译接口实现类,然后在接口中添加方法,在实现类中实现方法,但如果service接口去继承IService,那么Iservice接口中的方法,实现类必须全部实现。这与我们原先的白嫖想法冲突。因此官方为Iservice已经提供好了实现类ServiceImpl,所以我们只需要让我们的实现类去继承Iservice的实现类。所以我们的业务接口继承Iservice,而接口实现类继承Iservice的接口实现类。这样我们就达到了白嫖的目的。

代码展示:

public interface UserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

创建测试类:

 @SpringBootTest
 class UserServiceTest {
     @Autowired
     private UserService userService;
     @Test
     void testSaveUser(){
         User user = new User();
 //        user.setId(5L);
         user.setUsername("wew");
         user.setPassword("123456");
         user.setPhone("12345678901");
         user.setBalance(200);
         user.setInfo("{eDVTzx\"age\":24,\"intro\":\"英文老师\",\"gender\":\"female\"}");
         user.setCreateTime(LocalDateTime.now());
         user.setUpdateTime(LocalDateTime.now());
         userService.save(user);
     }
 }

测试新增操作:

MybatisPlus service接口功能介绍

查询操作:

MybatisPlus service接口功能介绍

小结:

Service接口使用流程:

  • 自定义Service接口继承Iservice接口
  • 自定义Service实现类,实现自定义接口不能够继承ServiceImpl类。

进阶用法

在前面学习完Iservice的基本用法后,发现MyBatisPlus中的BaseMapper以及Iservice接口有许多相似的功能,那么在实际开发中应该使用哪个接口提供的方法呢?

接下来通过几个案例去探究实际开发中如何使用:

案例展示:基于RestFul风格实现下面的接口:

编号接口请求方式请求路径请求参数返回值
1新增用户POST/users用户表单实体
2删除用户DELETE/users/{id}用户ID
3根据ID查询用户GET/users/{id}用户ID用户v0
4根基ID批量查询GET/users用户ID集合用户v0集合
5根基ID扣减余额PUT/users/{id}/deduction/{money}用户id以及扣减金额

前置需求:

引入web与swagger的起步依赖

 <dependency>
     <groupId>com.github.xiaoymin</groupId>
     <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
     <version>4.5.0</version>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
     <dependency>
             <groupId>org.springdoc</groupId>
             <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
             <version>2.5.0</version>
         </dependency>
         <dependency>
             <groupId>org.springdoc</groupId>
             <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
             <version>2.5.0</version>

配置swagger信息:

 springdoc:
   swagger-ui:
     path: /swagger-ui.html
     tags-sorter: alpha
     operations-sorter: alpha
   api-docs:
     path: /v3/api-docs
   group-configs:
     - group: default
       paths-to-match: /**
       packages-to-scan: com.lyc.mybatisplusdemo.controller
 
 @Configuration
 public class SwaggerConfig {
 ​
     @Bean
     public OpenAPI customOpenAPI() {
         return new OpenAPI()
                 .info(new Info()
                         .title("用户管理接口文档")
                         .version("1.0.0")
                         .description("用户管理接口文档")
                         .contact(new Contact()
                                 .name("lyc")
                                 .email("2089371290@qq.com")
                                 .url("https://www.dwq.cn")));
     }
 ​
     @Bean
     public GroupedOpenApi defaultApi() {
         return GroupedOpenApi.builder()
                 .group("default")
                 .packagesToScan("com.lyc.mybatisplusdemo.controller")
                 .pathsToMatch("/**")
                 .build();
     }
 }

定义VO包,DTO包以及对应的VO类及DTO类、

UserFormDTO.Java

 @Data
 @Schema(name = "用户表单实体")
 public class UserFormDTO {
     @Schema(description = "id")
     private Long id;
     @Schema(description = "用户名")
     private String username;
     @Schema(description = "密码")
     private String password;
     @Schema(description = "注册手机号")
     private String phone;
     @Schema(description = "详细信息,jsON风格")
     private String info;
     @Schema(description = "账户余额")
     private Integer status;

UserVO

 @Data
 @Schema(name = "用户VO实体")
 public class UserVO {
     @Schema(description = "用户id")
     private Long id;
     @Schema(description = "用户名")
     private String username;
     @Schema(description = "详细信息")
     private String info;
     @Schema(description = "使用状态(1正常,2冻结)")
     private Integer status;
     @Schema(description = "账户余额")
     private Integer balance;

然后新建controller包编写UserController。

在UserController类中编写接口,前四个接口业务逻辑较简单,在conroller层即可完成

 //编写swagger注解
 @Tag(name = "用户管理接口")
 @RestController
 @RequestMapping("/users")
 public class UserController {
     @Resource
     private  UserService  userService;
 ​
     @Operation(summary = "新增用户接口")
     @PostMapping
    public void saveUser(@RequestBody UserFormDTO userDTO){
        // @RequsetBody 将请求类型定义为json
         //1.将DTO拷贝到实体中
         User user = BeanUtil.copyProperties(userDTO, User.class);
         //2.新增用户
        userService.save(user);
     }
     @Operation(summary = "删除用户接口")
     @DeleteMapping("{id}")
     public void deleteUser(@Parwww.chinasem.cnameter(description = "用户id") @PathVariable("id") Long id){
         userService.removeById(id);
     }
     @Operation(summary = "根据ID查询用户接口")
     @GetMapping("{id}")
     public UserVO updateUser(@Parameter(description = "用户id") @PathVariable("id") Long id){
         //1.查询用户
         User user = userService.getById(id);
         //2.拷贝到VO中并返回
         return BeanUtil.copyProperties(user, UserVO.class);
     }
     @Operation(summary = "根据ID批量查询用户接口")
     @PutMapping
     public List<UserVO> updateUser(@Parameter(description = "用户id集合") @RequestParam("ids") List<Long> ids){
         List<User> users = userService.listByIds(ids);
         return BeanUtil.copyToList(users, UserVO.class);
     }

第五个接口:

conroller层:

  @Operation(summary = "根据ID扣减余额")
     @PutMapping("{id}/deduction/{money}")
     public void updateBalanceById(@PathVariable("id") Long id,  @PathVariable("money") Integer money){
        userService.updateBalanceByIds(id, money);
     }
 }

service层:

public void updateBalanceByIds(Long id, Integer money) {
     //1,查询用户
     User user = getById(id);
     //2.校验用户状态
     if (user.getStatus() == 2 || user == null) {
         throw new RuntimeException("用户不存在或者被禁用");
     }
     //3。校验余额是否充足
     if (user.getBalance() < money) {
         throw new RuntimeException("余额不足");
     }
     //4.更新用http://www.chinasem.cn户余额
     baseMapper.updateBalanceById(id, money);
 }

mapper层:

 @Update("update tb_user set balance = balance - #{money} where id = #{id}")
 void updateBalanceById(@Param("id") Long id, @Param("money") Integer money);

注意事项:在编译简单接口时可以直接在controller层调用MyBatisPlus提供的Iservice接口方法实现,但是遇到一些业务逻辑复杂的业务时,需要编写自定义的业务逻辑时,就需要自定义service方法编写业务逻辑了,当我们的业务需要去编写自定义的SQL语句时,我们还需要去自定义方法,在mapper层实现方法。

启动:在浏览器中进入用户管理接口文档

MybatisPlus service接口功能介绍

测试新增接口:

MybatisPlus service接口功能介绍

测试成功,查看数据库

MybatisPlus service接口功能介绍

测试查询接口:

MybatisPlus service接口功能介绍

测试批量查询接口:

MybatisPlus service接口功能介绍

测试扣减接口:

MybatisPlus service接口功能介绍

测试成功。

测试删除用户接口:

MybatisPlus service接口功能介绍

测试成功。

总结:

对于一些简单的增删改查的方法,可以直接在controller层中调用Iservice接口的方法,无需写任何的自定义service或者mapper。

只有在业务逻辑相对复杂,需要自己写一些业务逻辑,而MyBatisPlus只提供基础的增删改查,就需要自定义service方法,在其中编写业务逻辑。

而当BaseMapper中无法提供需要的增删改查方法时,就需要去自定义SQL语句,在mapper层中去定义方法,实现业务逻辑。

Lambda方法

基于案例理解:

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

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

就类似于前端页面中的用户列表查询,但是在查询顶部有几个过滤状态,可以对名字过滤,可以对用户状态进行过滤,以及余额的管理。

因此实现该接口就不能直接写条件,就需要加上判断,

SQL语句(全手动):

 <select id="queryUsers" resultType="com.lyc.mp.domain.po.user">
 select * from tb_user
 <where>
 <if test="name != null">
 and username like #{name}
 </if>
 <if test="status != null">
 and `status` = #{status}
 </if>
 <if test="minBalance != null and MaxBalance != null">
 and balance between #{minBalance} and #{maxBalance}
 and username like #{name}
 </if>
 </where>
 </select>

接下来着手准备编写接口。

注意事项:在传入参数较多时,可以将其封装为对象传入。

前置代码:

UserQuery.java

 @Data
 @Schema(name = "用户查询条件实体")
 public class UserQuery {
     @Schema(description = "用户名关键字")
     private String name;
     @Schema(description = "用户状态")
     private Integer status;
     @Schema(description = "余额最小值")
     private Integer minBalance;
     @Schema(description = "余额最大值")
     private Integer maxBalance;

Controller层:

 @Operation(summary = "根据复杂条件查询用户接口")
 @GetMapping("/list")
 public List<UserVO> getUserList(UserQuery query){
 //1.查询用户
     List<User> users = userService.getUserList(query.getName(),  query.getStatus(), query.getMinBalance(), query.getMaxBalance());
     //2.拷贝到VO中并返回
     return BeanUtil.copyToList(users, UserVO.class);
 }

Service层:

public List<User> getUserList(String name, Integer status, Integer minBalance, Integer maxBalance) {
    return lambdaQuery()
             //相当于 <if test="name != null"> and username like #{name} </if>
             .like(name != null, User::getUsername, name)
             //相当于 <if test="status != null"> and status = #{status} </if>
             .eq(status != null, User::getStatus, status)
             //相当于 <if test="minBalance != null"> and balance > #{minBalance} </if>
             .gt(minBalance != null, User::getBalance, minBalance)
             //相当于 <if test="maxBalance != null"> and balance < #{maxBalance} </if>
             .lt(maxBalance != null, User::getBalance, maxBalance)
             .list();
 }

测试:

MybatisPlus service接口功能介绍

测试成功,

以上演示的是LambdaQuery。

案例展示:Iservice的Lambda更新

需求:改造根据id修改用户余额的接口,要求如下

  • 完成对用户的校验
  • 完成对用户余额校验
  • 如果扣减后余额为0,则将用户status修改为冻结状态(2)

这与我们前面的扣减余额接口一致,直接在该接口上进行修改。

  @Transactional
     public void updateBalanceByIds(Long id, Integer money) {
   China编程      //1,查询用户
         User user = getById(id);
         //2.校验用户状态
         if (user.getStatus() == 2 || user == null) {
             throw new RuntimeException("用户不存在或者被禁用");
         }
         //3。校验余额是否充足
         if (user.getBalance() < money) {
             throw new RuntimeException("余额不足");
         }
         //4.更新用户余额
         int remainBalance = user.getBalance() - money;
         //链式函数 类似于流 需要中间方法 及 结束方法
         lambdaUpdate()
                 // 相当于 set balance = balance - #{money}
                  .set(User::getBalance,  remainBalance)
                 //  相当于 <if test="remainBalance == 0"> set status = 2 </if>
                 .set(remainBalance == 0, User::getStatus, 2)
                 // 相当于 where id = #{id}
                 .eq(User::getId, id)
                 .eq(User::getBalance,user.getBalance()) //  乐观锁
                 .update();
     }

在这里结束后会有并发线程安全问题,如果有多个线程同时访问,两个用户,两条线程,都来进行对比,最后减去相同的数据,这样就会导致两条线程中只会被减去一个线程。

我们可以采用乐观锁(CAS),比较并替换,如果余额不相同,就会回滚

进行测试:

MybatisPlus service接口功能介绍

测试成功。

案例展示:Iservice的批量新增

需求:批量插入1万条用户数据,并做出对比:

  • 普通for循环
  • Iservice的批量插入

普通for循环

 private User buildUser(int i){
     User user = new User();
     user.setUsername("user" + i);
     user.setPassword("123456");
     user.setPhone(""+(12345678901L  + i));
     user.seDVTzxetBalance(2000);
     user.setInfo("{\"age\":24,\"intro\":\"英文老师\",\"gender\":\"female\"}");
     user.setCreateTime(LocalDateTime.now());
     user.setUpdateTime(LocalDateTime.now());
     return user;
 }
 @Test
 void testSaveBatch(){
     long  start = System.currentTimeMillis();
     for (int i = 0; i < 10000; i++) {
         userService.save(buildUser(i));
     }
     long  end = System.currentTimeMillis();
     System.out.println("耗时:" + (end - start));
 }

测试结果:耗时:11148

Iservice的批量插入

 void testSaveBatch2(){
     //插入100次,每次插入1000条数据
     long  start = System.currentTimeMillis();
     //准备一个容量为1000的集合
     List<User> users = new ArrayList<>(1000);
     for (int i = 0; i < 10000; i++) {
         users.add(buildUser(i));
         //每1000条数据插入一次数据库
         if (i % 1000 == 0) {
             userService.saveBatch(users);
             //清空集合
             users.clear();
         }
     }
     long  end = System.currentTimeMillis();
     System.out.println("耗时:" + (end - start));
 }

耗时:1790

提升了十倍的效率,但还是不够快。

性能分析:

在普通for循环插入是一条一条插入数据库,每一次访问数据库就是一次IO操作,进行了10000网络请求,十分消耗性能

而Iservice的批量插入,MyBatisPlus采用的是JDBC底层的预编译方案,Prepare statement 预编译:这种方案在便利的过程中把用户提交的user数据对其进行编译变成SQL语句 。在代码中就是一千条SQL语句,在执行到saveBatch()时一次性提交到数据库,也就是每1000条数据进行一次IO操作,只进行了10次网络请求。但是这种方案是将数据编译成了SQL语句,数据库在执行时还是一条一条执行的,因此,性能还是有所损耗。

因此最优方案是将1000条数据编译成1条SQL语句,再交于数据库执行,这才是批量插入。

两种方案:

第一种是使用MyBatis使用动态SQL进行foreach遍历1000条数据,在便利的过程中拼接为一条SQL语句,这样性能最佳,但是需要我们去手写SQL语句,还是有些麻烦

第二种:

使用MyBatisPlus的批处理,加入一个参数 ,开启rewriteBathedStatements = true参数,(重写批处理),这并不是MyBatisPlus中的配置,其实是mysql中的配置。

在数据库配置中添加该参数即可

 spring:
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
     url: jdbc:mysql://localhost:3306/mp?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&rewriteBatchedStatements=true
     username: root
     password: 123456

再次测试:

耗时:862,有提升了将近一倍,而且数据量越大,差距越明显。

总结:

在批量插入数据时,提供了三种方案:

  • 普通for循环逐条插入速度极差,不推荐(原因:每次只提交一条数据插入数据库,数据库也是逐条执行)
  • 默认情况下MyBatisPlus的批量新增,基于预编译的批处理,性能良好(原因:一次性提交100条数据插入数据库,但数据库依旧是逐条插入)
  • 配置JDBC参数:rewriteBathedStatements = true,性能最佳(一次性提交100条数据插入数据库,数据库也是批量插入)

以上就是service接口的全部用法,让我们一起加油!

到此这篇关于MybatisPlus--核心功能--service接口的文章就介绍到这了,更多相关MybatisPlus service接口内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于MybatisPlus service接口功能介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155021

相关文章

Java实现预览与打印功能详解

《Java实现预览与打印功能详解》在Java中,打印功能主要依赖java.awt.print包,该包提供了与打印相关的一些关键类,比如PrinterJob和PageFormat,它们构成... 目录Java 打印系统概述打印预览与设置使用 PageFormat 和 PrinterJob 类设置页面格式与纸张

MySQL 8 中的一个强大功能 JSON_TABLE示例详解

《MySQL8中的一个强大功能JSON_TABLE示例详解》JSON_TABLE是MySQL8中引入的一个强大功能,它允许用户将JSON数据转换为关系表格式,从而可以更方便地在SQL查询中处理J... 目录基本语法示例示例查询解释应用场景不适用场景1. ‌jsON 数据结构过于复杂或动态变化‌2. ‌性能要

zookeeper端口说明及介绍

《zookeeper端口说明及介绍》:本文主要介绍zookeeper端口说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、zookeeper有三个端口(可以修改)aVNMqvZ二、3个端口的作用三、部署时注意总China编程结一、zookeeper有三个端口(可以

Qt使用QSqlDatabase连接MySQL实现增删改查功能

《Qt使用QSqlDatabase连接MySQL实现增删改查功能》这篇文章主要为大家详细介绍了Qt如何使用QSqlDatabase连接MySQL实现增删改查功能,文中的示例代码讲解详细,感兴趣的小伙伴... 目录一、创建数据表二、连接mysql数据库三、封装成一个完整的轻量级 ORM 风格类3.1 表结构

Python中win32包的安装及常见用途介绍

《Python中win32包的安装及常见用途介绍》在Windows环境下,PythonWin32模块通常随Python安装包一起安装,:本文主要介绍Python中win32包的安装及常见用途的相关... 目录前言主要组件安装方法常见用途1. 操作Windows注册表2. 操作Windows服务3. 窗口操作

mysql表操作与查询功能详解

《mysql表操作与查询功能详解》本文系统讲解MySQL表操作与查询,涵盖创建、修改、复制表语法,基本查询结构及WHERE、GROUPBY等子句,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随... 目录01.表的操作1.1表操作概览1.2创建表1.3修改表1.4复制表02.基本查询操作2.1 SE

MyBatisPlus如何优化千万级数据的CRUD

《MyBatisPlus如何优化千万级数据的CRUD》最近负责的一个项目,数据库表量级破千万,每次执行CRUD都像走钢丝,稍有不慎就引起数据库报警,本文就结合这个项目的实战经验,聊聊MyBatisPl... 目录背景一、MyBATis Plus 简介二、千万级数据的挑战三、优化 CRUD 的关键策略1. 查

Golang如何用gorm实现分页的功能

《Golang如何用gorm实现分页的功能》:本文主要介绍Golang如何用gorm实现分页的功能方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录背景go库下载初始化数据【1】建表【2】插入数据【3】查看数据4、代码示例【1】gorm结构体定义【2】分页结构体

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

Java Web实现类似Excel表格锁定功能实战教程

《JavaWeb实现类似Excel表格锁定功能实战教程》本文将详细介绍通过创建特定div元素并利用CSS布局和JavaScript事件监听来实现类似Excel的锁定行和列效果的方法,感兴趣的朋友跟随... 目录1. 模拟Excel表格锁定功能2. 创建3个div元素实现表格锁定2.1 div元素布局设计2.