欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MybatisPlus service接口功能介紹

 更新時(shí)間:2025年06月11日 14:46:40   作者:麓殤⊙  
這篇文章主要介紹了MybatisPlus service接口功能介紹,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

Service接口

基本用法

MyBatisPlus同時(shí)也提供了service接口,繼承后一些基礎(chǔ)的增刪改查的service代碼,也不需要去書寫。

接口名為Iservice,而Iservice也繼承了IRepository,這里提供的方法跟BaseMapper相比只多不少,整體還是分為增刪改查這幾大類。只不過查詢的類型占大半。

首先先看新增:

image-20250603142307119

save(T):接收一個(gè)泛型參數(shù),

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

saveOrUpdate():接受一個(gè)泛型參數(shù),會(huì)進(jìn)行判斷該對(duì)象有無ID,,如果有則認(rèn)為是一個(gè)Update操作,反之則為Insert操作,saveOrUpdateBatch():方法支持批量新增及更新。

再看刪除操作:

image-20250603142904405

removeById():只刪除一個(gè)

removeByIds():批量刪除,where條件后面用的是in關(guān)鍵字

修改操作:

image-20250603143337614

剩下的都是查詢操作:

將其分為以下幾類:

如果只查一條數(shù)據(jù),就調(diào)用get開頭的方法:

image-20250603143520016

查詢多條數(shù)據(jù)則為list:

image-20250603143704842

listByIds:傳入一個(gè)id的集合,返回一個(gè)List集合

list():查詢?nèi)?,或者基于Wrapper做復(fù)雜查詢

查詢數(shù)量就調(diào)用count開頭的方法:

image-20250603143920148

分頁查詢就調(diào)用page開頭的方法:

image-20250603144022020

image-20250603144112647

在進(jìn)行一些復(fù)雜查詢時(shí),就需要新建Wrapper,步驟較為繁瑣,因此提供了LambdaQuery()方法,返回LambdaQueryChainWrapper,即鏈?zhǔn)骄幊蘔rapper,調(diào)用該方法就可以直接基于LambdaWrapper做查詢,不需要再次新建。

注意事項(xiàng):

我們正常開發(fā)過程中,都是先編譯service接口,在編譯接口實(shí)現(xiàn)類,然后在接口中添加方法,在實(shí)現(xiàn)類中實(shí)現(xiàn)方法,但如果service接口去繼承IService,那么Iservice接口中的方法,實(shí)現(xiàn)類必須全部實(shí)現(xiàn)。這與我們?cè)鹊陌祖蜗敕_突。因此官方為Iservice已經(jīng)提供好了實(shí)現(xiàn)類ServiceImpl,所以我們只需要讓我們的實(shí)現(xiàn)類去繼承Iservice的實(shí)現(xiàn)類。所以我們的業(yè)務(wù)接口繼承Iservice,而接口實(shí)現(xiàn)類繼承Iservice的接口實(shí)現(xiàn)類。這樣我們就達(dá)到了白嫖的目的。

代碼展示:

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

創(chuàng)建測(cè)試類:

 @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("{\"age\":24,\"intro\":\"英文老師\",\"gender\":\"female\"}");
         user.setCreateTime(LocalDateTime.now());
         user.setUpdateTime(LocalDateTime.now());
         userService.save(user);
     }
 }

測(cè)試新增操作:

image-20250603150339707

查詢操作:

image-20250603150616380

小結(jié):

Service接口使用流程:

  • 自定義Service接口繼承Iservice接口
  • 自定義Service實(shí)現(xiàn)類,實(shí)現(xiàn)自定義接口不能夠繼承ServiceImpl類。

進(jìn)階用法

在前面學(xué)習(xí)完Iservice的基本用法后,發(fā)現(xiàn)MyBatisPlus中的BaseMapper以及Iservice接口有許多相似的功能,那么在實(shí)際開發(fā)中應(yīng)該使用哪個(gè)接口提供的方法呢?

接下來通過幾個(gè)案例去探究實(shí)際開發(fā)中如何使用:

案例展示:基于RestFul風(fēng)格實(shí)現(xiàn)下面的接口:

編號(hào)接口請(qǐng)求方式請(qǐng)求路徑請(qǐng)求參數(shù)返回值
1新增用戶POST/users用戶表單實(shí)體
2刪除用戶DELETE/users/{id}用戶ID
3根據(jù)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包以及對(duì)應(yīng)的VO類及DTO類、

