1. 程式人生 > >編寫工具類簡化JDBC(CURD)的操作

編寫工具類簡化JDBC(CURD)的操作

JDBC

全稱是JAVA DATE BASE Connectity(java資料庫連線),可以為多種資料庫提供統一的訪問。即定義一個規範的介面,讓各資料庫廠商提供介面的具體實現類,這種實現類同時也被稱作驅動。

JDBC規範

(4個核心物件) - DriverManager:用於註冊驅動。 - Connection:與資料庫建立連線。 - Statement:操作資料庫sql語句的物件。 - ResultSet:結果集(存放由select語句查詢到的資料資訊)

JDBC連線資料庫的5個主要步驟

(即根據4個核心類物件推匯出的步驟並封裝)

1.載入驅動

方式一: 直接註冊驅動

DriverManager.registerDriver(new Driver());

方式二:(推薦使用) 通過反射的方式,實現驅動的載入

Class.forName("com.mysql.cj.jdbc.Driver");

注意:

  • 在實際開發的過程中,不推薦使用方式一來進行驅動的註冊,檢視Driver原始碼(如下)可以看到,它本身就載入了一次驅動,這樣一來就會導致驅動程式註冊兩次,也就表示了在記憶體中會有兩個Driver物件
  • 程式依賴mysql的jar包,一旦脫離,程式將無法編譯,將來程式切換底層資料庫將會非常麻煩。

Driver原始碼

static {
        try
{ DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } }
  • 採用方式二不會導致驅動物件在記憶體中重複出現,程式僅需要一個字串,不需要依賴具體的驅動,使程式的靈活性更高。

2.建立連線

通過註冊的JDBC驅動來建立資料庫連線物件從而獲取資料庫的URL,使用者名稱,密碼來連線資料庫

 String
url = "jdbc:mysql://localhost:3306/day04?serverTimezone=UTC&characterEnconding=utf-8"; Connection con = DriverManager.getConnection(url,"root","123456");

解析

String url = "jdbc:mysql://localhost:3306/day04?serverTimezone=UTC&characterEnconding=utf-8";

jdbc:是JDBC連線協議 mysq:// 是mysql資料庫連線協議,即JDBC子協議 Localhost:3306 是主機和埠 day04 是指需要連線的資料庫 serverTimezone=UTC:國際通用時區 characterEncoding=utf-8:資料庫編碼格式 (主要是解決資料庫亂碼的問題)

3.操作資料

方式一:(該方式有弊端,會導致sql注入問題) 通過資料庫連線物件獲取操作資料庫sql語句的物件Statement

String sql = "select * from user where username = '"+name+"' and password = '"+passwd+"';";
            Statement stmt = con.createStatement();

模擬使用者登入 需求:通過鍵盤錄入來獲取使用者名稱和密碼,同時在資料庫中判斷該使用者是否存在 存在即登入成功,反之則失敗。

程式碼一:

import java.sql.*;
import java.util.Scanner;

public class JDBCTest3 {

