1. 程式人生 > 其它 >資料排序:選擇排序和氣泡排序

資料排序:選擇排序和氣泡排序

JDBC

1. JDBC 概述

1.1 客戶端操作資料庫的方式

  • 1) 方式1: 使用第三方客戶端來訪問 MySQL:SQLyog
  • 2) 方式2: 使用命令列3) 我們今天要學習的是通過 Java程式 來訪問 MySQL 資料庫

1.2 什麼是JDBC

JDBC(Java Data Base Connectivity) 是 Java 訪問資料庫的標準規範.是一種用於執行SQL語句的Java API,可以為
多種關係資料庫提供統一訪問,它由一組用Java語言編寫的類和介面組成。是Java訪問資料庫的標準規範.

1.3 JDBC 原理

JDBC是介面,驅動是介面的實現,沒有驅動將無法完成資料庫連線,從而不能操作資料庫!每個資料庫廠商都需
要提供自己的驅動,用來連線自己公司的資料庫,也就是說驅動一般都由資料庫生成廠商提供。

總結:

JDBC就是由sun公司定義的一套操作所有關係型資料庫的規則(介面),而資料庫廠商需要實現這套介面,提供資料庫
驅動jar包, 我們可以使用這套介面程式設計,真正執行的程式碼是對應驅動包中的實現類。

2. JDBC 開發

2.1 資料準備

-- 建立 jdbc_user表
CREATE TABLE jdbc_user (
    id INT PRIMARY KEY AUTO_INCREMENT ,
    username VARCHAR(50),
    PASSWORD VARCHAR(50),
    birthday DATE
);
-- 新增資料
INSERT INTO jdbc_user (username, PASSWORD,birthday)
VALUES('admin1', '123','1991/12/24'), ('admin2','123','1995/12/24'), ('test1', '123','1998/12/24'), ('test2', '123','2000/12/24');

2.2 MySql驅動包

1. 將MySQL驅動包新增到jar包庫資料夾中,Myjar資料夾,用於存放當前專案需要的所有jar包

2. 在 idea中 配置jar包庫的位置

3. 建立一個新的專案jdbc_task01, 配置jar包庫

2.3 API使用: 1.註冊驅動

  • JDBC規範定義驅動介面: java.sql.Driver
  • MySql驅動包提供了實現類: com.mysql.jdbc.Driver

1) 程式碼示例

public class JDBCDemo01 {
  public static void main(String[] args) throws ClassNotFoundException {
    //1.註冊驅動
    // forName 方法執行將類進行初始化
    Class.forName("com.mysql.jdbc.Driver");
  }
}

2) 為什麼這樣可以註冊驅動?

我們知道 Class類的forName方法 ,可以將一個類初始化, 現在我們一起Driver類的 看一下原始碼

// Driver類是MySql提供的資料庫驅動類, 實現了JDBC的Driver介面 java.sql.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
  // 空參構造
  public Driver() throws SQLException {}
  //靜態程式碼塊,Class類的 forName()方法將Driver類 載入到記憶體, static程式碼塊會自動執行
  static {
    try {
      /*
        DriverManager 驅動管理類
        registerDriver(new Driver) 註冊驅動的方法
        註冊資料庫驅動
      */
        DriverManager.registerDriver(new Driver());
       } catch (SQLException var1) {
          throw new RuntimeException("Can't register driver!");
       }
  }
}

注:

  • JDBC3 開始,目前已經普遍使用的版本。可以不用註冊驅動而直接使用。 Class.forName 這句話可以省略。

2.4 API使用: 2.獲得連線

  • Connection 介面,代表一個連線物件 ,具體的實現類由資料庫的廠商實現
  • 使用 DriverManager類的靜態方法,getConnection可以獲取資料庫的連線

1) getConnection方法 3個 連線引數說明

2) 對URL的詳細說明

jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8
  • JDBC規定url的格式由三部分組成,每個部分中間使用冒號分隔。
    • 第一部分是協議 jdbc,這是固定的;
    • 第二部分是子協議,就是資料庫名稱,連線mysql資料庫,第二部分當然是mysql了;
    • 第三部分是由資料庫廠商規定的,我們需要了解每個資料庫廠商的要求,mysql的第三部分分別由資料庫伺服器的IP地址(localhost)、埠號(3306),以及要使用的 資料庫名稱 組成。

3) 程式碼示例

public class JDBCDemo02 {

    public static void main(String[] args) throws Exception {
        //1.註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2.獲取連線 url,使用者名稱, 密碼
     String url = "jdbc:mysql://localhost:3306/db4";
     Connection con = DriverManager.getConnection(url, "root", "123456");
     //com.mysql.jdbc.JDBC4Connection@2e3fc542
     System.out.println(con);
  }
} 

2.5 API 使用: 3.獲取語句執行平臺

  • Connection介面中的方法說明
  • Statement createStatement() 建立 SQL語句執行物件

程式碼示例

public class JDBCDemo03 {
    public static void main(String[] args) throws Exception {
    //1.註冊驅動
    Class.forName("com.mysql.jdbc.Driver");
    //2.獲取連線 url,使用者名稱, 密碼
    String url = "jdbc:mysql://localhost:3306/db4";
    Connection con = DriverManager.getConnection(url, "root", "123456");
    //3.獲取 Statement物件
    Statement statement = con.createStatement();
    //4.執行建立表操作
    String sql = "create table test01(id int, name varchar(20),age int);";
    //5.增刪改操作 使用executeUpdate,增加一張表
    int i = statement.executeUpdate(sql);
    //6.返回值是受影響的函式
    System.out.println(i);
    //7.關閉流
    statement.close();
    con.close();
  }
}

2.6 API 使用: 4.處理結果集

  • 只有在進行查詢操作的時候, 才會處理結果集

程式碼示例

public class JDBCDemo04 {
  public static void main(String[] args) throws SQLException {
    //1.註冊驅動 可以省略
    //2.獲取連線
    String url = "jdbc:mysql://localhost:3306/db4";
    Connection con = DriverManager.getConnection(url, "root", "123456");
    //3.獲取 Statement物件
    Statement statement = con.createStatement();
    String sql = "select * from jdbc_user";
    //執行查詢操作,返回的是一個 ResultSet 結果物件
    ResultSet resultSet = statement.executeQuery(sql);
    //4.處理結果集 resultSet
  }
}

2.6.1 ResultSet介面

  • 作用:封裝資料庫查詢的結果集,對結果集進行遍歷,取出每一條記錄。

程式碼示例

public class JDBCDemo04 {
  public static void main(String[] args) throws SQLException {
    //1.註冊驅動 可以省略
    //2.獲取連線
    String url = "jdbc:mysql://localhost:3306/db4";
    Connection con = DriverManager.getConnection(url, "root", "123456");
    //3.獲取 Statement物件
    Statement statement = con.createStatement();
    String sql = "select * from jdbc_user";
    //執行查詢操作,返回的是一個 ResultSet 結果物件
    ResultSet resultSet = statement.executeQuery(sql);
    //4.處理結果集
    // //next 方法判斷是否還有下一條資料
    // boolean next = resultSet.next();
    // System.out.println(next);
    //
    // //getXXX 方法獲取資料 兩種方式
    // int id = resultSet.getInt("id");//列名
    // System.out.println(id);
    //
    // int anInt = resultSet.getInt(1);//列號
    // System.out.println(anInt);
    //使用while迴圈
    while(resultSet.next()){
      //獲取id
      int id = resultSet.getInt("id");
      //獲取姓名
      String username = resultSet.getString("username");
      //獲取生日
      Date birthday = resultSet.getDate("birthday");
      System.out.println(id + " = " +username + " : " + birthday);
    }
    //關閉連線
    resultSet.close();
    statement.close();
    con.close();
  }
}

