springboot3 集成mybatis 和通用mapper

来源:https://www.cnblogs.com/binz/archive/2023/08/28/17654403.html
-Advertisement-
Play Games

你的Java服務是如何監控的呢? 1.Null:監控?什麼監控?我一個寫代碼的服務掛了跟我有什麼關係? 2.命令行:服務掛了?記憶體泄漏?jstat jmap jcmd,還好不是我寫的 3.擼代碼:Java採集JVM/伺服器資源信息 -> Prometheus -> Grafana,請允許我對業務代碼 ...


xml版本查看:https://www.cnblogs.com/binz/p/6564490.html

springboot3.x以前的版本查看 https://www.cnblogs.com/binz/p/17421063.html

springboot3.x查看  https://www.cnblogs.com/binz/p/17654403.html

1、pom引用

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.1.2</version>
 </parent>

<dependencies>
     <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        
        <!-- web 容器使用 undertow 性能更強 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.4.7</version>
        </dependency>
<!-- 以前的tkmapper 不支持springboot3.x -->
<!-- 具體查看https://mapper.mybatis.io/--> <dependency> <groupId>io.mybatis</groupId> <artifactId>mybatis-mapper</artifactId> <version>2.1.1</version> </dependency>
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.2</version> </dependency>


    <!-- openapi、 swagger3、knife4j配置,適用boot3 -->
    <!-- https://doc.xiaominfo.com -->
    <dependency>
      <groupId>com.github.xiaoymin</groupId>
      <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
      <version>4.3.0</version>
    </dependency>
    <!-- openapi配置 -->

    
</dependencies>    

2、新建自己的BaseMapper

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.apache.ibatis.session.RowBounds;

import com.github.pagehelper.Page;
import cn.hutool.core.bean.BeanUtil;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import io.mybatis.mapper.Mapper;
import io.mybatis.mapper.example.Example;
import io.mybatis.mapper.example.LambdaExampleWrapper;

public interface BaseMapper<T> extends Mapper<T,Long>{
    
    List<T> selectList(T entity, RowBounds rowBounds);

    default T getById(Long id) {
        return selectByPrimaryKey(id).orElse(null);
    }

    default <RE extends Serializable> RE getById(Long id,Class<RE> returnClass) {
        T t = getById(id);
        if(t != null) {
            return covert(t,returnClass);
        }
        return null;
    }

    default T one(T query) {
        return selectOne(query).orElse(null);
    }


    default <RE extends Serializable> RE one(T query,Class<RE> returnClass) {
        Optional<T> optional = selectOne(query);
        if(optional.isPresent()) {
            T t = optional.get();
            return covert(t,returnClass);
        }
        return null;
    }


    default <RE extends Serializable> List<RE> select(T t,Class<RE> returnClass) {
        List<T> ts = selectList(t);
        return covertList(ts,returnClass);
    }

    default <RE extends Serializable> Page<RE> selectPage(T t,Class<RE> returnClass) {
        Page<T> ts = (Page<T>) selectList(t);
        return (Page<RE>) covertList(ts,returnClass);
    }

    default <RE extends Serializable> Page<RE> selectPageByExample(Example<T> example,Class<RE> returnClass) {
        Page<T> ts = (Page<T>) selectByExample(example);
        return (Page<RE>) covertList(ts,returnClass);
    }



    default <RE extends Serializable> List<RE> selectByExample(Example<T> example,Class<RE> returnClass) {
        List<T> ts = selectByExample(example);
        return covertList(ts,returnClass);
    }

    default <RE extends Serializable> RE selectOneByExample(Example<T> example,Class<RE> returnClass) {
        Optional<T> optional = selectOneByExample(example);
        if(optional.isPresent()) {
            T t = optional.get();
            return covert(t,returnClass);
        }
        return null;
    }

    default <RE extends Serializable> RE selectOneByExampleLimitOne(Example<T> example,Class<RE> returnClass) {
        T t = selectOneByExampleLimitOne(example);
        if(t != null) {
            return covert(t, returnClass);
        }
        return null;
    }

