一篇文章幫你搞懂Java與數據庫如何建立連接?

JDBC

一、DBUtils概述

1.1 數據的持久化

  • 持久化(persistence):把數據保存到可掉電式存儲設備中以供之後使用。大多數情況下,特別是企業級應用,數據持久化意味着將內存中的數據保存到硬盤上加以”固化”,而持久化的實現過程大多通過各種關係數據庫來完成
  • 持久化的主要應用是將內存中的數據存儲在關係型數據庫中,當然也可以存儲在磁盤文件、XML數據文件中。

1.2數據持久化的好處

  • 程序代碼重用性強,即使更換數據庫,只需要更改配置文件,不必重寫程序代碼。

  • 業務邏輯代碼可讀性強,在代碼中不會有大量的SQL語言,提高程序的可讀性。

  • 持久化技術可以自動優化,以減少對數據庫的訪問量,提高程序運行效率。

    數據持久化對象的基本操作有:保存、更新、刪除、查詢等。

二、獲取數據庫連接

2.1 要素一:Driver接口實現類

2.1.1 Driver接口介紹

  • java.sql.Driver 接口是所有 DBUtils 驅動程序需要實現的接口。這個接口是提供給數據庫廠商使用的,不同數據庫廠商提供不同的實現。

  • 在程序中不需要直接去訪問實現了 Driver 接口的類,而是由驅動程序管理器類(java.sql.DriverManager)去調用這些Driver實現。

    - Oracle的驅動:oracle.jdbc.driver.OracleDriver
    - mySql 的驅動:com.mysql.jdbc.Driver
    
  • 將上述jar包拷貝到Java工程的一個目錄中,習慣上新建一個lib文件夾,不同的idea有不同的操作。

2.1.2 加載與註冊DBUtils驅動

  • 加載驅動:加載 DBUtils 驅動需調用 Class 類的靜態方法 forName(),向其傳遞要加載的 DBUtils 驅動的類名

    • Class.forName(“com.mysql.jdbc.Driver”);
  • 註冊驅動:DriverManager 類是驅動程序管理器類,負責管理驅動程序

    • 使用DriverManager.registerDriver(com.mysql.jdbc.Driver)來註冊驅動

    • 通常不用顯式調用 DriverManager 類的 registerDriver() 方法來註冊驅動程序類的實例,因爲 Driver 接口的驅動程序類包含了靜態代碼塊,在這個靜態代碼塊中,會調用 DriverManager.registerDriver() 方法來註冊自身的一個實例。

2.2 要素二:URL

  • 幾種常用數據庫的 DBUtils URL

    MySQL的連接URL編寫方式:

    • jdbc:mysql://主機名稱:mysql服務端口號/數據庫名稱?參數=值&參數=值
    • jdbc:mysql://localhost:3306/shop_jdbc
    • jdbc:mysql://localhost:3306/shop_jdbc?useUnicode=true&characterEncoding=utf8(如果程序與服務器端的字符集不一致,會導致亂碼,那麼可以通過參數指定服務器端的字符集)
    • jdbc:mysql://localhost:3306/atguigu?user=root&password=root

2.3 要素三:用戶名和密碼

  • user,password可以用“屬性名=屬性值”方式告訴數據庫
  • 可以調用 DriverManager 類的 getConnection() 方法建立到數據庫的連接

2.4 數據庫連接方式舉例

//方式一
@Test
    public void testConnection1() throws Exception{
        //1.數據庫連接的4個基本要素:
        String url = "jdbc:mysql://localhost:3306/shop_jdbc";
        String user = "root";
        String password = "root";
        //8.0之後名字改了  com.mysql.cj.jdbc.Driver
        String driverName = "com.mysql.jdbc.Driver";

        //2.實例化Driver
        Class clazz = Class.forName(driverName);
        Driver driver = (Driver) clazz.newInstance();
        //3.註冊驅動
        DriverManager.registerDriver(driver);
        //4.獲取連接
        Connection conn = DriverManager.getConnection(url, user, password);
        System.out.println(conn);
    }

//方式二
	//一般常用這種方式
    @Test
    public void testConnection2() throws Exception{
        //1.數據庫連接的4個基本要素:
        String url = "jdbc:mysql://localhost:3306/xinzhishop";
        String user = "root";
        String password = "root";
        String driverName = "com.mysql.jdbc.Driver";

        //2.加載驅動
        Class.forName(driverName);

        //3.獲取連接
        Connection conn = DriverManager.getConnection(url, user, password);
        System.out.println(conn);
    }

//方式三
    @Test
    public void testConnection3() throws Exception{
        //1.數據庫連接的4個基本要素:
        String url = "jdbc:mysql://localhost:3306/xinzhishop";
        String user = "root";
        String password = "root";
        String driverName = "com.mysql.jdbc.Driver";

        //3.獲取連接
        Connection conn = DriverManager.getConnection(url, user, password);
        System.out.println(conn);
    }