2.7 API 使用: 5.釋放資源

  • 1) 需要釋放的物件:ResultSet 結果集,Statement 語句,Connection 連線
  • 2) 釋放原則:先開的後關,後開的先關。ResultSet ==> Statement ==> Connection
  • 3) 放在哪個程式碼塊中:finally 塊
    • 與IO流一樣,使用後的東西都需要關閉!關閉的順序是先開後關, 先得到的後關閉,後得到的先關閉

程式碼示例

public class JDBCDemo05 {
  public static void main(String[] args) {
    Connection connection = null;
    Statement statement = null;
    ResultSet resultSet = null;
    try {
      //1.註冊驅動(省略)
      //2.獲取連線
      String url = "jdbc:mysql://localhost:3306/db4";
      connection = DriverManager.getConnection(url, "root", "123456");
      //3.獲取 Statement物件
      statement = connection.createStatement();
      String sql = "select * from jdbc_user";
      resultSet = statement.executeQuery(sql);
    } catch (SQLException e) {
      e.printStackTrace();
    } finally{
      /**
      * 開啟順序: connection ==> statement => resultSet
      * 關閉順序: resultSet ==> statement ==> connection
      */
      try {
        connection.close();
        resultSet.close();
        statement.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }
}

2.8 步驟總結

  • 1. 獲取驅動(可以省略)
  • 2. 獲取連線
  • 3. 獲取Statement物件
  • 4. 處理結果集(只在查詢時處理)
  • 5. 釋放資源

3. JDBC實現增刪改查

3.1 JDBC工具類

  • 什麼時候自己建立工具類?
    • 如果一個功能經常要用到,我們建議把這個功能做成一個工具類,可以在不同的地方重用。
    • “獲得資料庫連線”操作,將在以後的增刪改查所有功能中都存在,可以封裝工具類JDBCUtils。提供獲取連線物件的方法,從而達到程式碼的重複利用。
  • 工具類包含的內容
    • 1) 可以把幾個字串定義成常量:使用者名稱,密碼,URL,驅動類
    • 2) 得到資料庫的連線:getConnection()
    • 3) 關閉所有開啟的資源:

程式碼示例

/**
* JDBC 工具類
*/
public class JDBCUtils {
  //1. 定義字串常量, 記錄獲取連線所需要的資訊
  public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
  public static final String URL = "jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8";
  public static final String USER = "root";
  public static final String PASSWORD = "123456";
  //2. 靜態程式碼塊, 隨著類的載入而載入
  static{
    try {
      //註冊驅動
      Class.forName(DRIVERNAME);
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
  }
  
//3.獲取連線的靜態方法   public static Connection getConnection(){     try {       //獲取連線物件       Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);       //返回連線物件       return connection;     } catch (SQLException e) {       e.printStackTrace();       return null;     }   }
  
//關閉資源的方法   public static void close(Connection con, Statement st){     if(con != null && st != null){       try {         st.close();         con.close();       } catch (SQLException e) {         e.printStackTrace();       }     }   }
  
public static void close(Connection con, Statement st, ResultSet rs){     if(rs != null){       try {         rs.close();       } catch (SQLException e) {         e.printStackTrace();       }     }     close(con,st);   } }

3.2 DML操作

3.2.1 插入記錄

解決插入中文亂碼問題.

jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8
characterEncoding=UTF-8 指定字元的編碼、解碼格式。

程式碼示例

/**
* 插入資料
* @throws SQLException
*/
@Test
public void testInsert() throws SQLException {
  //1.通過工具類獲取連線
  Connection connection = JDBCUtils.getConnection();
  //2.獲取Statement
  Statement statement = connection.createStatement();
  //2.1 編寫Sql
  String sql = "insert into jdbc_user values(null,'張百萬','123','2020/1/1')";
  //2.2 執行Sql
  int i = statement.executeUpdate(sql);
  System.out.println(i);
  //3.關閉流
  JDBCUtils.close(connection,statement);
}

3.2.2 更新記錄