    default T selectOneByExampleLimitOne(Example<T> example) {
        RowBounds rowBounds = new RowBounds(0, 1);
        List<T> ts = selectByExample(example, rowBounds);
        if(ObjectUtil.isNotEmpty(ts)) {
            return ts.get(0);
        }
        return null;
    }



    default T selectOneByLimitOne(T t) {
        RowBounds rowBounds = new RowBounds(0, 1);
        List<T> ts = selectList(t,rowBounds);
        if(ObjectUtil.isNotEmpty(ts)) {
            return ts.get(0);
        }
        return null;
    }


    @SuppressWarnings("unchecked")
    default Class<T> thisTClass() {
        Class<?> class1 = getClass();
        Class<?> interfaces = class1.getInterfaces()[0];
        Type[] genericInterfaces = interfaces.getGenericInterfaces();
        Type type = genericInterfaces[0];
        if( type instanceof ParameterizedType){
            ParameterizedType pType = (ParameterizedType) type;
            Type clazz = pType.getActualTypeArguments()[0];
            if( clazz instanceof Class<?> ){
                return (Class<T>) clazz;
            }
        }
        return null;
    }

    default <RE extends Serializable> List<RE> covertList(List<T> ts,Class<RE> returnClass){
        List<RE> responses;
        if(ts instanceof Page) {
            responses = new Page<>();
        }else {
            responses = new ArrayList<>();
        }
        for (T t : ts) {
            responses.add(covert(t,returnClass));
        }
        return responses;
    }

    default <RE extends Serializable> RE covert(T t , Class<RE> returnClass) {
        if(t != null) {
            RE response = null;
            try {
                response = ReflectUtil.newInstanceIfPossible(returnClass);
                BeanUtil.copy(t, response);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return response;
        }
        return null;
    }

    //自帶的wrapper()個人覺得還缺點東西,就自己複製了一份出來微調了,根據情況使用,不需要就刪除,需要對應的代碼在下方
    default LambdaExampleWrapper<T> lambdaWrapper() {
        return new LambdaExampleWrapper<>(BaseMapper.this, example());
    }

}
View Code

3、新建表模型,註意註解使用和以前版本有區別,但是相容一些之前javax.persistence的一些基本註解

import java.util.Date;

import io.mybatis.provider.Entity;
import lombok.Data;

@Data
@Entity.Table("system_user")
public class User {

    @Entity.Column(id = true,insertable = false,updatable = false)
    private Long id;

    /**
    * 姓名
    */
    private String realname;

    /**
    * 手機號
    */
    private String mobile;

    /**
    * 密碼
    */
    private String password;

    /**
    * 身份證號
    */
    private String idcard;
    

    /**
    * 頭像
    */
    private String avatar;

    /**
    * 最後登錄時間
    */
    private String lastLoginIp;

    /**
    * 最後登錄時間
    */
    private Date lastLoginTime;

    /**
    * 創建人
    */
    private Long createBy;

    /**
    * 創建時間
    */
    private Date createTime;

    /**
    * 修改人
    */
    private Long updateBy;

    /**
    * update_time
    */
    private Date updateTime;


}
View Code

4、創建對應業務的mapper繼承BaseMapper

import com.xxx.core.base.BaseMapper;
import com.xxx.system.model.User;

public interface UserMapper extends BaseMapper<User>{


}

5、啟動類掃描自己的mapper目錄 @MapperScan

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableCaching
@EnableScheduling
@SpringBootApplication
@MapperScan(basePackages="com.xxx.*.mapper")
@ComponentScan(basePackages = { "com.xxx.*.config", "com.xxx.*.controller", "com.xxx.*.job" })
public class OperateApiStarted {
    public static void main(String[] args) {
        SpringApplication.run(OperateApiStarted.class, args);
    }
}

 6、創建io.mybatis.mapper.example.LambdaExampleWrapper

package io.mybatis.mapper.example;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.ibatis.exceptions.TooManyResultsException;
import org.apache.ibatis.session.RowBounds;

import com.github.pagehelper.Page;
import com.xxx.BaseMapper;
import com.xxx.BeanUtil;

import io.mybatis.common.util.Assert;
import io.mybatis.mapper.fn.Fn;

public class LambdaExampleWrapper<T> {