UserFormDTO.java

 @Data
 @Schema(name = "用戶表單實(shí)體")
 public class UserFormDTO {
     @Schema(description = "id")
     private Long id;
     @Schema(description = "用戶名")
     private String username;
     @Schema(description = "密碼")
     private String password;
     @Schema(description = "注冊(cè)手機(jī)號(hào)")
     private String phone;
     @Schema(description = "詳細(xì)信息,JSON風(fēng)格")
     private String info;
     @Schema(description = "賬戶余額")
     private Integer status;

UserVO

 @Data
 @Schema(name = "用戶VO實(shí)體")
 public class UserVO {
     @Schema(description = "用戶id")
     private Long id;
     @Schema(description = "用戶名")
     private String username;
     @Schema(description = "詳細(xì)信息")
     private String info;
     @Schema(description = "使用狀態(tài)(1正常,2凍結(jié))")
     private Integer status;
     @Schema(description = "賬戶余額")
     private Integer balance;

然后新建controller包編寫UserController。

在UserController類中編寫接口,前四個(gè)接口業(yè)務(wù)邏輯較簡(jiǎn)單,在conroller層即可完成

 //編寫swagger注解
 @Tag(name = "用戶管理接口")
 @RestController
 @RequestMapping("/users")
 public class UserController {
     @Resource
     private  UserService  userService;
 ?
     @Operation(summary = "新增用戶接口")
     @PostMapping
    public void saveUser(@RequestBody UserFormDTO userDTO){
        // @RequsetBody 將請(qǐng)求類型定義為json
         //1.將DTO拷貝到實(shí)體中
         User user = BeanUtil.copyProperties(userDTO, User.class);
         //2.新增用戶
        userService.save(user);
     }
     @Operation(summary = "刪除用戶接口")
     @DeleteMapping("{id}")
     public void deleteUser(@Parameter(description = "用戶id") @PathVariable("id") Long id){
         userService.removeById(id);
     }
     @Operation(summary = "根據(jù)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 = "根據(jù)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);
     }

第五個(gè)接口:

conroller層:

  @Operation(summary = "根據(jù)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.校驗(yàn)用戶狀態(tài)
     if (user.getStatus() == 2 || user == null) {
         throw new RuntimeException("用戶不存在或者被禁用");
     }
     //3。校驗(yàn)余額是否充足
     if (user.getBalance() < money) {
         throw new RuntimeException("余額不足");
     }
     //4.更新用戶余額
     baseMapper.updateBalanceById(id, money);
 }

mapper層:

注意事項(xiàng):在編譯簡(jiǎn)單接口時(shí)可以直接在controller層調(diào)用MyBatisPlus提供的Iservice接口方法實(shí)現(xiàn),但是遇到一些業(yè)務(wù)邏輯復(fù)雜的業(yè)務(wù)時(shí),需要編寫自定義的業(yè)務(wù)邏輯時(shí),就需要自定義service方法編寫業(yè)務(wù)邏輯了,當(dāng)我們的業(yè)務(wù)需要去編寫自定義的SQL語句時(shí),我們還需要去自定義方法,在mapper層實(shí)現(xiàn)方法。

啟動(dòng):在瀏覽器中進(jìn)入用戶管理接口文檔

image-20250603191321677

測(cè)試新增接口:

image-20250603192901557

測(cè)試成功,查看數(shù)據(jù)庫:

image-20250603192934344

測(cè)試查詢接口:

image-20250603193143298

測(cè)試批量查詢接口:

image-20250603193247302

測(cè)試扣減接口:

image-20250603193341376

測(cè)試成功。

測(cè)試刪除用戶接口:

image-20250603193433212

測(cè)試成功。

總結(jié):

對(duì)于一些簡(jiǎn)單的增刪改查的方法,可以直接在controller層中調(diào)用Iservice接口的方法,無需寫任何的自定義service或者mapper。

只有在業(yè)務(wù)邏輯相對(duì)復(fù)雜,需要自己寫一些業(yè)務(wù)邏輯,而MyBatisPlus只提供基礎(chǔ)的增刪改查,就需要自定義service方法,在其中編寫業(yè)務(wù)邏輯。

而當(dāng)BaseMapper中無法提供需要的增刪改查方法時(shí),就需要去自定義SQL語句,在mapper層中去定義方法,實(shí)現(xiàn)業(yè)務(wù)邏輯。

Lambda方法

基于案例理解:

需求:實(shí)現(xiàn)一個(gè)根據(jù)復(fù)雜條件查詢用戶的接口,查詢條件如下:

  • name: 用戶名關(guān)鍵字,可以為空
  • status: 用戶狀態(tài),可以為空
  • minBalabce: 最小余額,可以為空
  • maxBalance:最大余額,可以為空

就類似于前端頁面中的用戶列表查詢,但是在查詢頂部有幾個(gè)過濾狀態(tài),可以對(duì)名字過濾,可以對(duì)用戶狀態(tài)進(jìn)行過濾,以及余額的管理。

因此實(shí)現(xiàn)該接口就不能直接寫條件,就需要加上判斷,

SQL語句(全手動(dòng)):

 <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>

接下來著手準(zhǔn)備編寫接口。

注意事項(xiàng):在傳入?yún)?shù)較多時(shí),可以將其封裝為對(duì)象傳入。