  • 根據ID 需改使用者名稱稱
/**
* 修改 id 為1 的使用者名稱為 廣坤
*/
@Test
public void testUpdate() throws SQLException {
  Connection connection = JDBCUtils.getConnection();
  Statement statement = connection.createStatement();
  String sql = "update jdbc_user set username = '廣坤' where id = 1";
  statement.executeUpdate(sql);
  JDBCUtils.close(connection,statement);
}

3.2.3 刪除記錄

  • 刪除id為 3 和 4 的記錄
/**
* 刪除id 為 3 和 4的記錄
* @throws SQLException
*/
@Test
public void testDelete() throws SQLException {
  Connection connection = JDBCUtils.getConnection();
  Statement statement = connection.createStatement();
  statement.executeUpdate("delete from jdbc_user where id in(3,4)");
  JDBCUtils.close(connection,statement);
}

3.3 DQL操作

3.3.1 查詢姓名為張百萬的一條記錄

public class TestJDBC02 {
  public static void main(String[] args) throws SQLException {
    //1.獲取連線物件
    Connection connection = JDBCUtils.getConnection();
    //2.獲取Statement物件
    Statement statement = connection.createStatement();
    String sql = "SELECT * FROM jdbc_user WHERE username = '張百萬';";
    ResultSet resultSet = statement.executeQuery(sql);
    //3.處理結果集
    while(resultSet.next()){
      //通過列名 獲取欄位資訊
      int id = resultSet.getInt("id");
      String username = resultSet.getString("username");
      String password = resultSet.getString("password");
      String birthday = resultSet.getString("birthday");
      System.out.println(id+" "+username+" " + password +" " + birthday);
    }
    //4.釋放資源
    JDBCUtils.close(connection,statement,resultSet);
  }
}

4. SQL注入問題

4.1 Sql注入演示

1) 向jdbc_user表中 插入兩條資料

# 插入2條資料
INSERT INTO jdbc_user VALUES(NULL,'jack','123456','2020/2/24');
INSERT INTO jdbc_user VALUES(NULL,'tom','123456','2020/2/24');

2) SQL注入演示

# SQL注入演示
-- 填寫一個錯誤的密碼
SELECT * FROM jdbc_user WHERE username = 'tom' AND PASSWORD = '123' OR '1' = '1';

如果這是一個登陸操作,那麼使用者就登陸成功了.顯然這不是我們想要看到的結果

4.2 sql注入案例:使用者登陸

  • 需求
    • 使用者在控制檯上輸入使用者名稱和密碼, 然後使用 Statement 字串拼接的方式 實現使用者的登入。
  • 步驟
    • 1) 得到使用者從控制檯上輸入的使用者名稱和密碼來查詢資料庫
    • 2) 寫一個登入的方法
    • a) 通過工具類得到連線
    • b) 建立語句物件,使用拼接字串的方式生成 SQL 語句
    • c) 查詢資料庫,如果有記錄則表示登入成功,否則登入失敗
    • d) 釋放資源
  • Sql注入方式: 123' or '1'=’1

程式碼示例

public class TestLogin01 {
  /**
  * 使用者登入案例
  * 使用 Statement字串拼接的方式完成查詢
  * @param args
  */
  public static void main(String[] args) throws SQLException {
    //1.獲取連線
    Connection connection = JDBCUtils.getConnection();
    //2.獲取Statement
    Statement statement = connection.createStatement();
    //3.獲取使用者輸入的使用者名稱和密碼
    Scanner sc = new Scanner(System.in);
    System.out.println("請輸入使用者名稱: ");
    String name = sc.nextLine();
    System.out.println("請輸入密碼: ");
    String pass = sc.nextLine();
    System.out.println(pass);
    //4.拼接Sql,執行查詢
    String sql = "select * from jdbc_user " +
    "where username = " + " '" + name +"' " +" and password = " +" '" + pass +"'";
    System.out.println(sql);
    ResultSet resultSet = statement.executeQuery(sql);
    //5.處理結果集,判斷結果集是否為空
    if(resultSet.next()){
      System.out.println("登入成功! 歡迎您: " + name);
    }else {
      System.out.println("登入失敗!");
    }
    //釋放資源
    JDBCUtils.close(connection,statement,resultSet);
  }
}

