【Springcloud微服務】MybatisPlus下篇

🔥 本文由 程序喵正在路上 原創,CSDN首發!
💖 系列專欄:Springcloud微服務
🌠 首發時間:2024年6月4日
🦋 歡迎關注🖱點贊👍收藏🌟留言🐾

目錄

  • 擴展功能
    • 代碼生成
    • 靜態工具
    • 邏輯刪除
    • 枚舉處理器
    • JSON處理器
  • 插件功能
    • 分頁插件
    • 通用分頁實體

擴展功能

代碼生成

在使用 MybatisPlus 以后,基礎的 Mapper、Service、PO 代碼相對固定,重復編寫也比較麻煩。因此 MybatisPlus 官方提供了代碼生成器根據數據庫表結構生成 PO、Mapper、Service 等相關代碼。只不過代碼生成器同樣要編碼使用,也很麻煩。

所以這里推薦使用另外一款 MybatisPlus 的插件,它可以基于圖形化界面完成 MybatisPlus 的代碼生成,非常簡單。

安裝插件

在 Idea 的 plugins 市場中搜索并安裝 MyBatisPlus 插件:

在這里插入圖片描述
然后重啟你的 Idea 即可使用。

使用

剛好數據庫中還有一張 address 表尚未生成對應的實體和 mapper 等基礎代碼,我們可以利用插件生成一下。

首先需要配置數據庫地址,在 Idea 頂部菜單中,找到 other,選擇 Config Database,然后填寫一些信息:
在這里插入圖片描述

然后再次點擊 Idea 頂部菜單中的 other,然后選擇 Code Generator,填寫表單信息:

在這里插入圖片描述
示例:

在這里插入圖片描述

在這里插入圖片描述

靜態工具

有的時候 Service 之間也會相互調用,為了避免出現循環依賴問題,MybatisPlus 提供一個靜態工具類:Db,其中的一些靜態方法與 IService 中方法簽名基本一致,也可以幫助我們實現 CRUD 功能:

在這里插入圖片描述
下面,我們通過一些案例來學習使用靜態工具。

需求:

1、改造根據 id 查詢用戶的接口,查詢用戶的同時,查詢出用戶對應的所有地址

由于現在需要額外返回收貨地址,所以我們需要定義一個收貨地址的 VO:

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "收貨地址VO")
public class AddressVO {@ApiModelProperty("id")private Long id;@ApiModelProperty("用戶ID")private Long userId;@ApiModelProperty("省")private String province;@ApiModelProperty("市")private String city;@ApiModelProperty("縣/區")private String town;@ApiModelProperty("手機")private String mobile;@ApiModelProperty("詳細地址")private String street;@ApiModelProperty("聯系人")private String contact;@ApiModelProperty("是否是默認 1默認 0否")private Boolean isDefault;@ApiModelProperty("備注")private String notes;
}

然后在 UserVO 中添加一個屬性:

在這里插入圖片描述

修改 UserController 中根據 id 查詢用戶的業務接口,新建一個方法:

/*** 根據id查詢用戶** @param userId* @return*/
@GetMapping("/{id}")
@ApiOperation("根據id查詢用戶")
public UserVO queryUserById(@ApiParam("用戶id") @PathVariable("id") Long userId) {return userService.queryUserAndAddressById(userId);
}

IUserService 接口中聲明該方法:

/*** 根據id查詢用戶及其收貨地址** @param userId* @return*/
UserVO queryUserAndAddressById(Long userId);

UserServiceImpl 中實現方法:

/*** 根據id查詢用戶及其收貨地址** @param userId* @return*/
public UserVO queryUserAndAddressById(Long userId) {// 1.查詢用戶User user = getById(userId);if (user == null || user.getStatus() == 2) {throw new RuntimeException("用戶狀態異常!");}// 2.查詢收貨地址列表List<Address> addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, userId).list();// 3.封裝數據UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);// 先判斷收貨地址列表是否為空if (CollUtil.isNotEmpty(addresses)) {userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));}// 4.返回數據return userVO;
}

測試:

在這里插入圖片描述

2、改造根據 id 批量查詢用戶的接口,查詢用戶的同時,查詢出用戶對應的所有地址

UserController:

/*** 根據id集合查詢用戶** @param ids* @return*/
@GetMapping
@ApiOperation("根據id集合查詢用戶")
public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids) {return userService.queryUserAndAddressByIds(ids);
}