前置代碼:

UserQuery.java

 @Data
 @Schema(name = "用戶查詢條件實(shí)體")
 public class UserQuery {
     @Schema(description = "用戶名關(guān)鍵字")
     private String name;
     @Schema(description = "用戶狀態(tài)")
     private Integer status;
     @Schema(description = "余額最小值")
     private Integer minBalance;
     @Schema(description = "余額最大值")
     private Integer maxBalance;

Controller層:

 @Operation(summary = "根據(jù)復(fù)雜條件查詢用戶接口")
 @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()
             //相當(dāng)于 <if test="name != null"> and username like #{name} </if>
             .like(name != null, User::getUsername, name)
             //相當(dāng)于 <if test="status != null"> and status = #{status} </if>
             .eq(status != null, User::getStatus, status)
             //相當(dāng)于 <if test="minBalance != null"> and balance > #{minBalance} </if>
             .gt(minBalance != null, User::getBalance, minBalance)
             //相當(dāng)于 <if test="maxBalance != null"> and balance < #{maxBalance} </if>
             .lt(maxBalance != null, User::getBalance, maxBalance)
             .list();
 }

測(cè)試:

image-20250603201647021

測(cè)試成功,

以上演示的是LambdaQuery。

案例展示:Iservice的Lambda更新

需求:改造根據(jù)id修改用戶余額的接口,要求如下

  • 完成對(duì)用戶的校驗(yàn)
  • 完成對(duì)用戶余額校驗(yàn)
  • 如果扣減后余額為0,則將用戶status修改為凍結(jié)狀態(tài)(2)

這與我們前面的扣減余額接口一致,直接在該接口上進(jìn)行修改。

  @Transactional
     public void updateBalanceByIds(Long id, Integer money) {
         //1,查詢用戶
         User user = getById(id);
         //2.校驗(yàn)用戶狀態(tài)
         if (user.getStatus() == 2 || user == null) {
             throw new RuntimeException("用戶不存在或者被禁用");
         }
         //3。校驗(yàn)余額是否充足
         if (user.getBalance() < money) {
             throw new RuntimeException("余額不足");
         }
         //4.更新用戶余額
         int remainBalance = user.getBalance() - money;
         //鏈?zhǔn)胶瘮?shù) 類似于流 需要中間方法 及 結(jié)束方法
         lambdaUpdate()
                 // 相當(dāng)于 set balance = balance - #{money}
                  .set(User::getBalance,  remainBalance)
                 //  相當(dāng)于 <if test="remainBalance == 0"> set status = 2 </if>
                 .set(remainBalance == 0, User::getStatus, 2)
                 // 相當(dāng)于 where id = #{id}
                 .eq(User::getId, id)
                 .eq(User::getBalance,user.getBalance()) //  樂觀鎖
                 .update();
     }

在這里結(jié)束后會(huì)有并發(fā)線程安全問題,如果有多個(gè)線程同時(shí)訪問,兩個(gè)用戶,兩條線程,都來進(jìn)行對(duì)比,最后減去相同的數(shù)據(jù),這樣就會(huì)導(dǎo)致兩條線程中只會(huì)被減去一個(gè)線程。

我們可以采用樂觀鎖(CAS),比較并替換,如果余額不相同,就會(huì)回滾

