JDBC,SQL註入,事務,C3P0與Druid連接池(最詳細解析)

来源:https://www.cnblogs.com/ihave2carryon/p/18401274
-Advertisement-
Play Games

JDBC JDBC(Java DataBase Connectivty,Java資料庫連接)API,是一種用於執行Sql語句的Java API,可以為關係型資料庫提供統一的訪問,其由一組Java編寫的類和介面組成. JDBC驅動程式 起初,SUN公司推出JDBC API希望能適用於所有資料庫,但實際 ...


JDBC

JDBC(Java DataBase Connectivty,Java資料庫連接)API,是一種用於執行Sql語句的Java API,可以為關係型資料庫提供統一的訪問,其由一組Java編寫的類和介面組成.

JDBC驅動程式

起初,SUN公司推出JDBC API希望能適用於所有資料庫,但實際中是不可能實現的,各個廠商提供的資料庫差異太大,SUN公司於資料庫廠商協同之後決定:由SUN公司提供一套訪問資料庫的API,各個廠商根據規範提供一套訪問自家資料庫API的介面,SUN公司提供的規範API稱之為JDBC,廠商提供的自家資料庫API介面稱之為 驅動

JDBC原理

  • 經過SUN公司於各個資料庫廠商的協同,JDBC的結構如下圖:

JDBC驅動的原理

我們知道了JDBC運行的原理,那麼JDBC驅動是怎麼運行的呢,我們開始進行探究

  • JDBC驅動的類型

    根據訪問資料庫資料庫技術不同,JDBC驅動程式被訪問四類

    • Type1: JDBC-ODBC橋驅動程式
      • 此類驅動程式由JDBC-ODBC橋金額一個ODBC驅動程式組成
    • Type2:部分Java本地JDBC API驅動程式
      • 此類驅動程式必須在本地電腦上先安裝好特點的驅動程式才能進行使用
    • Type3:純Java的資料庫中間件驅動程式(目前主流)
      • Pure Java Driver for Database Middleware使用此類驅動時,不需要再本地電腦上安裝任何附加軟體,但必須再安裝資料庫管理系統的伺服器端加裝中間件(Middleware) ,這個中間件負責所有存取資料庫時的必要轉換.中間件的工作原理是:驅動程式將JDBC訪問轉換為於資料庫無關的標準網路協議(通常是HTTP或HTTPS)送出,然後再由中間件伺服器將其轉換為資料庫專用的訪問指令,完成對資料庫的操作.中間件可以支持對多種資料庫的訪問.
    • Type4:純Java的JDBC驅動程式(最理想的驅動程式)
      • Direct-toDatabasePureJavaDriver 此類驅動程式是之間面向資料庫的Java驅動程式,即所謂的”瘦”驅動程式.使用該驅動程式無需安裝如何附加軟體(包括本地電腦或是資料庫伺服器端),所有存取的資料庫操作都直接由JDBC驅動程式來完成
  • JDBC驅動工作動作

    • 對於第三類Type3驅動程式來說,其是由純Java語言開發的,此類驅動程式體積最小
      • 下麵給出Type3驅動程式的結構圖

    - 可見,Type 3,JDBC驅動程式為兩層結構,分別為驅動程式客戶端和驅動程式服務端,客戶端直接於用戶交互,**其為用戶提供符合JDBC規範的資料庫統一編程介面,**客戶端將數據請求通過**特定的網路請求**發送值伺服器.伺服器作為中間件的角色,其負責接收和處理用戶的請求,JDBC驅動程式本身不進行直接與資料庫的交互,而是藉助其他已經實現的驅動,稱之為”雇佣”.當然”雇佣”的數量越多,可支持的資料庫數量就越多.”雇佣”的數量和成員可以動態的改變,以滿足業務的擴展,這也是Type3JDBC性能強大的原因