UserServiceImpl:

/*** 根據id集合查詢用戶及其收貨地址** @param ids* @return*/
public List<UserVO> queryUserAndAddressByIds(List<Long> ids) {// 1.查詢用戶List<User> users = listByIds(ids);if (CollUtil.isEmpty(users)) {return Collections.emptyList(); // 返回空列表}// 2.查詢收貨地址List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, ids).list(); //根據用戶id查詢收貨地址List<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);    //轉化地址VO//將用戶收貨地址分組處理,相同用戶的放入一個集合中Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);if (CollUtil.isNotEmpty(addressVOList)) {addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));}// 3.封裝數據List<UserVO> list = new ArrayList<>(users.size());for (User user : users) {UserVO vo = BeanUtil.copyProperties(user, UserVO.class);    // 拷貝用戶信息vo.setAddresses(addressMap.get(user.getId()));  // 設置用戶收貨地址list.add(vo);}return list;
}

測試:

在這里插入圖片描述

邏輯刪除

邏輯刪除就是基于代碼邏輯模擬刪除效果,但并不會真正刪除數據庫中的數據。思路如下:

  • 在表中添加一個字段標記數據是否被刪除
  • 當刪除數據時把標記置為 1
  • 查詢時只查詢標記為 0 的數據

一旦采用了邏輯刪除,所有的查詢和刪除邏輯都要跟著變化,非常麻煩。

為了解決這個問題,MybatisPlus 提供了邏輯刪除功能,無需改變方法調用的方式,而是在底層幫我們自動修改 CRUD 的語句。我們要做的就是在 application.yaml 文件中配置邏輯刪除的字段名稱和值即可:

在 Address 實體類中已經存在一個字段 deleted 用于邏輯刪除,所以我們不用再定義:

在這里插入圖片描述

mybatis-plus:global-config:db-config:logic-delete-field: deleted # 全局邏輯刪除的實體字段名(since 3.3.0,配置后可以忽略不配置步驟2)logic-delete-value: 1		 # 邏輯已刪除值(默認為 1, 可以不配置)logic-not-delete-value: 0 # 邏輯未刪除值(默認為 0, 可以不配置)

注意,只有 MybatisPlus 生成的 SQL 語句才支持自動的邏輯刪除,自定義 SQL 需要自己手動處理邏輯刪除。

寫一個測試類測試一下:

package com.itheima.mp.service;import com.itheima.mp.domain.po.Address;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTest
class IAddressServiceTest {@Autowiredprivate IAddressService addressService;@Testvoid testDeleteByLogic() {// 刪除方法與以前沒有區別addressService.removeById(59L);}@Testvoid testQuery() {List<Address> list = addressService.list();list.forEach(System.out::println);}
}

先執行第一個測試方法,進行刪除,結果如下:

在這里插入圖片描述

再執行第二個方法查詢一下,結果如下:

在這里插入圖片描述

綜上, 開啟了邏輯刪除功能以后,我們就可以像普通刪除一樣做 CRUD,基本不用考慮代碼邏輯問題。還是非常方便的。

但是,邏輯刪除本身也有自己的問題,比如:

  • 會導致數據庫表垃圾數據越來越多,影響查詢效率
  • SQL 中全都需要對邏輯刪除字段做判斷,影響查詢效率

因此,還是不太推薦采用邏輯刪除功能,如果數據不能刪除,可以采用把數據遷移到其它表的辦法。

枚舉處理器

User 類中有一個用戶狀態字段:

在這里插入圖片描述

像這種字段我們一般會定義一個枚舉,做業務判斷的時候就可以直接基于枚舉做比較。但是我們數據庫采用的是 int 類型,對應的PO也是 Integer。因此業務操作時必須手動把枚舉與 Integer 轉換,非常麻煩。

因此,MybatisPlus 提供了一個處理枚舉的類型轉換器,可以幫我們把枚舉類型與數據庫類型自動轉換。

定義枚舉

我們定義一個用戶狀態的枚舉:

package com.itheima.mp.enums;import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;@Getter
public enum UserStatus {NORMAL(1, "正常"),FREEZE(2, "凍結");private final int value;private final String desc;UserStatus(int value, String desc) {this.value = value;this.desc = desc;}
}

項目結構如下:

在這里插入圖片描述

然后把 User 類中和 UserVO 中的 status 字段改為 UserStatus 類型。

將 UserServiceImpl 代碼中的 2 替換為 UserStatus.FREEZE,顯得更專業一點。

要讓 MybatisPlus 處理枚舉與數據庫類型自動轉換,我們必須告訴 MybatisPlus,枚舉中的哪個字段的值作為數據庫值。

MybatisPlus 中提供了 @EnumValue 注解來標記枚舉屬性:

在這里插入圖片描述

配置枚舉處理器

在 application.yaml 文件中添加配置:

mybatis-plus:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

然后我們測試一下查詢:

在這里插入圖片描述

查詢成功,不過查詢出的 User 類的 status 字段是枚舉類型,可能不是很好理解。

我們在 UserStatus 枚舉中通過 @JsonValue 注解標記 JSON 序列化時要展示的字段,添加在 value 或者 desc 上都可以:

比如,我們添加在 desc 上,就會顯示 “正常” 或者 “凍結”:

在這里插入圖片描述

在這里插入圖片描述

JSON處理器

數據庫的 user 表中有一個 info 字段,是 JSON 類型:

在這里插入圖片描述

而目前我們的 User 實體類中卻是 String 類型的:

在這里插入圖片描述

這樣一來,我們要讀取 info 中的屬性時就非常不方便。如果要方便獲取,info 的類型最好是一個 Map 或者實體類。

而一旦我們把 info 改為對象類型,就需要在寫入數據庫時手動轉為 String,再讀取數據庫時,手動轉換為對象,這將會非常麻煩。

因此,MybatisPlus 為我們提供了很多特殊類型字段的類型處理器,解決特殊字段類型與數據庫類型轉換的問題。例如處理 JSON 就可以使用 JacksonTypeHandler 處理器。

接下來,我們就來看看這個處理器該如何使用。

定義實體

首先,我們在 po 下定義一個單獨實體類 UserInfo 來與 info 字段的屬性匹配:

package com.itheima.mp.domain.po;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")
public class UserInfo {private Integer age;private String intro;private String gender;
}

使用類型處理器

接下來,將 User 類的 info 字段修改為 UserInfo 類型,并聲明類型處理器,同時開啟結果自動映射;UserVO 中的 info 字段也需要修改為 UserInfo 類型:

package com.itheima.mp.domain.po;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.itheima.mp.enums.UserStatus;
import lombok.Data;import java.time.LocalDateTime;@Data
@TableName(value = "user", autoResultMap = true)    //開啟結果自動映射
public class User {@TableId(type = IdType.AUTO)    //不指定的話,默認為隨機生成id,也就是第三種方式private Long id;                //用戶idprivate String username;        //用戶名private String password;        //密碼private String phone;           //注冊手機號@TableField(typeHandler = JacksonTypeHandler.class)private UserInfo info;            //詳細信息private UserStatus status;      //使用狀態(1正常 2凍結)private Integer balance;        //賬戶余額private LocalDateTime createTime;//創建時間private LocalDateTime updateTime;//更新時間
}

將測試方法中關于設置詳細信息的代碼修改一下:

在這里插入圖片描述

重啟服務,測試一下查詢接口,可以看到信息成功返回:

在這里插入圖片描述

插件功能

MybatisPlus 提供了很多的插件功能,進一步拓展其功能。目前已有的插件有:

  • PaginationInnerInterceptor:自動分頁
  • TenantLineInnerInterceptor:多租戶
  • DynamicTableNameInnerInterceptor:動態表名
  • OptimisticLockerInnerInterceptor:樂觀鎖
  • IllegalSQLInnerInterceptor:sql 性能規范
  • BlockAttackInnerInterceptor:防止全表更新與刪除

最常用的是自動分頁插件。

分頁插件

在未引入分頁插件的情況下,MybatisPlus 是不支持分頁功能的,IService 和 BaseMapper 中的分頁方法都無法正常起效。所以,我們必須配置分頁插件。

在項目中新建一個配置類,項目結構如下:

在這里插入圖片描述

package com.itheima.mp.config;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MybatisConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {// 1.初始化核心插件MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 2.創建分頁插件PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();paginationInnerInterceptor.setMaxLimit(1000L);  //設置最大分頁限制// 3.添加分頁插件interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;}
}

在 IUserServiceTest 中寫一個分頁查詢的測試方法:

@Test
void testPageQuery() {// 1.準備分頁條件int pageNum = 1, pageSize = 2;  //頁碼、每頁大小Page<User> page = Page.of(pageNum, pageSize);// 2.排序條件page.addOrder(new OrderItem("balance", true));  // 先按余額升序排序page.addOrder(new OrderItem("id", true));       // 再按id升序排序// 3.分頁查詢Page<User> p = userService.page(page);// 4.解析數據long total = p.getTotal();  // 總條數System.out.println("total = " + total);long pages = p.getPages();// 總頁數System.out.println("pages = " + pages);List<User> users = p.getRecords();  // 當前頁碼的數據記錄users.forEach(System.out::println);
}

結果:

在這里插入圖片描述

通用分頁實體

需求:遵循下面的接口規范,編寫一個 UserController 接口,實現 User 的分頁查詢。

在這里插入圖片描述
定義實體

這里需要定義3個實體:

  • UserQuery:分頁查詢條件的實體,包含分頁、排序參數、過濾條件
  • PageDTO:分頁結果實體,包含總條數、總頁數、當前頁數據
  • UserVO:用戶頁面視圖實體(已存在)

雖然 UserQuery 之前已經定義過了,并且其中已經包含了過濾條件,其中缺少的僅僅是分頁條件,但是分頁條件不僅僅是用戶分頁查詢需要,以后其它業務也都有分頁查詢的需求。因此建議將分頁查詢條件單獨定義為一個 PageQuery 實體:

PageQuery 是前端提交的查詢參數,一般包含四個屬性:

  • pageNo:頁碼
  • pageSize:每頁數據條數
  • sortBy:排序字段
  • isAsc:是否升序
package com.itheima.mp.domain.query;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "分頁查詢實體")
public class PageQuery {@ApiModelProperty("頁碼")private Long pageNo;@ApiModelProperty("每頁數據條數")private Long pageSize;@ApiModelProperty("排序字段")private String sortBy;@ApiModelProperty("是否升序")private Boolean isAsc;
}

然后,讓我們的 UserQuery 繼承這個實體:

package com.itheima.mp.domain.query;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel(description = "用戶查詢條件實體")
public class UserQuery extends PageQuery{@ApiModelProperty("用戶名關鍵字")private String name;@ApiModelProperty("用戶狀態:1-正常,2-凍結")private Integer status;@ApiModelProperty("余額最小值")private Integer minBalance;@ApiModelProperty("余額最大值")private Integer maxBalance;
}

最后,則是分頁實體 PageDTO,由于在其它微服務項目中可能也會使用到這個分頁實體,我們將其定為為 DTO:

package com.itheima.mp.domain.dto;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.List;@Data
@ApiModel(description = "分頁結果")
public class PageDTO<T> {@ApiModelProperty("總條數")private Long total;@ApiModelProperty("總頁數")private Long pages;@ApiModelProperty("集合")private List<T> list;
}

開發接口

在 UserController 中定義分頁查詢用戶的接口:

/*** 根據條件分頁查詢用戶** @param query* @return*/
@GetMapping("/page")
@ApiOperation("根據條件分頁查詢用戶接口")
public PageDTO<UserVO> queryUsersPage(UserQuery query) {return userService.queryUsersPage(query);
}

在 IUserService 中創建 queryUsersPage 方法:

/*** 根據條件分頁查詢用戶** @param query* @return*/
PageDTO<UserVO> queryUsersPage(UserQuery query);

在 UserServiceImpl 中實現該方法:

/*** 根據條件分頁查詢用戶** @param query* @return*/
public PageDTO<UserVO> queryUsersPage(UserQuery query) {String name = query.getName();Integer status = query.getStatus();// 構建分頁條件Page<User> page = Page.of(query.getPageNo(), query.getPageSize()); 3// 排序條件if (StrUtil.isNotBlank(query.getSortBy())) {// 不為空page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));} else {// 為空,默認按照更新時間排序page.addOrder(new OrderItem("update_time", query.getIsAsc()));}// 分頁查詢Page<User> p = lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).page(page);//封裝VO結果PageDTO<UserVO> dto = new PageDTO<>();dto.setTotal(p.getTotal());dto.setPages(p.getPages());List<User> records = p.getRecords();if (CollUtil.isEmpty(records)) {// 為空,設置為空列表dto.setList(Collections.emptyList());} else {// 不為空dto.setList(BeanUtil.copyToList(records, UserVO.class));}return dto;
}