4.3 問題分析

1) 什麼是SQL注入?

我們讓使用者輸入的密碼和 SQL 語句進行字串拼接。使用者輸入的內容作為了 SQL 語句語法的一部分,改變了 原有
SQL 真正的意義,以上問題稱為 SQL 注入 .

2) 如何實現的注入

  • 根據使用者輸入的資料,拼接處的字串
  • select * from jdbc_user where username = 'abc' and password = 'abc' or '1'='1'
    name='abc' and password='abc' 為假 '1'='1' 真
    相當於 select * from user where true=true; 查詢了所有記錄

3) 如何解決

要解決 SQL 注入就不能讓使用者輸入的密碼和我們的 SQL 語句進 行簡單的字串拼接。

5. 預處理物件

5.1 PreparedStatement 介面介紹

  • PreparedStatement 是 Statement 介面的子介面,繼承於父介面中所有的方法。它是一個預編譯的 SQL 語句物件.
  • 預編譯: 是指SQL 語句被預編譯,並存儲在 PreparedStatement 物件中。然後可以使用此物件多次高效地執行該語句。

5.2 PreparedStatement 特點

  • 因為有預先編譯的功能,提高 SQL 的執行效率。
  • 可以有效的防止 SQL 注入的問題,安全性更高

5.3 獲取PreparedStatement物件

  • 通過Connection建立PreparedStatement物件

5.4 PreparedStatement介面常用方法

5.5 使用PreparedStatement的步驟

1) 編寫 SQL 語句,未知內容使用?佔位:

"SELECT * FROM jdbc_user WHERE username=? AND password=?";

2) 獲得 PreparedStatement 物件 3) 設定實際引數:setXxx( 佔位符的位置, 真實的值) 4) 執行引數化 SQL 語句 5)
關閉資源

5.6 使用PreparedStatement完成登入案例

  • 使用 PreparedStatement 預處理物件,可以有效的避免SQL注入
  • 步驟:
    • 1.獲取資料庫連線物件
    • 2.編寫SQL 使用? 佔位符方式3.獲取預處理物件 (預編譯物件會將Sql傳送給資料庫 進
    • 行預編譯)
    • 4.提示使用者輸入使用者名稱 & 密碼
    • 5.設定實際引數:setXxx(佔位符的位置, 真實的值)
    • 6.執行查詢獲取結果集
    • 7.判斷是否查詢到資料
    • 8.關閉資源
public class TestLogin02 {
  /**
  * 使用預編譯物件 PrepareStatement 完成登入案例
  * @param args
  * @throws SQLException
  */
  public static void main(String[] args) throws SQLException {
    //1.獲取連線
    Connection connection = JDBCUtils.getConnection();
    //2.獲取Statement
    Statement statement = connection.createStatement();
    //3.獲取使用者輸入的使用者名稱和密碼
    Scanner sc = new Scanner(System.in);
    System.out.println("請輸入使用者名稱: ");
    String name = sc.nextLine();
    System.out.println("請輸入密碼: ");
    String pass = sc.nextLine();
    System.out.println(pass);
    //4.獲取 PrepareStatement 預編譯物件
    //4.1 編寫SQL 使用 ? 佔位符方式
    String sql = "select * from jdbc_user where username = ? and password = ?";
    PreparedStatement ps = connection.prepareStatement(sql);
    //4.2 設定佔位符引數
    ps.setString(1,name);
    ps.setString(2,pass);
    //5. 執行查詢 處理結果集
    ResultSet resultSet = ps.executeQuery();
    if(resultSet.next()){
      System.out.println("登入成功! 歡迎您: " + name);
    }else{
      System.out.println("登入失敗!");
    }
    //6.釋放資源
    JDBCUtils.close(connection,statement,resultSet);
  }
}

