JDBC相關知識

来源:http://www.cnblogs.com/CComma/archive/2017/07/13/7148520.html
-Advertisement-
Play Games

一、連接資料庫 1. 步驟 2. 獲取Driver的方式 通過new新建 :Driver driver = new com.mysql.jdbc.Driver(); 通過反射創建類的實例 :Driver driver = (Driver)Class.forName(driverClass).newI ...


一、接資料庫

1. 步驟

//1.創建一個Driver實現類的對象
Driver driver = new com.mysql.jdbc.Driver();//註意拋異常

//2.準備 url 和 info
String url = "jdbc:mysql://localhost:3306/test";
//Oracle:"jdbc:oracle:thin:@localhost:1512:sid"
//SQLServer:"jdbc:microsoft:sqlserver//localhost:1433:DatabaseName=sid"
//MySql:"jdbc:mysql://localhost:3306/sid"

Properties info = new Properties();
info.put("user", "root");
info.put("password","1230");

//3.獲取連接
Connection connection = driver.connect(url, info);//註意拋異常

 

2. 獲取Driver的方式

  • 通過new新建  :Driver driver = new com.mysql.jdbc.Driver();
  • 通過反射創建類的實例  :Driver driver = (Driver)Class.forName(driverClass).newInstance();  //driverClass為全類名,即 com.mysql.jdbc.Driver
  • 通過DriverManager獲取資料庫連接(載入資料庫驅動程式 即 註冊驅動)  :DriverManager.registerDriver(Class.forName(driverClass).newInstance());   可簡化為 Class.forName(driverClass); 可載入多個驅動
    //載入資料庫驅動程式 (可載入多個驅動程式)(註冊驅動)
    //DriverManager.registerDriver(Class.forName(driverClass).newInstance());
    Class.forName(driverClass);
    Class.forName(driverClass2);

    //通過getConnection方法獲得連接
    //getConnection(String url, String user, String password) throws SQLException
    Connection connection = DriverManager.getConnection(jdbcUrl, user, password);

 

3. 提取方法來獲得Connection

實現方式:把URL、賬號、密碼等信息放入 jdbc.properties 文件,通過反射獲取

public class Utils {
    public Connection getConnection() throws Exception {
        String driverClass = null;
        String jdbcUrl = null;
        String user = null;
        String password = null;

        // 讀取類路徑下的jdbc.properties文件
        InputStream in = getClass().getClassLoader().getResourceAsStream("jdbc.properties");
        Properties properties = new Properties();
        properties.load(in);
        driverClass = properties.getProperty("driver");
        jdbcUrl = properties.getProperty("jdbcUrl");
        user = properties.getProperty("user");
        password = properties.getProperty("password");

/*        // 通過反射創建類的實例
        Driver driver = (Driver) Class.forName(driverClass).newInstance();

        Properties info = new Properties();
        info.put("user", user);
        info.put("password", password);
        Connection connection = driver.connect(jdbcUrl, info);*/
        
        Class.forName(driverClass);
        Connection connection = DriverManager.getConnection(jdbcUrl, user, password);

        return connection;
    }

}

 

二、資料庫操作

前提:連接資料庫的前提下,才能進行操作

1. 執行更新操作 (插入、修改、刪除 )

//1.獲得連接
Connection conn = driver.connect(url, info);
String sql="...";     //sql語句

//2.獲取statement對象
Statement statement = conn.createStatement();

//3.執行
statement.executeUpdate(sql);

//4.關閉
statement.close();
conn.close();

 

2. 查詢操作

//1.通過連接獲得statement對象
Statement statement = conn.createStatement();
String sql = "...";

//2.執行
ResultSet rs = statement.executeQuery(sql);

//3.遍歷輸出
while(rs.next()){
     int id = rs.getInt(1);
     String name = rs.getString("name");
}

//4.關閉
rs.close();
statement.close();
conn.close();

 

3. 給SQL語句設置參數——PreparedStatement

父類:Statement

//1.通過連接獲得preparedStatement對象
String sql = "insert into customers (name, email, birth) values(?,?,?)";
preparedStatement = conn.prepareStatement(sql);  //與無參的Statement對象不同,獲得對象時就得傳入sql語句,執行語句時不用再傳入sql語句

//2.設置參數
preparedStatement.setString(1, "zhangsan");
preparedStatement.setString(2, "[email protected]");
preparedStatement.setDate(3, new Date(new java.util.Date().getTime()));