將JDBC驅動導入idea(以Mysql為例)

  1. 第一步肯定是下載JDBC驅動程式了,各個廠商把特定的驅動程式打包成.jar發佈在其官網上大伙可用到官網下載

    下載好後,解壓至文件夾備用
  1. 打開idea,在要導入驅動的項目中新建一個名為lib的文件夾(建議命名)

  1. 將下載的zip文件解壓,找到其中的mysql-connector-java-8.0.26.jar包,將其複製

  1. 複製其中的mysql-connector-java-8.0.18.jar文件,在lib文件夾上右鍵,粘貼到IDEA中,剛剛新建的lib文件夾里

使導入的驅動生效

  1. 在idea點擊File→Project Structure

  1. 在Modules中選擇Denpendencies

  1. 點擊左側+號,選擇 JARS or directories

  1. 在彈出的視窗中選擇剛剛導入 lib 文件夾的驅動,點擊Ok

  1. 可以看到Module模塊中,多出了一個mysql驅動,選擇之後點擊Apply,然後Ok

JDBC中的的類及其應用

JDBC API中包含四個常用的介面和一個類,分別是Connection介面,Statement介面,PreparedStatement介面,ResultSet介面,DriverManager類,jar包中已經包含這些介面的實現類直接使用即可

Statement介面

Statement介面是Java程式執行資料庫操作的重要介面,用於已經建立了資料庫連接的基礎上,向資料庫發送要執行的Sql語句

  • 作用:執行不帶參數的簡單Sql語句
  • 主要方法
    • void addBatch(String sql )throws SQLException :該方法用於將Sql語句添加到Statement對象的當前命令列表中,用於Sql語句的批量處理
    • void clearBatch() throws SQLException:立即釋放Statement對象中的命令列表
    • boolean excute(String sql) throws SQLException :執行指定的Sql語句,成功返回true否則返回false
    • int[] excuteBatch() throws SQLException:將命令列表中的sql命令提交執行,返回一個int數組表示每個sql語句影響的行數
    • ResultSet excuteQuery(String sql) throws SQLException:該方法用於執行查詢類型(Select類型)的Sql語句,返回的查詢所獲取的結果集ResultSet對象
    • void close() throws SQLException:用於立即釋放此Statement對象的資料庫和JDBC資源

Connection介面

Connection介面位於java.sql包中,是用於與資料庫連接的對象,只有獲取了與資料庫連接的對象後,才能訪問資料庫進行操作

  • 作用:與資料庫進行連接

  • 主要方法

    • Statement createStatement() throws SQLException:用於創建一個Statement對象,用於執行Sql語句

    • PreparedStatement prepareStatement(String sql) throws SQLException: 創建一個PreparedStatement對象,用於執行預編譯的Sql語句

    • CallableStatement prepareCall(String sql)throws SQLException:創建一個CallableStatement對象用於執行存儲過程或函數

    • void commit() throws SQLException:提交當前事務

    • void rollback() throws SQLException:回滾事務

    • void close() throws SQLException:關閉連接

      在進行資料庫連接的時候還要用到DriverManager類中的getConnection(url,username,password)方法

    E.g:

            String url = "jdbc:mysql://localhost:3306/demo";
            String username = "root";
            String password = "root";
            //建立連接
            Connection connection = DriverManager.getConnection(url, username, password);
            String sql = "SELECT * FROM dept";
            //創建Statement對象執行查詢操作
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery(sql);
            //處理查詢結果
            while (resultSet.next()) {
                String dId = resultSet.getString("d_id");
                String dName = resultSet.getString("d_name");
                String loc = resultSet.getString("loc");
                System.out.println(dId + " " + dName + " " + loc);
            }
            connection.close();
            statement.close();
            resultSet.close();
        }
    

DriverManager類

DriverManager類是JDBC API的核心,該類中包含了與資料庫交互操作的方法,類中的方法都由資料庫廠商提供

  • 作用:管理和協調不同的JDBC驅動程式
  • 主要方法
    • public static Connection getConnection(String url, String user, String password)throws SQLException:根據指定的資料庫url,用戶名以及密碼建立資料庫連接
    • public static Connection getConnection(String url,Properties info):根據指定的資料庫url以及連接屬性建立資料庫連接
    • public static synchronized void deregisterDriver(Driver driver) throws SQLException:從DriverManager管理列表中刪除一個驅動,driver參數是要刪除的驅動對象