//方式四
	/**
     * 使用配置文件的方式 連接數據庫
     * 當需要更改數據庫時 可以直接在文件中更改  方便 快捷
     * @throws Exception
     */



    @Test
    public void testConnection4() throws Exception{
        //1.數據庫連接的4個基本要素:
        //jdbc.confile  是文件名稱
        //文件內容
        //> url=jdbc:mysql://localhost:3306/shop_jdbc 
		//> user=root 
		//> password=root
		//> driverName=com.mysql.jdbc.Driver
		
        InputStream in = TestUser.class.getClassLoader().getResourceAsStream("jdbc.config");
        Properties properties = new Properties();
        properties.load(in);

        String url = properties.getProperty("url");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String driverName = properties.getProperty("driverName");

        //2.加載驅動
        Class.forName(driverName);

        //3.獲取連接
        Connection conn = DriverManager.getConnection(url, user, password);
        System.out.println(conn);
    }

}

說明:使用配置文件的方式保存配置信息,在代碼中加載配置文件

使用配置文件的好處:

①實現了代碼和數據的分離,如果需要修改配置信息,直接在配置文件中修改,不需要深入代碼
②如果修改了配置信息,省去重新編譯的過程。

三、Statement 和 PreparedStatement

3.1 操作和訪問數據庫

  • 數據庫連接被用於向數據庫服務器發送命令和 SQL 語句,並接受數據庫服務器返回的結果。其實一個數據庫連接就是一個Socket連接。

  • 在 java.sql 包中有 3 個接口分別定義了對數據庫的調用的不同方式:

    • Statement:用於執行靜態 SQL 語句並返回它所生成結果的對象。
    • PrepatedStatement:SQL 語句被預編譯並存儲在此對象中,可以使用此對象多次高效地執行該語句。

3.2 使用Statement操作數據表的弊端

  • 通過調用 Connection 對象的 createStatement() 方法創建該對象。該對象用於執行靜態的 SQL 語句,並且返回執行結果。

  • Statement 接口中定義了下列方法用於執行 SQL 語句:

    int excuteUpdate(String sql):執行更新操作INSERTUPDATEDELETE
    ResultSet executeQuery(String sql):執行查詢操作SELECT
    
  • 但是使用Statement操作數據表存在弊端:

    • 問題一:存在拼串操作,繁瑣
    • 問題二:存在SQL注入問題
  • SQL 注入是利用某些系統沒有對用戶輸入的數據進行充分的檢查,而在用戶輸入數據中注入非法的 SQL 語句段或命令(如:String sql = "select id,username,password from user where username = 'zxcaf1' or 1 = 1";) ,從而利用系統的 SQL 引擎完成惡意行爲的做法。

  • 對於 Java 而言,要防範 SQL 注入,只要用 PreparedStatement(從Statement擴展而來) 取代 Statement

3.2.1 增刪改 只需修改對應的 sql 語句就可

@Test
    public void testConnection2() throws Exception{
        //1.數據庫連接的4個基本要素:
        String url = "jdbc:mysql://localhost:3306/shop_jdbc";
        String user = "root";
        String password = "root";
        //8.0之後名字改了  com.mysql.cj.jdbc.Driver
        String driverName = "com.mysql.jdbc.Driver";

        //2、加載驅動(實例化Driver  註冊驅動)
        Class.forName(driverName);

        //3、獲取連接
        Connection conn = DriverManager.getConnection(url,user,password);

        //sql語句
        Statement statement = conn.createStatement();
        statement.execute("insert into user value (1,'ycw','123')");

        System.out.println(conn);
    }

3.2.2 查詢

@Test
    public void testConnection2() throws Exception{
        //1.數據庫連接的4個基本要素:
        String url = "jdbc:mysql://localhost:3306/shop_jdbc";
        String user = "root";
        String password = "root";
        //8.0之後名字改了  com.mysql.cj.jdbc.Driver
        String driverName = "com.mysql.jdbc.Driver";
        String sql = "select id,username,password from user";

        //2、加載驅動(實例化Driver  註冊驅動)
        Class.forName(driverName);

        //3、獲取連接
        Connection conn = DriverManager.getConnection(url,user,password);

        //sql語句
        Statement statement = conn.createStatement();
        ResultSet resultSet = statement.executeQuery(sql);
        resultSet.next();
        int id = resultSet.getInt("id");
        String username = resultSet.getString("username");
        String pwd = resultSet.getString("password");
        System.out.println(id+username+pwd);

        resultSet.next();
        id = resultSet.getInt("id");
        username = resultSet.getString("username");
        pwd = resultSet.getString("password");
        System.out.println(id+username+pwd);
    }