    private final BaseMapper<T>    baseMapper;
    private final Example<T>          example;
    private       Example.Criteria<T> current;

    public LambdaExampleWrapper(BaseMapper<T> baseMapper, Example<T> example) {
        this.baseMapper = baseMapper;
        this.example = example;
        this.current = example.createCriteria();
    }

    /**
     * or 一組條件
     *
     * @return 條件
     */
    public LambdaExampleWrapper<T> or() {
        this.current = this.example.or();
        return this;
    }

    /**
     * 獲取查詢條件
     */
    public Example<T> example() {
        return example;
    }

    /**
     * 清除條件,可重用
     */
    public LambdaExampleWrapper<T> clear() {
        this.example.clear();
        this.current = example.createCriteria();
        return this;
    }

    /**
     * 指定查詢列
     *
     * @param fns 方法引用
     */
    @SafeVarargs
    public final LambdaExampleWrapper<T> select(Fn<T, Object>... fns) {
        this.example.selectColumns(fns);
        return this;
    }

    /**
     * 排除指定查詢列
     *
     * @param fns 方法引用
     */
    @SafeVarargs
    public final LambdaExampleWrapper<T> exclude(Fn<T, Object>... fns) {
        this.example.excludeColumns(fns);
        return this;
    }

    /**
     * 設置起始 SQL
     *
     * @param startSql 起始 SQL,添加到 SQL 前,註意防止 SQL 註入
     */
    public LambdaExampleWrapper<T> startSql(String startSql) {
        this.example.setStartSql(startSql);
        return this;
    }

    /**
     * 設置結尾 SQL
     *
     * @param endSql 結尾 SQL,添加到 SQL 最後,註意防止 SQL 註入
     */
    public LambdaExampleWrapper<T> endSql(String endSql) {
        this.example.setEndSql(endSql);
        return this;
    }

    /**
     * 通過方法引用方式設置排序欄位
     *
     * @param fn    排序列的方法引用
     * @param order 排序方式
     * @return Example
     */
    public LambdaExampleWrapper<T> orderBy(Fn<T, Object> fn, Example.Order order) {
        this.example.orderBy(fn, order);
        return this;
    }

    /**
     * 支持使用字元串形式來設置 order by,用以支持一些特殊的排序方案
     *
     * @param orderByCondition 排序字元串(不會覆蓋已有的排序內容)
     * @return Example
     */
    public LambdaExampleWrapper<T> orderBy(String orderByCondition) {
        this.example.orderBy(orderByCondition);
        return this;
    }

    /**
     * 支持使用字元串形式來設置 order by,用以支持一些特殊的排序方案
     *
     * @param orderByCondition 排序字元串構造方法,比如通過數組集合迴圈拼接等
     * @return Example
     */
    public LambdaExampleWrapper<T> orderBy(Supplier<String> orderByCondition) {
        this.example.orderBy(orderByCondition);
        return this;
    }

    /**
     * 支持使用字元串形式來設置 order by,用以支持一些特殊的排序方案
     *
     * @param useOrderBy       條件表達式,true使用,false不使用 字元串排序
     * @param orderByCondition 排序字元串構造方法,比如通過數組集合迴圈拼接等
     * @return Example
     */
    public LambdaExampleWrapper<T> orderBy(boolean useOrderBy, Supplier<String> orderByCondition) {
        return useOrderBy ? this.orderBy(orderByCondition) : this;
    }

    /**
     * 通過方法引用方式設置排序欄位,升序排序
     *
     * @param fns 排序列的方法引用
     * @return Example
     */
    @SafeVarargs
    public final LambdaExampleWrapper<T> orderByAsc(Fn<T, Object>... fns) {
        this.example.orderByAsc(fns);
        return this;
    }

    /**
     * 通過方法引用方式設置排序欄位,降序排序
     *
     * @param fns 排序列的方法引用
     * @return Example
     */
    @SafeVarargs
    public final LambdaExampleWrapper<T> orderByDesc(Fn<T, Object>... fns) {
        this.example.orderByDesc(fns);
        return this;
    }