進(jìn)行測(cè)試:

image-20250603203619872

測(cè)試成功。

案例展示:Iservice的批量新增

需求:批量插入1萬條用戶數(shù)據(jù),并做出對(duì)比:

  • 普通for循環(huán)
  • Iservice的批量插入

普通for循環(huán)

 private User buildUser(int i){
     User user = new User();
     user.setUsername("user" + i);
     user.setPassword("123456");
     user.setPhone(""+(12345678901L  + i));
     user.setBalance(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("耗時(shí):" + (end - start));
 }

測(cè)試結(jié)果:耗時(shí):11148

Iservice的批量插入

 void testSaveBatch2(){
     //插入100次,每次插入1000條數(shù)據(jù)
     long  start = System.currentTimeMillis();
     //準(zhǔn)備一個(gè)容量為1000的集合
     List<User> users = new ArrayList<>(1000);
     for (int i = 0; i < 10000; i++) {
         users.add(buildUser(i));
         //每1000條數(shù)據(jù)插入一次數(shù)據(jù)庫
         if (i % 1000 == 0) {
             userService.saveBatch(users);
             //清空集合
             users.clear();
         }
     }
     long  end = System.currentTimeMillis();
     System.out.println("耗時(shí):" + (end - start));
 }

耗時(shí):1790

提升了十倍的效率,但還是不夠快。

性能分析:

在普通for循環(huán)插入是一條一條插入數(shù)據(jù)庫,每一次訪問數(shù)據(jù)庫就是一次IO操作,進(jìn)行了10000網(wǎng)絡(luò)請(qǐng)求,十分消耗性能

而Iservice的批量插入,MyBatisPlus采用的是JDBC底層的預(yù)編譯方案,Prepare statement 預(yù)編譯:這種方案在便利的過程中把用戶提交的user數(shù)據(jù)對(duì)其進(jìn)行編譯變成SQL語句 。在代碼中就是一千條SQL語句,在執(zhí)行到saveBatch()時(shí)一次性提交到數(shù)據(jù)庫,也就是每1000條數(shù)據(jù)進(jìn)行一次IO操作,只進(jìn)行了10次網(wǎng)絡(luò)請(qǐng)求。但是這種方案是將數(shù)據(jù)編譯成了SQL語句,數(shù)據(jù)庫在執(zhí)行時(shí)還是一條一條執(zhí)行的,因此,性能還是有所損耗。

因此最優(yōu)方案是將1000條數(shù)據(jù)編譯成1條SQL語句,再交于數(shù)據(jù)庫執(zhí)行,這才是批量插入。

兩種方案:

第一種是使用MyBatis使用動(dòng)態(tài)SQL進(jìn)行foreach遍歷1000條數(shù)據(jù),在便利的過程中拼接為一條SQL語句,這樣性能最佳,但是需要我們?nèi)ナ謱慡QL語句,還是有些麻煩

第二種:

使用MyBatisPlus的批處理,加入一個(gè)參數(shù) ,開啟rewriteBathedStatements = true參數(shù),(重寫批處理),這并不是MyBatisPlus中的配置,其實(shí)是MySQL中的配置。

在數(shù)據(jù)庫配置中添加該參數(shù)即可

 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

再次測(cè)試:

耗時(shí):862,有提升了將近一倍,而且數(shù)據(jù)量越大,差距越明顯。

總結(jié):

在批量插入數(shù)據(jù)時(shí),提供了三種方案:

  • 普通for循環(huán)逐條插入速度極差,不推薦(原因:每次只提交一條數(shù)據(jù)插入數(shù)據(jù)庫,數(shù)據(jù)庫也是逐條執(zhí)行)
  • 默認(rèn)情況下MyBatisPlus的批量新增,基于預(yù)編譯的批處理,性能良好(原因:一次性提交100條數(shù)據(jù)插入數(shù)據(jù)庫,但數(shù)據(jù)庫依舊是逐條插入)
  • 配置JDBC參數(shù):rewriteBathedStatements = true,性能最佳(一次性提交100條數(shù)據(jù)插入數(shù)據(jù)庫,數(shù)據(jù)庫也是批量插入)

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

到此這篇關(guān)于MybatisPlus--核心功能--service接口的文章就介紹到這了,更多相關(guān)MybatisPlus service接口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論