//3.執行
preparedStatement.executeUpdate();

//4.獲得ResultSet
ResultSet rs = preparedStatement.getResultSet();

 

獲取插入記錄的主鍵值

實現方式:使用重載的preparedStatement(sql, flag)來生成preparedStatement對象

preparedStatement = conn.preparedStatement(sql, Statement.RETURN_GENERATED_KEYS);

//生成的結果集里只有一列,即主鍵編號 ResultSet rs
= preparedStatement.getGeneratedKeys(); if(rs.next()){
//獲得主鍵 String generatedKey = rs.getObject(
1); }

 

4. 調用函數&存儲過程——CallableStatement

父類:Statement

// 1. 通過 Connection 對象的 prepareCall()方法創建一個 CallableStatement 對象的實例。
//sql字元串指明如何調用存儲過程:?=call name(param, ..., param) 或 callname(param, ..., param) String sql = "{?= call sum_salary(?, ?)}"; CallableStatement callableStatement = connection.prepareCall(sql); // 2. 通過 CallableStatement 對象的 reisterOutParameter() 方法註冊 OUT 參數。 callableStatement.registerOutParameter(1, Types.NUMERIC); callableStatement.registerOutParameter(3, Types.NUMERIC); // 3. 通過 CallableStatement 對象的 setXxx() 方法設定 IN 或 IN OUT 參數。
//若想將參數預設值設為
null, 可以使用 setNull() 方法。 callableStatement.setInt(2, 80); // 4. 通過 CallableStatement 對象的 execute() 方法執行存儲過程。 callableStatement.execute(); // 5. 如果所調用的是帶返回參數的存儲過程, 還需要通過 CallableStatement 對象的 getXxx() 方法獲取其返回值。 double sumSalary = callableStatement.getDouble(1); long empCount = callableStatement.getLong(3);

 

三、JDBC元數據

1. ResultSetMetaData

方法簽名:ResultSetMetaData getMetaData()       //通過 resultSet.getMetaData() 方法獲得 ResultSetMetaData 對象

                  int getColumnCount()                            //查詢結果集中的列數

                  String getColumnName(int colum)        //獲取查詢結果集中指定的列別名,從1開始

                  String getColumnLabel(int colum)         //獲取查詢結果集中指定的列別名,從1開始

    //在資料庫中查詢記錄,轉換成與表相對應的實體類對象