    /**
     * 設置 distince
     */
    public LambdaExampleWrapper<T> distinct() {
        this.example.setDistinct(true);
        return this;
    }

    /**
     * 設置更新欄位和值
     *
     * @param useSet 表達式條件, true 使用,false 不使用
     * @param setSql "column = value"
     */
    public LambdaExampleWrapper<T> set(boolean useSet, String setSql) {
        return useSet ? set(setSql) : this;
    }

    /**
     * 設置更新欄位和值
     *
     * @param setSql "column = value"
     */
    public LambdaExampleWrapper<T> set(String setSql) {
        this.example.set(setSql);
        return this;
    }

    /**
     * 設置更新欄位和值
     *
     * @param useSet 表達式條件, true 使用,false 不使用
     * @param fn     欄位
     * @param value  值
     */
    public LambdaExampleWrapper<T> set(boolean useSet, Fn<T, Object> fn, Object value) {
        return useSet ? set(fn, value) : this;
    }


    /**
     * 設置更新欄位和值
     *
     * @param useSet   表達式條件, true 使用,false 不使用
     * @param fn       欄位
     * @param supplier 值構造函數
     */
    public LambdaExampleWrapper<T> set(boolean useSet, Fn<T, Object> fn, Supplier<Object> supplier) {
        return useSet ? set(fn, supplier.get()) : this;
    }


    /**
     * 設置更新欄位和值
     *
     * @param fn    欄位
     * @param value 值
     */
    public LambdaExampleWrapper<T> set(Fn<T, Object> fn, Object value) {
        this.example.set(fn, value);
        return this;
    }

    /**
     * 指定欄位為 null
     *
     * @param useCondition 表達式條件, true 使用,false 不使用
     * @param fn           欄位對應的 get 方法引用
     */
    public LambdaExampleWrapper<T> isNull(boolean useCondition, Fn<T, Object> fn) {
        return useCondition ? isNull(fn) : this;
    }

    /**
     * 指定欄位為 null
     *
     * @param fn 欄位對應的 get 方法引用
     */
    public LambdaExampleWrapper<T> isNull(Fn<T, Object> fn) {
        this.current.addCriterion(fn.toColumn() + " IS NULL");
        return this;
    }

    /**
     * 指定欄位不為 null
     *
     * @param useCondition 表達式條件, true 使用,false 不使用
     * @param fn           欄位對應的 get 方法引用
     */
    public LambdaExampleWrapper<T> isNotNull(boolean useCondition, Fn<T, Object> fn) {
        return useCondition ? isNotNull(fn) : this;
    }

    /**
     * 指定欄位不為 null
     *
     * @param fn 欄位對應的 get 方法引用
     */
    public LambdaExampleWrapper<T> isNotNull(Fn<T, Object> fn) {
        this.current.addCriterion(fn.toColumn() + " IS NOT NULL");
        return this;
    }

    /**
     * 欄位 = 值
     *
     * @param useCondition 表達式條件, true 使用,false 不使用
     * @param fn           欄位對應的 get 方法引用
     * @param value        值
     */
    public LambdaExampleWrapper<T> eq(boolean useCondition, Fn<T, Object> fn, Object value) {
        return useCondition ? eq(fn, value) : this;
    }

    /**
     * 欄位 = 值
     *
     * @param useCondition 表達式條件, true 使用,false 不使用
     * @param fn           欄位對應的 get 方法引用
     * @param supplier     值構造函數
     */
    public LambdaExampleWrapper<T> eq(boolean useCondition, Fn<T, Object> fn, Supplier<Object> supplier) {
        return useCondition ? eq(fn, supplier.get()) : this;
    }

    /**
     * 欄位 = 值
     *
     * @param fn    欄位對應的 get 方法引用
     * @param value 值
     */
    public LambdaExampleWrapper<T> eq(Fn<T, Object> fn, Object value) {
        this.current.addCriterion(fn.toColumn() + " =", value);
        return this;
    }