5.7 PreparedStatement的執行原理

  • 分別使用 Statement物件 和 PreparedStatement物件進行插入操作

程式碼示例

public class TestPS {
  public static void main(String[] args) throws SQLException {
    Connection con = JDBCUtils.getConnection();
    //獲取 Sql語句執行物件
    Statement st = con.createStatement();
    //插入兩條資料
    st.executeUpdate("insert into jdbc_user values(null,'張三','123','1992/12/26')");
    st.executeUpdate("insert into jdbc_user values(null,'李四','123','1992/12/26')");
    //獲取預處理物件
    PreparedStatement ps = con.prepareStatement("insert into jdbc_user values(?,?,?,?)");
    //第一條數 設定佔位符對應的引數
    ps.setString(1,null);
    ps.setString(2,"長海");
    ps.setString(3,"qwer");
    ps.setString(4,"1990/1/10");
    //執行插入
    ps.executeUpdate();
    //第二條資料
    ps.setString(1,null);
    ps.setString(2,"小斌");
    ps.setString(3,"1122");
    ps.setString(4,"1990/1/10");
    //執行插入
    ps.executeUpdate();
    //釋放資源
    st.close();
    ps.close();
    con.close();
  }
}

5.8 Statement 與 PreparedStatement的區別?

  • 1. Statement用於執行靜態SQL語句,在執行時,必須指定一個事先準備好的SQL語句。
  • 2. PrepareStatement是預編譯的SQL語句物件,語句中可以包含動態引數“?”,在執行時可以為“?”動態設定引數值。
  • 3. PrepareStatement可以減少編譯次數提高資料庫效能。

6. JDBC 控制事務

  • 之前我們是使用 MySQL 的命令來操作事務。接下來我們使用 JDBC 來操作銀行轉賬的事務。

6.1 資料準備

-- 建立賬戶表
CREATE TABLE account(
-- 主鍵
id INT PRIMARY KEY AUTO_INCREMENT,
-- 姓名
NAME VARCHAR(10),
-- 轉賬金額
money DOUBLE
);
-- 新增兩個使用者
INSERT INTO account (NAME, money) VALUES ('tom', 1000), ('jack', 1000);

6.2 事務相關API

我們使用 Connection中的方法實現事務管理

6.3 開發步驟

  • 1. 獲取連線
  • 2. 開啟事務
  • 3. 獲取到 PreparedStatement , 執行兩次更新操作
  • 4. 正常情況下提交事務
  • 5. 出現異常回滾事務
  • 6. 最後關閉資源

6.4 程式碼示例

public class JDBCTransaction {
  //JDBC 操作事務
  public static void main(String[] args) {
    Connection con = null;
    PreparedStatement ps = null;
    try {
      //1. 獲取連線
      con = JDBCUtils.getConnection();
      //2. 開啟事務
      con.setAutoCommit(false);
      //3. 獲取到 PreparedStatement 執行兩次更新操作
      //3.1 tom 賬戶 -500
      ps = con.prepareStatement("update account set money = money - ? where name = ? ");
      ps.setDouble(1,500.0);
      ps.setString(2,"tom");
      ps.executeUpdate();
      //模擬tom轉賬後 出現異常
      System.out.println(1 / 0);
      //3.2 jack 賬戶 +500
      ps = con.prepareStatement("update account set money = money + ? where name = ? ");
      ps.setDouble(1,500.0);
      ps.setString(2,"jack");
      ps.executeUpdate();
      //4. 正常情況下提交事務
      con.commit();
      System.out.println("轉賬成功!");
    } catch (SQLException e) {
      e.printStackTrace();
      try {
        //5. 出現異常回滾事務
        con.rollback();
      } catch (SQLException ex) {
        ex.printStackTrace();
      }
    } finally {
      //6. 最後關閉資源
      JDBCUtils.close(con,ps);
    }
  }
}