public <T> T get(Class<T> clazz, String sql, Object... args) throws Exception { T entity = null; Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; //1.獲得connection connection = getConnection(); //2.設置參數 preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < args.length; i++) { preparedStatement.setObject(i + 1, args[i]); } //3.執行,並獲得resultSet resultSet = preparedStatement.executeQuery(); //4.通過resultSet獲得resultSetMetaData ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); Map<String, Object> values = new HashMap<>(); if (resultSet.next()) { //5.把結果放入哈希表中 for (int i = 0; i < resultSetMetaData.getColumnCount(); i++) { String columnLabel = resultSetMetaData.getColumnLabel(i + 1); Object columnValue = resultSet.getObject(i + 1); values.put(columnLabel, columnValue); }
//6.為相應實體類對象設置屬性值
if(values.size() > 0){ entity = clazz.newInstance(); for(Map.Entry<String, Object> entry: values.entrySet()){ String fieldName = entry.getKey(); Object value = entry.getValue(); BeanUtils.setProperty(entity, fieldName, value);//需要使用beanutils工具包 } } } return entity; }

 

2. DatabaseMetaData

方法簽名:DatabaseMetaData getMetaData()      //通過 connection.getMetaData() 方法獲得

作用:可以得到資料庫本身的一些信息

DatabaseMetaData data = connection.getMetaData();
            //1. 得到資料庫的版本號
int version = data.getDatabaseMajorVersion();
            
//2. 得到連接到資料庫的用戶名
String user = data.getUserName();
            
//3. 得到 MySQL 中有哪些資料庫
ResultSet resultSet = data.getCatalogs();

 

四、處理Blob大對象數據

LOB:大對象,存儲大量數據的一種數據類型。

BLOB:二進位大對象。

1. 讀取 Blob 數據

 實現方式:使用 getBlob 方法讀取到 Blob 對象

                   調用 Blob 的 getBinaryStream() 方法得到輸入流。再使用 IO 操作即可。

...
resultSet = preparedStatement.executeQuery();
          
if(resultSet.next()){
    int id = resultSet.getInt(1);
    String name = resultSet.getString(2);
    String email = resultSet.getString(3);
                
    //1.獲得Blob對象
    Blob picture = resultSet.getBlob(5);
    
    //2.獲得輸入流
    InputStream in = picture.getBinaryStream();
    
    //3.輸出到flower.jpg
    OutputStream out = new FileOutputStream("flower.jpg");
    byte [] buffer = new byte[1024];
    int len = 0;
    while((len = in.read(buffer)) != -1){
        out.write(buffer, 0, len);
    }
                
    in.close();
    out.close();
}

 

2. 寫入Blob數據

作用:實現將本地圖片寫入資料庫

String sql = "INSERT INTO customers(name, email, birth, picture) VALUES(?,?,?,?)";
//通過連接 獲得preparedStatement對象 preparedStatement
= connection.prepareStatement(sql);
//設置參數 preparedStatement.setString(
1, "ABCDE"); preparedStatement.setString(2, "[email protected]"); preparedStatement.setDate(3, new Date(new java.util.Date().getTime()));
//1.創建輸入流 InputStream inputStream
= new FileInputStream("Hydrangeas.jpg");
//2.設置Blob參數 preparedStatement.setBlob(
4, inputStream); preparedStatement.executeUpdate();

 

五、事務

1. 方法

//取消預設提交後才能進行事務

connection.setAutoCommit(false);  //取消預設提交並開始事務

//提交事務

connection.commit();

//回滾

 connection.rollback();

 

2. 資料庫的隔離級別

  • READ UNCOMMITTED  :讀未提交數據,允許事務讀取未被其他事務提交的變更,臟讀、不可重覆讀、幻讀都會出現。
  • READ COMMITTED       :讀已提交數據,只允許事務讀取已經被其他事務提交的變更。可避免臟讀,不可重覆讀和幻讀仍然存在。
  • REPEATABLE READ      :可重覆讀,確保事務可以多次從一個欄位中讀取相同的值,在這個事務持續期間,禁止其他事務對這個欄位進行更新。可避免臟讀和不可重覆讀,幻讀問題仍然存在。
  • SERIALIZABLE          :串列化,確保事務可以從一個表中讀取相同的行,在這個事務持續期間,禁止其他事務對該表執行操作,所有併發問題都可避免,但性能低下。

通過Connection的setTransactionIsolation來設置事務的隔離級別。

例:connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

註:mysql的預設隔離級別是REPEATABLE READ。

 

六、批量處理

方法簽名:preparedStatement.addBatch();         //加入Batch

                  preparedStatement.executeBatch();  //批量處理

                  preparedStatement.clearBatch();      //清除當前的積累量

//批量插入十萬條數據
sql = "INSERT INTO customers VALUES(?,?,?)";
preparedStatement = connection.prepareStatement(sql);
Date date = new Date(new java.util.Date().getTime());
for
(int i = 0; i < 100000; i++){ preparedStatement.setInt(1, i + 1); preparedStatement.setString(2, "name_" + i); preparedStatement.setDate(3, date); //"積攢" SQL preparedStatement.addBatch();  //Statement寫法:statement.addBatch(String sql); //當 "積攢" 到一定程度, 就統一的執行一次. 並且清空先前 "積攢" 的 SQL if((i + 1) % 300 == 0){ preparedStatement.executeBatch(); preparedStatement.clearBatch(); } } //若總條數不是批量數值的整數倍, 則還需要再額外的執行一次. if(100000 % 300 != 0){ preparedStatement.executeBatch(); preparedStatement.clearBatch(); }

 

七、資料庫連接池

必要性

傳統連接使用DriverManager獲取,再驗證用戶名密碼,執行完畢再斷開連接。資料庫的連接資源沒有得到很好的重覆利用。

資料庫連接池負責分配、管理和釋放資料庫連接,它允許應用程式重覆使用一個現有的資料庫連接,而不是重新建立一個。

資料庫連接池是通過實現DataSource介面來實現的。

 

1. DBCP

需要2 個jar 包:

  • dbcp
  • pool

註:dbcp依賴於 pool

final BasicDataSource dataSource = new BasicDataSource();
        
//1. 為數據源實例指定必須的屬性
dataSource.setUsername("root");
dataSource.setPassword("1230");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        
//2. 指定數據源的一些可選的屬性
//1). 指定資料庫連接池中初始化連接數的個數
dataSource.setInitialSize(5);
        
//2). 指定最大的連接數: 同一時刻可以同時向資料庫申請的連接數
dataSource.setMaxActive(5);
        
//3). 指定小連接數: 在資料庫連接池中保存的最少的空閑連接的數量 
dataSource.setMinIdle(2);
        
//4).等待資料庫連接池分配連接的最長時間. 單位為毫秒. 超出該時間將拋出異常. 
dataSource.setMaxWait(1000 * 5);
        
//3. 從數據源中獲取資料庫連接
Connection connection = dataSource.getConnection();

 

通過DataSourceFactory創建連接池(DataSource),這樣可以直接從配置文件中獲取信息

//從Properties文件里獲得數據
Properties properties = new Properties(); InputStream inStream = DBCPTest.class.getClassLoader().getResourceAsStream("dbcp.properties"); properties.load(inStream);
//使用工廠創建連接池 DataSource dataSource
= BasicDataSourceFactory.createDataSource(properties);

 

 

2. C3P0

@Test
public void testC3P0() throws Exception{
    ComboPooledDataSource cpds = new ComboPooledDataSource();
    cpds.setDriverClass( "com.mysql.jdbc.Driver" ); //loads the jdbc driver            
    cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/test" );
    cpds.setUser("root");
    cpds.setPassword("1230");
        
    System.out.println(cpds.getConnection()); 
}

 

通過載入配置文件來獲得信息

DataSource dataSource = new ComboPooledDataSource("helloc3p0");

dataSource.getConnection();

<!-- 配置文件helloc3p0.xml -->
<?
xml version="1.0" encoding="UTF-8"?> <c3p0-config> <named-config name="helloc3p0"> <!-- 指定連接數據源的基本屬性 --> <property name="user">root</property> <property name="password">1230</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql:///atguigu</property> <!-- 若資料庫中連接數不足時, 一次向資料庫伺服器申請多少個連接 --> <property name="acquireIncrement">5</property> <!-- 初始化資料庫連接池時連接的數量 --> <property name="initialPoolSize">5</property> <!-- 資料庫連接池中的最小的資料庫連接數 --> <property name="minPoolSize">5</property> <!-- 資料庫連接池中的最大的資料庫連接數 --> <property name="maxPoolSize">10</property> <!-- C3P0 資料庫連接池可以維護的 Statement 的個數 --> <property name="maxStatements">20</property> <!-- 每個連接同時可以使用的 Statement 對象的個數 --> <property name="maxStatementsPerConnection">5</property> </named-config> </c3p0-config>

 

八、DBUtils

註:dbutils只是對底層的封裝

       QueryRunner可能會有線程安全問題,逐一不要創建多個實例

1. update方法

//1.創建QueryRunner實現類
QueryRunner queryRunner = new QueryRunner(); String sql = "DELETE FROM customers WHERE id IN (?,?)";

//2.使用update方法 queryRunner.update(connection, sql,
12, 13);

 

2. query方法

方法簽名:queryRunner.query(Connection connection, String sql, ResultSetHandler rs);

使用:在實現ResultSetHandler介面的類的handle方法中編寫對結果集的處理方法。

Object object = queryRunner.query(conn, sql, new ResultSetHandler() {
    @Override
    public Object handle(ResultSet rs) throws SQLException {
        List<Customer> customers = new ArrayList<>();

        while (rs.next()) {
            int id = rs.getInt(1);
            String name = rs.getString(2);
            String email = rs.getString(3);
            Date birth = rs.getDate(4);

            Customer customer = new Customer(id, name, email, birth);
            customers.add(customer);
        }
//也是query方法的返回值
return customers; } });

 

以下Handler類實現了ResultSetHandler介面

1)BeanHandler