    public static void main(String[] args) {
        System.out.println("請輸入使用者名稱");
        String name = new Scanner(System.in).nextLine();
        System.out.println("請輸入密碼");
        String passwd = new Scanner(System.in).nextLine();
    //載入驅動
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        Connection con = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
        //建立連線
            String url = "jdbc:mysql://localhost:3306/day04?serverTimezone=UTC&characterEnconding=utf-8";
            con = DriverManager.getConnection(url,"root","123456");

        //操作資料
            String sql = "select * from user where username = '"+name+"' and password = '"+passwd+"';";
            stmt = con.createStatement();
            rs = stmt.executeQuery(sql);
            // 判斷返回的結果
            if (rs.next()) {
                // 登入成功
                int id = rs.getInt("id");
                String u_name = rs.getString("username");
                String u_pwd = rs.getString("password");
                String email = rs.getString("email");
//                System.out.println(id + " : " + u_name + " : " + u_pwd + " : " + email);
                System.out.println("登入成功!");
            } else {
                // 登入失敗
                System.out.println("登入失敗! 使用者名稱或密碼錯誤!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //釋放資源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                rs = null;
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                stmt = null;
            }
            if (con != null) {
                try {
                    con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                con = null;
            }

        }
    }
}

結果:

請輸入使用者名稱
lisi
請輸入密碼
123
登入成功!

由於對使用者的輸入沒有進行充分的檢查而SQL又是拼接而成,在使用者輸入引數時,在引數中新增一些SQL 關鍵字,達到改變SQL執行結果的目的。

案例一: 輸入使用者時: zhangsan’ or ‘1’=’1 password 隨意 程式碼中sql語句進行拼接

select * from user where username ='zhangsan' or '1'='1' and password ='';

由於 and 優先順序 執行 高於 or,導致不需要任何密碼都能登入成功

請輸入使用者名稱
lisi' or '1'='1
請輸入密碼
00000000
登入成功!

案例二: 使用者輸入 username: zhangsan’ – password 隨意 程式碼中sql語句進行拼接

select * from user where username ='zhangsan' -- ' and password ='' ;

在SQL新增 – 是mysql的註釋

請輸入使用者名稱
lisi' -- 
請輸入密碼
111111
登入成功!

方式二:(推薦使用) 通過資料庫連線物件獲取操作資料庫sql語句的物件PreparedStatement

String sql = "select * from user where username = ? and password = ?;";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setString(1,name);
pstmt.setString(2,passwd);

解析:

  • ?表示佔位符
  • 將帶有?的SQL 傳送給資料庫完成預編譯 ,在SQL編譯後發現缺少兩個引數。PreparedStatement 可以將? 代替引數傳送給資料庫伺服器,因為SQL已經編譯過,引數中特殊字元不會當做特殊字元編譯,無法達到SQL注入的目的 。
  • setString(int parameterIndex, String x)
//表示給第一個?設定一個字串型別的值
pstmt.setString(1,name);
  • PreparedStatement是Statement的子介面,它的例項物件可以通過呼叫Connection.preparedStatement(sql)方法獲得,相對於Statement物件而言: PreperedStatement可以避免SQL注入的問題。 Statement會使資料庫頻繁編譯SQL,可能造成資料庫緩衝區溢位。PreparedStatement可對SQL進行預編譯,從而提高資料庫的執行效率。並且PreperedStatement對於sql中的引數,允許使用佔位符的形式進行替換,簡化sql語句的編寫。

程式碼二:

import java.sql.*;
import java.util.Scanner;

public class JDBCTest3 {