3.2.3 代碼優化

資源的釋放

  • 釋放ResultSet, Statement,Connection。
  • 數據庫連接(Connection)是非常稀有的資源,用完後必須馬上釋放,如果Connection不能及時正確的關閉將導致系統宕機。Connection的使用原則是儘量晚創建,儘量早的釋放。
  • 可以在finally中關閉,保證及時其他代碼出現異常,資源也一定能被關閉。

1、手動處理異常

2、合理關係資源

3、遍歷處理數據

@Test
    public void testConnection2(){
        //1.數據庫連接的4個基本要素:
        String url = "jdbc:mysql://localhost:3306/shop_jdbc";
        String user = "root";
        String password = "root";
        //8.0之後名字改了  com.mysql.cj.jdbc.Driver
        String driverName = "com.mysql.jdbc.Driver";
        String sql = "select id,username,password from user";

        //2、加載驅動(實例化Driver  註冊驅動)
        Connection conn = null;
        Statement statement = null;
        ResultSet resultSet = null;
        
        try {
            Class.forName(driverName);
            //3、獲取連接
            conn = DriverManager.getConnection(url,user,password);

            //sql語句
            statement = conn.createStatement();
            resultSet = statement.executeQuery(sql);
            //使用while遍歷讀取
            while (resultSet.next()){
                int id = resultSet.getInt("id");
                String username = resultSet.getString("username");
                String pwd = resultSet.getString("password");
                System.out.println(id+username+pwd);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //避免浪費資源  用完即關閉資源
            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (statement != null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (resultSet != null){
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        
    }

3.2.4 公共提取

1、不管哪裏要操作數據庫都要獲取連接,所以能不能提取

2、不管哪裏都要關閉資源能不能提取


public class DBUtil {
    public static Connection getConnection(){
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "root";
        String driverName = "com.mysql.jdbc.Driver";
        
        Connection conn = null;
        try {
            Class clazz = Class.forName(driverName);
            Driver driver = (Driver) clazz.newInstance();
            //3.註冊驅動
            DriverManager.registerDriver(driver);
            //4.獲取連接
            conn = DriverManager.getConnection(url, user, password);
        }catch (Exception e){
            e.printStackTrace();
        }
        return conn;
    }

    public static void closeAll(Connection connection, Statement statement, ResultSet rs){
       if(connection != null){
           try {
               connection.close();
           } catch (SQLException e) {
               e.printStackTrace();
           }
       }
       if(statement != null){
           try {
               statement.close();
           } catch (SQLException e) {
               e.printStackTrace();
           }
       }
       if( rs != null ){
           try {
               rs.close();
           } catch (SQLException e) {
               e.printStackTrace();
           }
       }
    }
    
    
}
public static void main(String[] args) {

    String sql = "select id,name,age from user";

    Connection conn = null;
    Statement statement = null;
    ResultSet resultSet = null;
    //try catch 獲取異常
    try {
        conn = DBUtil.getConnection();
        statement = conn.createStatement();
        resultSet = statement.executeQuery(sql);
        //使用遍歷獲取數據
        while (resultSet.next()){
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            int age = resultSet.getInt("age");
            System.out.println("id=" + id);
            System.out.println("name=" + name);
            System.out.println("age=" + age);
        }
    } catch (Exception exception) {
        exception.printStackTrace();
    }finally {
        DBUtil.closeAll(conn,statement,resultSet);
    }
}

3.2.5 sql注入問題

Sql注入是一種將sql代碼添加到輸入參數中,傳遞到Sql服務器解析並執行的一種攻擊手法

由於SQL語句是通過拼接完成的,那麼就有可能存在SQL注入攻擊

如何進行SQL注入攻擊?

數字注入

String sql = "select id,username,password from user where username = 'zxcaf1' or 1 = 1";

3.3 PreparedStatement的使用

使用preparestatement做查詢語句時可解決SQL注入的問題

    @Test
    public void testConnection4() throws Exception{
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "root";
        String driverName = "com.mysql.jdbc.Driver";
        
        //這裏的SQL語句沒有使用拼接,而是使用了?來代替字段
        String sql = "select id,username,password from user where id = ? and username = ?";
        //2、加載驅動
        Class.forName(driverName);

        //3、獲取連接
        Connection conn = DriverManager.getConnection(url,user,password);
        PreparedStatement preparedStatement = conn.prepareStatement(sql);
        //爲 ? 設置參數
        preparedStatement.setInt(1,1);
        preparedStatement.setString(2,"ycw");
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()){
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String pwd = resultSet.getString("password");
            System.out.println(id+username+pwd);
        }


    }

PreparedStatement可以有效防止sql注入,所以生產環境上一定要使用PreparedStatement,而不能使用Statement

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章