測試一下:

在這里插入圖片描述

改造PageQuery實體

在上面的代碼中,從 PageQuery 到 MybatisPlus 的 Page 之間轉換的過程還是比較麻煩的。

我們完全可以在 PageQuery 這個實體中定義一個工具方法,來簡化開發。

package com.itheima.mp.domain.query;import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "分頁查詢實體")
public class PageQuery {@ApiModelProperty("頁碼")private Long pageNo = 1L;@ApiModelProperty("每頁數據條數")private Long pageSize = 2L;@ApiModelProperty("排序字段")private String sortBy;@ApiModelProperty("是否升序")private Boolean isAsc = true;// 多個參數public <T> Page<T> toMpPage(OrderItem... orders) {// 分頁條件Page<T> page = Page.of(pageNo, pageSize);// 排序條件if (StrUtil.isNotBlank(sortBy)) {// 不為空page.addOrder(new OrderItem(sortBy, isAsc));} else if (orders != null) {// 為空page.addOrder(orders);}return page;}// 只有一個參數public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc) {return toMpPage(new OrderItem(defaultSortBy, isAsc));}// 沒有參數,希望按照創建時間排序public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {return toMpPage("create_time", false);}// 沒有參數,希望按照更新時間排序public <T> Page<T> toMpPageDefaultSortByUpdateTimeDesc() {return toMpPage("update_time", false);}
}

這樣我們在開發也時就可以省去對從 PageQuery 到 Page 的的轉換:

// 構建分頁條件
Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();

改造PageDTO實體

同理,在查詢出分頁結果后,數據的非空校驗,數據的 vo 轉換都是模板代碼,編寫起來很麻煩。

我們完全可以將其封裝到 PageDTO 的工具方法中,簡化整個過程:

package com.itheima.mp.domain.dto;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;@Data
@ApiModel(description = "分頁結果")
public class PageDTO<T> {@ApiModelProperty("總條數")private Long total;@ApiModelProperty("總頁數")private Long pages;@ApiModelProperty("集合")private List<T> list;// 需要傳結果類型的字節碼public static <PO, VO> PageDTO<VO> of(Page<PO> p, Class<VO> clazz) {PageDTO<VO> dto = new PageDTO<>();dto.setTotal(p.getTotal());     // 總條數dto.setPages(p.getPages());     // 總頁數// 當前頁數據List<PO> records = p.getRecords();if (CollUtil.isEmpty(records)) {// 為空,設置為空里欸博愛dto.setList(Collections.emptyList());} else {// 不為空dto.setList(BeanUtil.copyToList(records, clazz));}return dto;}// 需要傳PO如何轉換為VO的方法public static <PO, VO> PageDTO<VO> of(Page<PO> p, Function<PO, VO> convertor) {PageDTO<VO> dto = new PageDTO<>();dto.setTotal(p.getTotal());     // 總條數dto.setPages(p.getPages());     // 總頁數// 當前頁數據List<PO> records = p.getRecords();if (CollUtil.isEmpty(records)) {// 為空,設置為空里欸博愛dto.setList(Collections.emptyList());} else {// 不為空dto.setList(records.stream().map(convertor).collect(Collectors.toList()));}return dto;}
}

最終,業務層的代碼可以簡化為:

public PageDTO<UserVO> queryUsersPage(UserQuery query) {String name = query.getName();Integer status = query.getStatus();// 構建分頁條件Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();// 分頁查詢Page<User> p = lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).page(page);return PageDTO.of(p, UserVO.class);
}

如果是希望自定義 PO 到 VO 的轉換過程,可以這樣做:

return PageDTO.of(p, user -> {// 1.拷貝基礎屬性UserVO vo = BeanUtil.copyProperties(user, UserVO.class);// 2.處理特殊邏輯:比如用戶名脫敏String username = vo.getUsername();vo.setUsername(username.substring(0, username.length() - 2) + "**");return vo;
});

測試一下:

在這里插入圖片描述

在改進的過程中,我們都是在實體類中添加了對應的方法來簡化我們的開發,這也意味著這些方法和實體類耦合了。因為我們使用的是 MP,所以可以這樣寫,如果使用的不是 MP,我們可以考慮定義一些工具類來實現這些類型之間的轉換,以此讓方法與實體類解耦合。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/22214.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/22214.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/22214.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