    public static void main(String[] args) {
        System.out.println("請輸入使用者名稱");
        String name = new Scanner(System.in).nextLine();
        System.out.println("請輸入密碼");
        String passwd = new Scanner(System.in).nextLine();

        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            String url = "jdbc:mysql://localhost:3306/day04?serverTimezone=UTC&characterEnconding=utf-8";
            con = DriverManager.getConnection(url,"root","123456");

            String sql = "select * from user where username = ? and password = ?;";
            pstmt = con.prepareStatement(sql);
            pstmt.setString(1,name);
            pstmt.setString(2,passwd);


            rs = pstmt.executeQuery();
            // 判斷返回的結果
            if (rs.next()) {
                // 登入成功
                int id = rs.getInt("id");
                String u_name = rs.getString("username");
                String u_pwd = rs.getString("password");
                String email = rs.getString("email");
//                System.out.println(id + " : " + u_name + " : " + u_pwd + " : " + email);
                System.out.println("登入成功!");
            } else {
                // 登入失敗
                System.out.println("登入失敗! 使用者名稱或密碼錯誤!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                rs = null;
            }
            if (pstmt != null) {
                try {
                    pstmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                pstmt = null;
            }
            if (con != null) {
                try {
                    con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                con = null;
            }

        }
    }
}

結果

請輸入使用者名稱
lisi
請輸入密碼
123
登入成功!

4.獲取操作資料

  • executeUpdate 無結果集 用於向資料庫傳送 insert/update/delete 語句,返回int 型別引數,代表影響記錄行數.
int count = pstmt.executeUpdate();
  • executeQuery 通過結果集獲取操作資料庫後的結果 用於向資料庫傳送 select 語句,返回ResultSet 結果集物件.。
ResultSet rs = pstmt.executeQuery();
  • execute 用於判斷資料庫傳送任何SQL語句(包括 select/insert/update/delete) 返回boolean型別 ,SQL執行結果是select返回true,否則 false。
boolean flag = pstmt.execute();

5.釋放資源

Jdbc程式執行完後,切記要釋放程式在執行過程中,建立的那些與資料庫進行互動的物件,這些物件通常是ResultSet, Statement和Connection物件。 特別是Connection物件,它是非常稀有的資源,用完後必須馬上釋放,如果Connection不能及時、正確的關閉,極易導致系統宕機。Connection的使用原則是儘量晚建立,儘量早的釋放。為確保資源釋放程式碼能執行,資源釋放程式碼也一定要放在finally語句中。

無結果集釋放資源

public static void release(Connection con, Statement stmt) {
        if(con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            con = null;
        }

        if(stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            stmt = null;
        }
    }

有結果集釋放資源

public static void release(Connection con,Statement stmt,ResultSet rs) {
        if(con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            con = null;
        }

        if(stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            stmt = null;
        }

       release(rs);
    }

    public static void release(ResultSet rs) {
        if(rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null;
        }
    }

對上述JDBC連線資料庫並操作資料的步驟進行封裝

  • 抽取資料庫資訊

    1. 抽取註冊驅動依賴的mysql的jar包
    2. 抽取註冊的JDBC驅動來建立資料庫連線物件從而獲取資料庫的URL,使用者名稱,密碼
    3. 把以上抽取的物件放入配置檔案中
driverClass=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/day04?serverTimezone=UTC&characterEncoding=utf-8
username=root
passwd=123456

解析配置檔案並對上述JDBC連線資料庫的步驟進行封裝

import java.sql.*;

public class JDBCUtils {

    private static final String DRIVERCLASS="com.mysql.cj.jdbc.Driver";
    private static final String URL="jdbc:mysql://localhost:3306/day04?serverTimezone=UTC" +
            "&characterEncoding=utf-8";
    private static final String USERNAME="root";
    private static final String PASSWD="123456";

    //載入驅動
    public static void loadDriver() {
        try {
            Class.forName(DRIVERCLASS);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("驅動載入失敗");
        }
    }

    //獲取連線
    public static Connection getConnection() throws SQLException {
        String url = URL;
        Connection con = DriverManager.getConnection(url,USERNAME,PASSWD);

        return con;
    }

    //無結果集釋放資源
    public static void realease(Connection con, Statement stmt) {
        if(stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            stmt = null;
        }

        if(con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            con = null;
        }
    }

    //有結果集釋放資源
    public static void realease(Connection con, Statement stmt,ResultSet rs) {
        if(stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            stmt = null;
        }

        if(con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            con = null;
        }

        realease(rs);
    }

    public static void realease(ResultSet rs) {
        if(rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null;
        }
    }
}

測試:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JDBCTest5 {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement pstmt = null;

        try {
            con = JDBCUTils2.getConnection();

            String sql = "update user set username = ?,password = ?,email = ? where id = ?;";
            pstmt = con.prepareStatement(sql);

            pstmt.setString(1,"jiuba");
            pstmt.setString(2,"888");
            pstmt.setString(3,"[email protected]");
            pstmt.setString(4,"2");

            int count = pstmt.executeUpdate();
            System.out.println(count);

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUTils2.realease(con,pstmt);
        }
    }
}

結果

1