PreparedStatement介面

PreparedStatement介面位於java.servlet包中,其繼承了Statement介面

  • 與Statement的區別:
    • 執行速度較快:PreparedStatement對象是已經預編譯過的,執行速度快於Statement.因此若要執行大量的Sql語句時使用PreparedStatement以提高效率
  • 主要方法
    • setXXX()

      此類方法都是設置sql語句中傳入的參數里類型

      • void setBinaryStream(int parameterIndex,InputStream x) throws SQLException:將二進位流作為sql語句傳入的參數,二進位流可用高效地處理圖片,音頻,視頻登媒介,parameterIndex 是參數位置索引
      • void setBoolean(int parameterIndex,boolean x) throws SQLException:將boolen作為sql傳入的參數類型,parameterIndex為參數位置索引
      • void setByte(int parameterIndex,byte x) throws SQLException:將byte作為sql傳入的參數類型
      • void setDate(int parameterIndex,Date x) throws SQLException: 將java.sql.Date值x做為SQL語句中的參數值
      • void setDouble(int parameterIndex,double x) :將double值x做為SQL語句中的參數值
      • void setInt(int parameterIndex,int x) throws SQLException :將int值x做為SQL語句中的參數值
      • void setObject(int parameterIndex,Object x) throws SQLException :將object對象x做為SQL語句中的參數值
      • void setString(int parameterIndex,String x) throws SQLException: 將String值x做為SQL語句的參數值
      • void setTimestamp(int parameterIndex,Timestamp x) throws SQLException: 將java.sql.Timestamp值x做為SQL語句中的參數值
    • int executeUpdate() throws SQLException:executeUpdate() 方法返回的 int 值表示受影響的行數。如果返回值為 0,則可能表示沒有符合條件的記錄被修改;執行INSERTUPDATE 或 DELETE這些DML語句時同理

ResultSet介面

是用於接收查詢的結果,是結果集合,當你執行一個SELECT語句時DBMS會返回一個包含查詢結果的數據表,ResultSet接收用於表現這個數據表的對象

  • 作用:表示查詢後的返回值
  • 主要方法
    • Boolean next() throws SQLException:移動游標到結果集的下一行,並返回一個boolen值,結尾返回false
    • getXXX(String columnLabel):獲取指定列名的值,XXX表示Java數據類返回XXX類型,如getString(),columnLabel表示列名
    • getXXX(int columnIndex):獲取指定列索引的值,列索引從 1 開始

SQL註入

所謂SQL註入,是值通過把惡意SQL語句插入到Web表單提交或頁面請求的查詢字元串,最終達到欺騙伺服器的結果

SQL註入實例

  • 對於一個簡單的登入功能,關鍵函數如下:
 static boolean noProtectLogin(String username, String password, Statement statement) throws SQLException {
        //username="abc";
        //password = "or '1'='1'";
        String sql = "SELECT *FROM  user WHERE username= '" + username + "'AND password=+''";
        ResultSet resultSet = statement.executeQuery(sql);
        return resultSet.next();
    }
  • 方法中的username於password沒有進行任何處理,直接接受前端傳入的數據,這樣拼接的SQL語句會發送註入漏洞
  • 若把password參數修改成"or '1'='1'",username為任意值,那麼這條語句結果為SELECT *FROM user WHERE username= 'abc' AND password= 'or '1'='1''顯然這條語句的一直是true,這樣可以把user表中的所有用戶信息查詢到,就可以成功實現無密碼登入

SQL預編譯