功能:返回查詢的第一條記錄對應的那個實體類對象

原理:BeanHandler類的handle方法中通過反射創建傳入的Class類對應的類的對象,並用set方法為該對象設置屬性,然後返回該對象。

註意:查詢語句中的列名要和實體類的屬性相對應,若不對應則需要使用別名   如下name:

String sql = "SELECT id, name customerName, email, birth FROM customers WHERE id >= ?";

// 1. 創建 QueryRunner 對象
QueryRunner queryRunner = new QueryRunner();

// 2. 調用query方法 Object object = queryRunner.query(conn, sql, new BeanHandler<>(Customer.class),5);

 

2)BeanListHandler

作用:可以通過查詢記錄獲得實體類對象集合

           若可以查詢到記錄,則返回傳入的Class對象對應的對象集合。

String sql = "SELECT id, name customerName, email, birth FROM customers";
QueryRunner queryRunner = new QueryRunner();

// 返回List<Customer> 對象 Object object
= queryRunner.query(conn, sql, new BeanListHandler<>(Customer.class));

 

3)MapHandler

作用:返回查詢的第一條記錄對應的Map對象  鍵:列名(非別名);值:列的值

QueryRunner queryRunner = new QueryRunner();
String sql = "SELECT id, name customerName, email, birth FROM customers WHERE id = ?";