24、matlab二維和三維網格(meshgrid函數)以及散點數據插值 griddata()函數

1、二維和三維網格(meshgrid函數) 語法 語法1:[X,Y] = meshgrid(x,y) 基于向量 x 和 y 中包含的坐標返回二維網格坐標。 語法2:[X,Y] = meshgrid(x) 與 [X,Y] = meshgrid(x,x) 相同,并返回網格大小為 length(x)length(x) 的方形網格坐標。 語法3:[X,Y,Z] = meshgrid(x,y,…

汽車銷售門店零售價格違規檢查的實踐經驗方法

隨著汽車市場的蓬勃發展&#xff0c;汽車銷售門店的零售價格合規性日益受到業界和消費者的關注。為確保銷售過程的公平與透明&#xff0c;開展零售價格違規檢查顯得尤為重要。 在這方面&#xff0c;深圳神秘顧客&#xff08;SMS&#xff09;公司憑借其深厚的實踐經驗和專業技巧…

弘君資本炒股開戶:如何看待股價波動?

在股票商場上股價的動搖無疑是投資者最為關心的話題之一&#xff0c;面臨股價的起伏不定投資者往往會感到迷茫和焦慮。關于怎么看待股價動搖&#xff0c;弘君資本下面就為大家詳細介紹一下。 股價動搖是股市運行的常態&#xff0c;股市是國民經濟的晴雨表&#xff0c;股票價格…

Flink run 自動化運行任務shell腳本

Linux命令行&#xff1a; sh flink_run.sh test com.dzj.app.base.test.FlinkKafkaOffsetTest /root/soft/test.jar flink_run.sh腳本內容&#xff1a; #!/bin/bash# 檢查參數數量是否正確 if [ "$#" -ne 3 ]; thenecho "錯誤&#xff1a;需要提供 3 個參數&…

SpringBoot+layui實現Excel導入操作

excel導入步驟 第三方插件引入插件 效果圖 &#xff08;方法1&#xff09;代碼實現&#xff08;方法1&#xff09;Html代碼&#xff08; 公共&#xff09;下載導入模板 js實現 &#xff08;方法1&#xff09;上傳文件實現 效果圖&#xff08;方法2&#xff09;代碼實現&#xf…

多語言大模型 Aya-23 開源!覆蓋23種語言,性能刷新SOTA

文章目錄 1. Aya-23 技術特點1.1 預訓練階段1.2 指令微調階段 2. Aya-23 性能表現3. Aya-23 多語言任務評估4. Aya-23 支持 23 種語言5. Aya-23 應用場景 近年來&#xff0c;多語言大模型&#xff08;MLLM&#xff09;發展迅速&#xff0c;但大多數模型的性能依然存在顯著差距&…

“滴滴打車,用友入賬”,YonSuite商旅費控助力企業“降低成本”更進一步

在當今競爭激烈的商業環境中&#xff0c;企業對于成本控制和效率提升的需求日益迫切。特別是在商旅管理方面&#xff0c;如何有效整合資源、優化流程、降低費用&#xff0c;成為了成長型企業關注的焦點。用友YonSuite商旅費控作為用友集團旗下的重要產品&#xff0c;憑借其卓越…

ctfshow pwn17-18

毛坯的人生和精裝的朋友圈 pwn17 while ( 1 ){menu();v4 0;puts("\nEnter the command you want choose:(1.2.3.4 or 5)\n");__isoc99_scanf("%d", &v4);switch ( v4 ){case 1:system("id");break;case 2:puts("Which directory?(/,…

克隆別人的項目并上傳到自己的倉庫

克隆別人的項目并上傳到自己的倉庫通常涉及以下步驟&#xff1a; 克隆項目&#xff1a;首先&#xff0c;你需要將別人的項目克隆到你的本地計算機。可以使用以下Git命令&#xff1a; git clone [項目的URL]將 [項目的URL] 替換為你想克隆的項目的URL。 創建新的倉庫&#xff1…

卡爾曼濾波算法的matlab實現

卡爾曼濾波算法的matlab實現 figure; hold on;Z(1:1:100); %觀測值&#xff1a;第一秒觀測1m 第二秒觀測兩米 勻速運動, 每秒1m, 最后擬合的也是速度 1m/splot(Z); plot([0,100], [1,1]);noiserandn(1,100)*0.5; %生成方差為1的高斯噪聲 ZZnoise; % 加入噪聲plot(Z);X[0;…

LabVIEW動態力傳感器校準系統

LabVIEW動態力傳感器校準系統 開發了一種基于LabVIEW的動態力傳感器校準系統。系統主要用于動態力的測量和校準&#xff0c;通過高度集成化和自動化的設計&#xff0c;顯著提升校準的效率和精確度。系統采用沖擊法進行動態校準&#xff0c;涵蓋了完整的硬件設計和軟件開發流程…

Kotlin 注解

文章目錄 定義注解類的注解標注目標聲明 定義 注解使用annotation關鍵字定義&#xff0c;且只能用于普通類&#xff0c;該類被稱為注釋類。可以使用注釋類為某個變量、函數、類、接口等注釋。與我們寫的代碼注釋類似&#xff0c;注釋類可以指明被標注類的狀態、作用等等&#…

智能體應用開發:構建各類垂直領域的ai智能體應用

最近在做個類似的項目&#xff0c;有用到這方面的知識&#xff0c;順便做一些記錄和筆記吧&#xff0c;希望能幫到大家了解智能體應用開發 目錄 引言 AI原生應用的興起 智能體在AI中的角色 實現原理詳解 機器學習基礎 數據管理與關聯數據庫 數據結構 Embedding 檢索方…

Pytorch實用教程:torch.cat()函數的用法詳解

torch.cat 是 PyTorch 中用于沿指定維度連接張量的函數。以下是該函數的詳細用法: 語法 torch.cat(tensors, dim=0, *, out=None)參數說明 tensors (sequence of Tensors): 要連接的張量序列。這些張量必須具有相同的形狀(除了連接的維度)。dim (int, optional): 沿著哪個…

衛星位置解算

前言&#xff1a; 本章節代碼均在Gitee中開源&#xff1a; 衛星位置計算代碼https://gitee.com/Ehundred/navigation-engineering/tree/master/%E5%8D%AB%E6%98%9F%E5%AF%BC%E8%88%AA%E5%8E%9F%E7%90%86/GPS%E5%8D%AB%E6%98%9F%E4%BD%8D%E7%BD%AE%E8%A7%A3%E7%AE%97/Satellit…

SAP物料自動記賬科目設置總結

SAP物料自動記賬科目設置總結 目錄 物料自動記賬科目設置總結... 1 總體說明... 1 庫存移動事務類型的設置... 4 庫存科目設置... 6 期初導入... 6 業務舉例... 6 配置... 6 庫存初始單據... 7 采購收貨(缺少到票) 8 業務舉例... 8 配置... 8 采購收貨單據-MIGO_GR…

心懷希望の光柵化

還記得什么是光柵化咩&#xff1f; 將三維空間的幾何形體顯現在屏幕上&#xff0c;這就是光柵化&#xff08;游戲、實時圖形學的應用&#xff09; Perspective Projection 在正交投影里如何定義三維空間中的立方體呢&#xff1f; 用x軸的覆蓋&#xff08;左、右&#xff09;…

【UML用戶指南】-02-UML基本元素的介紹(二)

目錄 1、語法和語義規則 2、UML中的公共機制 &#xff08;1&#xff09;規約 &#xff08;2&#xff09;修飾 &#xff08;3&#xff09;通用劃分 &#xff08;4&#xff09;擴展機制 衍型/版型/類型&#xff08;stereotype&#xff09; 標記值 &#xff08;tagged val…

Java編程常見問題匯總四

系列文章目錄 文章目錄 系列文章目錄前言一、忽略所有異常二、重復包裝RuntimeException三、不正確的傳播異常四、用日志記錄異常五、異常處理不徹底 前言 前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。…

[C/C++]_[初級]_[在Windows和macOS平臺上導出動態庫的一些思考]

場景 最近看了《COM本質論》里關于如何設計基于抽象基類作為二進制接口,把編譯器和鏈接器的實現隱藏在這個二進制接口中,從而使用該DLL時不需要重新編譯。在編譯出C接口時,發現接口名直接是函數名,比如BindNativePort,怎么不是_BindNativePort?說明 VC++導出的函數默認是使…