也稱之為SQL預處理是一種,可以提高sql語句安全性和性能的技術

  • 預編譯SQL語句允許在運行之前定義SQL語句結構,同時使用占位符(通常是問號?)來動態表示數據部分

  • 在Java中可以使用PreparedStatement來創建和執行預編譯的SQL語句,剛剛的登入操作使用PreparedStatement操作的代碼如下

    static boolean noProtectLogin(String username, String password, Connection connection) throws SQLException {
    //      username="abc";
    //      password = "or '1'='1'";
            String sql = "SELECT * FROM  user WHERE username= ? AND password = ? ";
            PreparedStatement statement = connection.prepareStatement(sql);
            statement.setString(1, username);
            statement.setString(2, password);
            ResultSet resultSet = statement.executeQuery();
            return resultSet.next();
        }
    
    • 這樣我們在進行惡意的SQL註入,如把password定義成"or '1'='1'" 執行結果直接返回false

事務

什麼是事務? 官方的說法:事務是訪問資料庫的一個操作序列,資料庫應用系統通過執行業務集合要完成對資料庫的存取,簡單來說:事務是執行工作操作中最小的不可再分的工作單位,通常一個業務對應一個事務,多個操作同時進行要麼同時成功,要麼同時失敗,這就是事務

事務的理解

事務的特性

  • 原子性: 即不可分割,事務要麼全部被執行,要麼全部不執行.若所有的事務都提交成功,那麼資料庫操作被提交,資料庫狀態發生變化,若有一個子事務失敗,那麼其餘事務的資料庫操作都會回滾,即資料庫狀態回到事務執行之前,保持狀態不變
  • 一致性:事務的執行使得資料庫從一種正確狀態轉換為另一個正確狀態
  • 隔離性:在事務正確提交之前,不允許把事務對該數據的改變提交給其他事務,即在正確提交之前,其可能的結果不應該用於給其他事務
  • 持久性:即事務正確提交之後,其結果會永遠保存在資料庫之中,即事務提交之後有了其他故障,事務的處理結果也會得到保存

事務的通俗例子

  • 假設張三要給李四轉賬,要完成這個操作,要執行兩個事務,一個是:扣除張三的賬戶餘額;另一個是:李四的賬餘額增加,這兩個事務是不可分割的

事務的作用

  • 主要作用:保證了用戶的每一次操作都是可靠的,即便出現了異常的訪問情況,也不會破壞後臺的數據完整性,拿ATM機舉例子,若ATM在操作過程中突然出現故障,此時事務必須確保故障前對賬號的操作不生效,確保用戶於銀行的利益不受損

JDBC中對事物進行管理

在JDBC中,Connection介面中定義了幾個對事務操作的方法,我們一一講解

  • void setAutoCommit*(*boolean autoCommit*)* throws SQLException:在預設情況下JDBC連接處於自動提交模式,這意味著每個SQL語句執行後都會立即提交,這明顯不符合事務的特性,要我們進行顯性關閉,即將autoCommit設置為false
  • void commit():若所有操作都執行成功調用該方法提交事務
  • void rollback():若事務中任何操作失敗或出現異常錯誤則需調用rollback()回滾事務

銀行存/取款舉例

拿剛剛的張三和李四的例子說明

try {
            String sql1 = "UPDATE bank SET balance = balance - ? WHERE b_id=?";
            String sql2 = "UPDATE bank SET balance = balance + ? WHERE b_id=?";

            stmt1 = conn.prepareStatement(sql1);
            stmt2 = conn.prepareStatement(sql2);
            //事務1執行

            stmt1.setDouble(1, 500);
            stmt1.setString(2, "01");
            int r1 = stmt1.executeUpdate();
            //事務2執行

            stmt2.setDouble(1, 500);
            stmt2.setString(2, "02");
            int r2 = stmt2.executeUpdate();

            if (r1 == 1 && r2 == 1) {
                System.out.println("業務執行成功");
                conn.rollback();
            } else {
                System.out.println("業務執行失敗");
                conn.commit();
            }
        } catch (SQLException e) {
            e.printStackTrace();
            //有異常回滾事務
            conn.rollback();
        } finally {
            Objects.requireNonNull(stmt1).close();
            Objects.requireNonNull(stmt2).close();
            conn.close();
        }

連接池

連接池是創建和管理資料庫連接的技術,這些連接隨時準備被任何需要它的線程使用