Map<String, Object> map = queryRunner.query(connection, sql, new MapHandler(), 4);

 

4)MapListHandler

作用:返回查詢的所有記錄對應的Map對象的集合

QueryRunner queryRunner = new QueryRunner();
String sql = "SELECT id, name, email, birth FROM customers";

List<Map<String, Object>> mapList = queryRunner.query(connection, sql, new MapListHandler());

 

5)ScalarHandler

作用:返回結果集中的第一列對應的數據

QueryRunner queryRunner = new QueryRunner();
String sql = "SELECT name FROM customers WHERE id = ?";
	   

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

-Advertisement-
Play Games
更多相關文章
  • 題目描述 小玉開心的在游泳,可是她很快難過的發現,自己的力氣不夠,游泳好累哦。已知小玉第一步能游2米,可是隨著越來越累,力氣越來越小,她接下來的每一步都只能游出上一步距離的98%。現在小玉想知道,如果要游到距離x米的地方,她需要游多少步呢。請你編程解決這個問題。 輸入輸出格式 輸入格式: 輸入一個數 ...
  • 題目描述 輸入一組勾股數a,b,c(a≠b≠c),用分數格式輸出其較小銳角的正弦值。(要求約分。) 輸入輸出格式 輸入格式: 一行,包含三個數,即勾股數a,b,c(無大小順序)。 輸出格式: 一行,包含一個數,即較小銳角的正弦值 輸入輸出樣例 輸入樣例#1: 3 5 4 輸出樣例#1: 3/5 說明 ...
  • 時間限制: 1 s 空間限制: 1000 KB 題目等級 : 黃金 Gold 題解 查看運行結果 時間限制: 1 s 空間限制: 1000 KB 題目等級 : 黃金 Gold 時間限制: 1 s 空間限制: 1000 KB 題目等級 : 黃金 Gold 時間限制: 1 s 時間限制: 1 s 空間限 ...
  • 在某博客上看到的一個C++書籍閱讀清單,可以參考下: 階段 1《Essential C++》這是一本內容不多但很實用的C++入門書籍,強調快速上手與理解C++編程。本書主要圍繞一系列逐漸複雜的程式問題,以及用以解決這些問題的語言特性展開講解。你不只學到C++的函數和結構,也會學習到它們的設計目的和基 ...
  • 題目背景 mxj的啟發。 題目描述 禽獸患傳染病了。一個禽獸會傳染x個禽獸。試問n輪傳染後有多少禽獸被傳染? 輸入輸出格式 輸入格式: 兩個數x和n。 輸出格式: 一個數:被傳染的禽獸數。 輸入輸出樣例 輸入樣例#1: 10 2 輸出樣例#1: 121 輸入樣例#1: 10 2 輸出樣例#1: 12 ...
  • 題目描述 班主任給小玉一個任務,到文具店裡買儘量多的簽字筆。已知一隻簽字筆的價格是1元9角,而班主任給小玉的錢是a元b角,小玉想知道,她最多能買多少只簽字筆呢。 輸入輸出格式 輸入格式: 輸入的數據,在一行內,包括兩個整數,依次表示a和b,a<=10000,b<=9。 輸出格式: 輸出一個整數,表示 ...
  • 通過輸出$GLOBALS可以看到'/'後的參數都存在於$_SERVER['PATH_INFO']里; 聲明一個數組來獲取我們在'/'後傳遞的參數 $arr = explode('/', $_SERVER['PATH_INFO']); //print_r($arr)查看詳細信息 ...
  • 今天開始學習python,首先環境安裝 1.在https://www.python.org/downloads/下載python2.X或者3.X(ps:這裡建議下載32位的python ,因為64位python開發出來的程式,打包成 EXE程式後會不相容32位系統) 2.下載之後安裝,打開安裝包 2 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...