    /**
     * 欄位 != 值
     *
     * @param useCondition 表達式條件, true 使用,false 不使用
     * @param fn           欄位對應的 get 方法引用
     * @param supplier     值構造函數
     */
    public LambdaExampleWrapper<T> ne(boolean useCondition, Fn<T, Object> fn, Supplier<Object> supplier) {
        return useCondition ? ne(fn, supplier.get()) : this;
    }

    /**
     * 欄位 != 值
     *
     * @param useCondition 表達式條件, true 使用,false 不使用
     * @param fn           欄位對應的 get 方法引用
     * @param value        值
     */
    public LambdaExampleWrapper<T> ne(boolean useCondition, Fn<T, Object> fn, Object value) {
        return useCondition ? ne(fn, value) : this;
    }

    /**
     * 欄位 != 值
     *
     * @param fn    欄位對應的 get 方法引用
     * @param value 值
     */
    public LambdaExampleWrapper<T> ne(Fn<T, Object> fn, Object value) {
        this.current.addCriterion(fn.toColumn() + " <>", value);
        return this;
    }

    /**
     * 欄位 > 值
     *
     * @param useCondition 表達式條件, true 使用,false 不使用
     * @param fn           欄位對應的 get 方法引用
     * @param supplier     值構造函數
     */
    public LambdaExampleWrapper<T> gt(boolean useCondition, Fn<T, Object> fn, Supplier<Object> supplier) {
        return useCondition ? gt(fn, supplier.get()) : this;
    }

    /**	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ##### 17 JavaScript 中的call和apply 對於咱們逆向工程師而言. 並不需要深入的理解call和apply的本質作用. 只需要知道這玩意執行起來的邏輯順序是什麼即可 在運行時. 正常的js調用: ```js function People(name, age){ this.n ...
  • 地圖在 app 中使用還是很廣泛的,常見的應用常見有: 1、獲取自己的位置,規劃路線。 2、使用標記點進行標記多個位置。 3、繪製多邊形,使用圍牆標記位置等等。 此篇文章就以高德地圖為例,以上述三個常見需求為例,教大家如何在 uniapp 中添加地圖。 作為一個不管閑事的前端姑娘,我就忽略掉那些繁瑣 ...
  • # 項目主體搭建 - 前端:`vue3`、`element-plus`、`ts`、`axios`、`vue-router`、`pinia` - 後端:`nodejs`、`koa`、`koa-router`、`koa-body`、`jsonwebtoken` - 部署:`nginx`、`pm2`、`x ...
  • 前端實現文件下載功能有多種方法,這裡就不一一介紹,這裡只介紹使用文件流下載的實現方法。 既然是文件流那就肯定需要給前端返回一堆二進位編碼,作為後端就可以返回一個OutPutStream 後端可以使用Java中servlet提供的HttpServletResponse,核心步驟是要設置響應的數據類型, ...
  • Printf() 函數可以使用多種格式化動詞對輸出進行格式化。下麵是可以與所有數據類型一起使用的一些通用格式化動詞: **通用格式化動詞:** 以下動詞適用於所有數據類型: |動詞|描述| |-|-| |`%v`|以預設格式列印值| |`%#v`|以 Go 語法格式列印值| |`%T`|列印值的類型 ...
  • `pandas`小技巧系列是介紹的是使用`pandas`分析數據時,最常用的一些操作技巧。 具體包括: 1. [創建測試數據](https://www.cnblogs.com/wang_yb/p/17552748.html) 學習pandas的過程中,為了嘗試pandas提供的各類功能強大的函數,常 ...
  • ## 1. 什麼是WebSocket? WebSocket 是一種在單個 TCP 連接上進行全雙工通信的協議,它允許在瀏覽器和伺服器之間進行實時的、雙向的通信。相對於傳統的基於請求和響應的 HTTP 協議,WebSocket 提供了一種更有效、更實時的通信方式,適用於需要實時更新、實時通知和實時交互 ...
  • 來源:toutiao.com/article/7234104886726705716 ## 1.前言 我們的生產環境基本上都部署在雲伺服器上,例如應用伺服器、MySQL伺服器等。如果MySQL伺服器直接暴露在公網,就會存在很大的風險,為了保證數據安全,MySQL伺服器的埠是不對外開放的。 好巧不巧 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...