連接池的原理

  • 連接池的基本思想是在系統初始化時,將資料庫連接作為對象存儲在運行記憶體中,當用戶需要訪問資料庫時,並非建立一個新的連接,而是從連接池中取出一個已建立的空閑連接對象.使用完畢後,用戶也並非將連接之間關閉,而是將連接放回連接池中,提供給下一次請求訪問使用,而連接池的建立,斷開都由連接池自身來管理.同時,還可以通過設置連接池的參數來控制連接池中的初始連接數,連接的上下限數以及每個連接的最大使用次數,最大空閑時間等等
  • 連接池參數作用
    • 最小連接數:是連接池一直保持於資料庫連接的數量,因此若應用程式對資料庫連接的使用量不大還設置較大的最小連接數,會造成大量的連接資源的浪費
    • 最大連接數:是連接池能申請的最大連接數,若資料庫連接請求超過最大連接數,後續的資料庫連接請求將被加入到等待隊列中
    • 若min連接於max連接相差很大時,那麼最先連接請求將會獲利,之後超過min連接的連接請求等價於新建一個資料庫連接,但這些大於min連接的資料庫連接在使用之後不會馬上被釋放,將被放入連接池中等待重覆利用

C3P0

C3P0是一個開放源代碼的JDBC連接池,包括了jdbc3和jdbc2擴展規範說明的Connection和Statement池的DataSources對象

C3P0的配置

導入方法於導入JDBC-Mysql jar包類似這裡不再贅述

  • 接著要配置c3p0-config.xml文件(這裡選擇xml配置)→直接配置到src文件夾下,否則會報配置錯誤

  • c3p0-config.xml文件的一般模板(可直接拷貝使用):

    <c3p0-config>
        <default-config>
            <!-- 資料庫驅動名 -->
            <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
    
            <!-- 資料庫的url -->
            <property name="jdbcUrl">jdbc:mysql://localhost:3306/demo</property>
    
            <!--用戶名。Default: null -->
            <property name="user">root</property>
    
            <!--密碼。Default: null -->
            <property name="password">root</property>
    
            <!--初始化時獲取三個連接,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
            <property name="initialPoolSize">3</property>
    
            <!--連接池中保留的最大連接數。Default: 15 -->
            <property name="maxPoolSize">5</property>
            <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 -->
    		
            <property name="acquireIncrement">3</property>
            <!--最大空閑時間,60秒內未使用則連接被丟棄。若為0則永不丟棄。Default: 0 -->
            <property name="maxIdleTime">60</property>
    
            <!--當連接池用完時客戶端調用getConnection()後等待獲取新連接的時間,超時後將拋出 SQLException,如設為0則無限期等待。單位毫秒。Default:  0 -->
            <property name="checkoutTimeout">0</property>
        </default-config>
    </c3p0-config>
    

C3P0的使用方法

C3P0中只有一個類,顯然這個類是最主要的,其包含了C3P0所有對資料庫連接的操作,接下來我們開始講解

ComboPooledDataSource類

  • 主要方法

    • Connection getConnection():從連接池中獲取一個資料庫連接,若當前沒有空閑連接,則新建連接,直到達到最大連接數.其返回一個Connection對象

    除了使用xml文件配置還可以在代碼中直接使用ComboPooledDataSource 類中的方法配置,但一般使用xml文件,避免冗餘

    • void setXXX() :設置C3P0中的各類屬性,XXX表示屬性,例如setUser(),setMinPoolSize(int min) 等等
  • 使用連接池測試更新語句

    public static void main(String[] args) throws SQLException {
            //建立連接池,獲取連接
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            Connection conn = dataSource.getConnection();
            //用連接池測試更新語句
            String sql = "SELECT * FROM  user WHERE username= ? AND password = ? ";
            PreparedStatement stmt = conn.prepareStatement(sql);
    
            stmt.setInt(1, 11111);
            stmt.setString(2, "11111111");
            ResultSet resultSet = stmt.executeQuery();
            if (resultSet.next()) {
                int username = resultSet.getInt("username");
                String password = resultSet.getString("password");
                System.out.println("username:" + username + "password:" + password);
            }
            stmt.close();
            conn.close();
        }
    

Druid(最好用的Java連接池)

Druid是目前最好資料庫連接池,在功能.性能.擴展性方面都弔打其他連接池,包括DBCP,C3P0,BoneCP,Proxool等等

Druid配置

  • Druid執行要一個jar包大家可以去官網:https://repo1.maven.org/maven2/com/alibaba/druid/下載所需的jar包,在導入即可

  • Druid的參數列表

    屬性(Parameter) 預設值(Default) 描述(Description)
    username **** 連接資料庫的用戶名
    password **** 連接資料庫的密碼
    jdbcUrl **** 同C3P0中的jdbcUrl屬性
    driverClassName 根據url自動識別 這一項可配可不配,如果不配置druid會根據url自動識別dbType,然後選擇相應的driverClassName
    initialSize 0 *初始化時建立物理連接的個數。初始化發生在顯示調用init方法,或者第一次getConnection時                   參見DBCP中的initialSize屬性
    maxActive 8 最大連接池數量(Maximum number of Connections a pool will maintain at any given time.
    maxIdle 8 已經不再使用,配置了也沒效果
    minIdle **** 最小連接池數量
    maxWait **** 獲取連接時最大等待時間,單位毫秒。配置了maxWait之後,預設啟用公平鎖,併發效率會有所下降,如果需要可以通過配置useUnfairLock屬性為true使用非公平鎖。
    poolPreparedState- ments false 是否緩存preparedStatement,也就是PSCache。PSCache對支持游標的資料庫性能提升巨大,比如說oracle。
    maxOpenPrepared- Statements -1 要啟用PSCache,必須配置大於0,當大於0時,poolPreparedStatements自動觸發修改為true。      在Druid中,不會存在Oracle下PSCache占用記憶體過多的問題,可以把這個數值配置大一些,比如說100
    testOnBorrow true 申請連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能。
    testOnReturn false 歸還連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能
    testWhileIdle false 建議配置為true,不影響性能,並且保證安全性。申請連接的時候檢測,如果空閑時間大於timeBetweenEvictionRunsMillis,執行validationQuery檢測連接是否有效。
    validationQuery **** 用來檢測連接是否有效的sql,要求是一個查詢語句。如果validationQuery為null,testOnBorrow、testOnReturn、 testWhileIdle都不會其作用。在mysql中通常為select 'x',在oracle中通常為select 1 from dual
    timeBetweenEviction-RunsMillis **** 1) Destroy線程會檢測連接的間隔時間 2) testWhileIdle的判斷依據
    minEvictableIdle- TimeMillis **** Destory線程中如果檢測到當前連接的最後活躍時間和當前時間的差值大於minEvictableIdleTimeMillis,則關閉當前連接。
    removeAbandoned **** 對於建立時間超過removeAbandonedTimeout的連接強制關閉
    removeAbandoned-Timeout **** 指定連接建立多長時間就需要被強制關閉
    logAbandoned false 指定發生removeabandoned的時候,是否記錄當前線程的堆棧信息到日誌中
    filters **** 屬性類型是字元串,通過別名的方式配置擴展插件,常用的插件有: 1)監控統計用的filter:stat 2)日誌用的filter:log4j  3)防禦sql註入的filter:wall
    • 紅色屬性為必要配置屬性
    • 定義application.properties(名字可以順便取但必須是properties),可以放置任意目錄下

    • application.properties文件一般模板如下
    driverClassName=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/test?userSSL=false&serverTimezone=Asia/Shanghai
    username=root
    password=123456
    initialSize=3
    maxActive=5
    maxWait=1000
    

Druid使用方法

Druid也是只要一個核心類DruidDataSource它實現了 javax.sql.DataSource 介面

  • 主要類與方法

    • DruidDataSource()構造方法:構造一個預設的 DruidDataSource實例這個實例主要用於顯性配置Druid屬性
      • setXXX(): 設置屬性值,如setUrl(String url),setInitialSize(int initialSize)等等
    • DruidDataSourceFactory類:是一個工廠類,用於根據提供的配置信息創建 DruidDataSource 實例,這個類簡化了從配置文件中載入配置信息並創建 DruidDataSource 的過程
      • createDataSource(Properties properties):最常用的方法之一,其接受一個Properties對象作為參數,從該對象讀取配置信息,並創建一個DruidDataSource
  • 將Druid封裝為JdbcUtils類

    public class JdbcUtil {
        private static DataSource dataSource;
    
        static {
            //先建立配置,連接連接池
            try {
                InputStream inputStream = JdbcUtil.class.getResourceAsStream("resource/application.properties");
                Properties props = new Properties();
                props.load(inputStream);
                dataSource = DruidDataSourceFactory.createDataSource(props);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static Connection getConnection() {
            Connection conn = null;
            try {
                conn = dataSource.getConnection();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return conn;
        }
    
        public static void close(ResultSet rs, PreparedStatement stmt, Connection conn) {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (stmt != null) {
                    stmt.close();
                }
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
  • 本文藉鑒同平臺許多作者如@少平的博客人生,@chy_18883701161,@濫好人


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

-Advertisement-
Play Games
更多相關文章
  • 基於electron32+vue3 setup+pinia2桌面端os管理解決方案ElectronVue3OS。 vue3-electron32-os全新原創Electron32+Vite5+Vue3+Pinia2+ArcoDesign+Echarts+Swiper搭建桌面版os管理模板。內置mac ...
  • title: 使用 nuxi generate 進行預渲染和部署 date: 2024/9/4 updated: 2024/9/4 author: cmdragon excerpt: 通過 nuxi generate 命令,你可以輕鬆地將 Nuxt 應用程式預渲染為靜態 HTML 文件,並將其部署到 ...
  • title: 探索 Nuxt Devtools:功能全面指南 date: 2024/9/3 updated: 2024/9/3 author: cmdragon excerpt: 摘要:本文介紹了Nuxt Devtools的功能和使用方法,包括自動安裝、手動安裝和各項主要功能,如頁面、組件、構建分析 ...
  • Angular 的路由復用策略(RouteReuseStrategy)是一種用於優化路由跳轉性能和提高用戶體驗的機制。通過實現RouteReuseStrategy介面,後可以自定義路由的復用行為,避免不必要的組件銷毀和重建,同時保持組件的狀態。 以下是對Angular路由復用策略的詳細介紹: 一、基 ...
  • 1. 概念的特性 1.1. 專一性原則(specificity principle)認為概念與目的應該一一對應 1.1.1. 專一性原則已被證明是概念設計中最有用的原則之一 1.1.2. 一個概念最多只能滿足一個目的 1.2. 很少有沒有目的的概念 1.2.1. 如果本應隱藏的用戶機制被暴露,可能會 ...
  • 1. 概念的關係 1.1. 概念是獨立的,彼此間無須相互依賴 1.1.1. 一個概念是應該獨立地被理解、設計和實現的 1.1.2. 獨立性是概念的簡單性和可重用性的關鍵 1.2. 軟體存在依賴性 1.2.1. 不是說一個概念需要依賴另一個概念才能正確運行 1.2.2. 只有當一個概念存在時,包含另一 ...
  • 模板方法模式(Template Method Pattern)也稱之為模板模式(Template Pattern),是設計模式中最簡單的模式之一。 先來看定義:定義一個操作中演算法的骨架(模板),將一些步驟延遲到子類中,模板方法使得子類可以不改變演算法的結構即可重新定義演算法某些特定的步驟。這個定義還是有 ...
  • 1. 概念的組合 1.1. 概念不像程式那樣,可以用較大的包含較小的 1.1.1. 每個概念對用戶來說都是平等的,軟體或系統就是一組串聯運行的概念組合 1.2. 概念是通過操作來同步組合的 1.2.1. 同步並不增加新的概念操作,但會限制已有的操作,從而消除一些獨立概念可能會出現的操作序列 1.3. ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...