JDBC

來自拉鉤教育-JAVA就業(yè)集訓(xùn)營

1. JDBC 概述

1.1 客戶端操作數(shù)據(jù)庫的方式

  1. 方式1: 使用第三方客戶端來訪問 MySQL:SQLyog
image-20210227201534174.png
  1. 方式2: 使用命令行
image-20210227201545052.png
  1. 我們今天要學(xué)習(xí)的是通過 Java程序 來訪問 MySQL 數(shù)據(jù)庫

1.2 什么是JDBC

  • JDBC(Java Data Base Connectivity) 是 Java 訪問數(shù)據(jù)庫的標(biāo)準(zhǔn)規(guī)范.是一種用于執(zhí)行SQL語句的Java API,可以為多種關(guān)系數(shù)據(jù)庫提供統(tǒng)一訪問,它由一組用Java語言編寫的類和接口組成。是Java訪問數(shù)據(jù)庫的標(biāo)準(zhǔn)規(guī)范.

1.3 JDBC 原理

  • JDBC是接口,驅(qū)動(dòng)是接口的實(shí)現(xiàn),沒有驅(qū)動(dòng)將無法完成數(shù)據(jù)庫連接,從而不能操作數(shù)據(jù)庫!每個(gè)數(shù)據(jù)庫廠商都需
    要提供自己的驅(qū)動(dòng),用來連接自己公司的數(shù)據(jù)庫,也就是說驅(qū)動(dòng)一般都由數(shù)據(jù)庫生成廠商提供。
image-20210227201602505.png
  • 總結(jié):
    • JDBC就是由sun公司定義的一套操作所有關(guān)系型數(shù)據(jù)庫的規(guī)則(接口),而數(shù)據(jù)庫廠商需要實(shí)現(xiàn)這套接口,提供數(shù)據(jù)庫
      驅(qū)動(dòng)jar包, 我們可以使用這套接口編程,真正執(zhí)行的代碼是對應(yīng)驅(qū)動(dòng)包中的實(shí)現(xiàn)類。

2. JDBC 開發(fā)

2.1 數(shù)據(jù)準(zhǔn)備

-- 創(chuàng)建 jdbc_user表
CREATE TABLE jdbc_user (
    id INT PRIMARY KEY AUTO_INCREMENT ,
    username VARCHAR(50),
    PASSWORD VARCHAR(50),
    birthday DATE
);
-- 添加數(shù)據(jù)
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驅(qū)動(dòng)包

  1. 將MySQL驅(qū)動(dòng)包添加到j(luò)ar包庫文件夾中,Myjar文件夾,用于存放當(dāng)前項(xiàng)目需要的所有jar包
image-20210227210508051.png
    1. 在 idea中 配置jar包庫的位置
image-20210227201714935.png
    1. 創(chuàng)建一個(gè)新的項(xiàng)目jdbc_task01, 配置jar包庫
image-20210227201727559.png

2.3 API使用: 1.注冊驅(qū)動(dòng)

  • JDBC規(guī)范定義驅(qū)動(dòng)接口: java.sql.Driver
  • MySql驅(qū)動(dòng)包提供了實(shí)現(xiàn)類: com.mysql.jdbc.Driver
加載注冊驅(qū)動(dòng)的方式 描述
Class.forName(數(shù)據(jù)庫驅(qū)動(dòng)實(shí)現(xiàn)類) 加載和注冊數(shù)據(jù)庫驅(qū)動(dòng),數(shù)據(jù)庫驅(qū)動(dòng)由數(shù)據(jù)庫廠商MySql提供"com.mysql.jdbc.Driver"
  1. 代碼示例
public class JDBCDemo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.注冊驅(qū)動(dòng)
        // forName 方法執(zhí)行將類進(jìn)行初始化
        Class.forName("com.mysql.jdbc.Driver");
    }
}
  1. 為什么這樣可以注冊驅(qū)動(dòng)?
  • 我們知道 Class類的forName方法 ,可以將一個(gè)類初始化, 現(xiàn)在我們一起Driver類的 看一下源碼
// Driver類是MySql提供的數(shù)據(jù)庫驅(qū)動(dòng)類, 實(shí)現(xiàn)了JDBC的Driver接口 java.sql.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    // 空參構(gòu)造
    public Driver() throws SQLException {
    }
    //靜態(tài)代碼塊,Class類的 forName()方法將Driver類 加載到內(nèi)存, static代碼塊會(huì)自動(dòng)執(zhí)行
    static {
        try {
            /*
            DriverManager 驅(qū)動(dòng)管理類
            registerDriver(new Driver) 注冊驅(qū)動(dòng)的方法
            注冊數(shù)據(jù)庫驅(qū)動(dòng)
            */
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}
  • 注:

從 JDBC3 開始,目前已經(jīng)普遍使用的版本??梢圆挥米则?qū)動(dòng)而直接使用。 Class.forName 這句話可以省略。

2.4 API使用: 2.獲得連接

  • Connection 接口,代表一個(gè)連接對象 ,具體的實(shí)現(xiàn)類由數(shù)據(jù)庫的廠商實(shí)現(xiàn)
  • 使用 DriverManager類的靜態(tài)方法,getConnection可以獲取數(shù)據(jù)庫的連接
獲取連接的靜態(tài)方法 說明
Connection getConnection(String url, String user, String password) 通過連接字符串和用戶名,密碼來獲取數(shù)據(jù)庫連接對象
  1. getConnection方法 3個(gè) 連接參數(shù)說明
連接參數(shù) 說明
user 登錄用戶名
password 登錄密碼
url mySql URL的格式
jdbc:mysql://localhost:3306/db4
  1. 對URL的詳細(xì)說明
jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8
  • JDBC規(guī)定url的格式由三部分組成,每個(gè)部分中間使用冒號(hào)分隔。
    • 第一部分是協(xié)議 jdbc,這是固定的;
    • 第二部分是子協(xié)議,就是數(shù)據(jù)庫名稱,連接mysql數(shù)據(jù)庫,第二部分當(dāng)然是mysql了;
    • 第三部分是由數(shù)據(jù)庫廠商規(guī)定的,我們需要了解每個(gè)數(shù)據(jù)庫廠商的要求,mysql的第三部分分別由數(shù)據(jù)庫服務(wù)器的IP地址(localhost)、端口號(hào)(3306),以及要使用的 數(shù)據(jù)庫名稱 組成。
image-20210227201744190.png
  1. 代碼示例
public class JDBCDemo02 {
    public static void main(String[] args) throws Exception {
//1.注冊驅(qū)動(dòng)
        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.獲取語句執(zhí)行平臺(tái)

  • 通過Connection 的 createStatement方法 獲取sql語句執(zhí)行對象
Connection接口中的方法 說明
Statement createStatement() 創(chuàng)建 SQL語句執(zhí)行對象
  • Statement : 代表一條語句對象,用于發(fā)送 SQL 語句給服務(wù)器,用于執(zhí)行靜態(tài) SQL 語句并返回它所生成結(jié)果的對象。
Statement類 常用方法 說明
int executeUpdate(String sql); 執(zhí)行insert update delete語句.返回int類型,代表受影響的行數(shù)
ResultSet executeQuery(String sql); 執(zhí)行select語句, 返回ResultSet結(jié)果集對象
  • 代碼示例
public class JDBCDemo03 {
    public static void main(String[] args) throws Exception {
//1.注冊驅(qū)動(dòng)
        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.執(zhí)行創(chuàng)建表操作
        String sql = "create table test01(id int, name varchar(20),age int);";
//5.增刪改操作 使用executeUpdate,增加一張表
        int i = statement.executeUpdate(sql);
//6.返回值是受影響的函數(shù)
        System.out.println(i);
//7.關(guān)閉流
        statement.close();
        con.close();
    }
}

2.6 API 使用: 4.處理結(jié)果集

  • 只有在進(jìn)行查詢操作的時(shí)候, 才會(huì)處理結(jié)果集
  • 代碼示例
public class JDBCDemo04 {
    public static void main(String[] args) throws SQLException {
//1.注冊驅(qū)動(dòng) 可以省略
//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";
//執(zhí)行查詢操作,返回的是一個(gè) ResultSet 結(jié)果對象
        ResultSet resultSet = statement.executeQuery(sql);
//4.處理結(jié)果集 resultSet
    }
}

2.6.1 ResultSet接口

  • 作用:封裝數(shù)據(jù)庫查詢的結(jié)果集,對結(jié)果集進(jìn)行遍歷,取出每一條記錄。
ResultSet接口方法 說明
boolean next() 1) 游標(biāo)向下一行
2) 返回 boolean 類型,如果還有下一條記錄,返回 true,否則返回 false
xxx getXxx( String or int) 1) 通過列名,參數(shù)是 String 類型。返回不同的類型
2) 通過列號(hào),參數(shù)是整數(shù),從 1 開始。返回不同的類型
image-20210227201804432.png
  • 代碼示例
public class JDBCDemo04 {
    public static void main(String[] args) throws SQLException {
        //1.注冊驅(qū)動(dòng) 可以省略
        //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";
        //執(zhí)行查詢操作,返回的是一個(gè) ResultSet 結(jié)果對象
        ResultSet resultSet = statement.executeQuery(sql);
        //4.處理結(jié)果集
        // //next 方法判斷是否還有下一條數(shù)據(jù)
        // boolean next = resultSet.next();
        // System.out.println(next);
        //
        // //getXXX 方法獲取數(shù)據(jù) 兩種方式
        // int id = resultSet.getInt("id");//列名
        // System.out.println(id);
        //
        // int anInt = resultSet.getInt(1);//列號(hào)
        // System.out.println(anInt);
        //使用while循環(huán)
        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);
        }
        //關(guān)閉連接
        resultSet.close();
        statement.close();
        con.close();
    }
}

2.7 API 使用: 5.釋放資源

  1. 需要釋放的對象:ResultSet 結(jié)果集,Statement 語句,Connection 連接
  2. 釋放原則:先開的后關(guān),后開的先關(guān)。ResultSet ==> Statement ==> Connection
  3. 放在哪個(gè)代碼塊中:finally 塊
  • 與IO流一樣,使用后的東西都需要關(guān)閉!關(guān)閉的順序是先開后關(guān), 先得到的后關(guān)閉,后得到的先關(guān)閉
image-20210227201821591.png
  • 代碼示例
public class JDBCDemo05 {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            //1.注冊驅(qū)動(dòng)(省略)
            //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
         * 關(guān)閉順序: resultSet ==> statement ==> connection
         */
            try {
                connection.close();
                resultSet.close();
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

2.8 步驟總結(jié)

  1. 獲取驅(qū)動(dòng)(可以省略)
  2. 獲取連接
  3. 獲取Statement對象
  4. 處理結(jié)果集(只在查詢時(shí)處理)
  5. 釋放資源

3. JDBC實(shí)現(xiàn)增刪改查

3.1 JDBC工具類

  • 什么時(shí)候自己創(chuàng)建工具類?

    • 如果一個(gè)功能經(jīng)常要用到,我們建議把這個(gè)功能做成一個(gè)工具類,可以在不同的地方重用。
    • “獲得數(shù)據(jù)庫連接”操作,將在以后的增刪改查所有功能中都存在,可以封裝工具類JDBCUtils。提供獲取連接對象的方法,從而達(dá)到代碼的重復(fù)利用。
  • 工具類包含的內(nèi)容

    1. 可以把幾個(gè)字符串定義成常量:用戶名,密碼,URL,驅(qū)動(dòng)類
    2. 得到數(shù)據(jù)庫的連接:getConnection()
    3. 關(guān)閉所有打開的資源:
  • 代碼示例

/**
 * 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. 靜態(tài)代碼塊, 隨著類的加載而加載
    static{
        try {
        //注冊驅(qū)動(dòng)
            Class.forName(DRIVERNAME);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    //3.獲取連接的靜態(tài)方法
    public static Connection getConnection(){
        try {
        //獲取連接對象
            Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
        //返回連接對象
            return connection;
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }
    //關(guān)閉資源的方法
    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 指定字符的編碼、解碼格式。
  • 代碼示例
/**
 * 插入數(shù)據(jù)
 * @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 執(zhí)行Sql
    int i = statement.executeUpdate(sql);
    System.out.println(i);
    //3.關(guān)閉流
    JDBCUtils.close(connection,statement);
}

3.2.2 更新記錄

  • 根據(jù)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.處理結(jié)果集
        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表中 插入兩條數(shù)據(jù)

  2. SQL注入演示

# SQL注入演示
-- 填寫一個(gè)錯(cuò)誤的密碼
SELECT * FROM jdbc_user WHERE username = 'tom' AND PASSWORD = '123' OR '1' = '1';
  • 如果這是一個(gè)登陸操作,那么用戶就登陸成功了.顯然這不是我們想要看到的結(jié)果

4.2 sql注入案例:用戶登陸

  • 需求
    • 用戶在控制臺(tái)上輸入用戶名和密碼, 然后使用 Statement 字符串拼接的方式 實(shí)現(xiàn)用戶的登錄。
  • 步驟
  1. 得到用戶從控制臺(tái)上輸入的用戶名和密碼來查詢數(shù)據(jù)庫

  2. 寫一個(gè)登錄的方法
    a) 通過工具類得到連接
    b) 創(chuàng)建語句對象,使用拼接字符串的方式生成 SQL 語句
    c) 查詢數(shù)據(jù)庫,如果有記錄則表示登錄成功,否則登錄失敗
    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,執(zhí)行查詢
        String sql = "select * from jdbc_user " +
                "where username = " + " '" + name +"' " +" and password = " +" '" + pass +"'";
        System.out.println(sql);
        ResultSet resultSet = statement.executeQuery(sql);
        //5.處理結(jié)果集,判斷結(jié)果集是否為空
        if(resultSet.next()){
            System.out.println("登錄成功! 歡迎您: " + name);
        }else {
            System.out.println("登錄失敗!");
        }
        //釋放資源
        JDBCUtils.close(connection,statement,resultSet);
    }
}

4.3 問題分析

  1. 什么是SQL注入?
  • 我們讓用戶輸入的密碼和 SQL 語句進(jìn)行字符串拼接。用戶輸入的內(nèi)容作為了 SQL 語句語法的一部分,改變了 原有SQL 真正的意義,以上問題稱為 SQL 注入 .
  1. 如何實(shí)現(xiàn)的注入
  • 根據(jù)用戶輸入的數(shù)據(jù),拼接處的字符串
select * from jdbc_user where username = 'abc' and password = 'abc' or '1'='1'
name='abc' and password='abc' 為假 '1'='1' 真
相當(dāng)于 select * from user where true=true; 查詢了所有記錄
image-20210227201857034.png
  1. 如何解決
  • 要解決 SQL 注入就不能讓用戶輸入的密碼和我們的 SQL 語句進(jìn) 行簡單的字符串拼接。

5. 預(yù)處理對象

5.1 PreparedStatement 接口介紹

  • PreparedStatement 是 Statement 接口的子接口,繼承于父接口中所有的方法。它是一個(gè)預(yù)編譯的 SQL 語句對象.
  • 預(yù)編譯: 是指SQL 語句被預(yù)編譯,并存儲(chǔ)在 PreparedStatement 對象中。然后可以使用此對象多次高效地執(zhí)行該語句。

5.2 PreparedStatement 特點(diǎn)

  • 因?yàn)橛蓄A(yù)先編譯的功能,提高 SQL 的執(zhí)行效率。
  • 可以有效的防止 SQL 注入的問題,安全性更高

5.3 獲取PreparedStatement對象

  • 通過Connection創(chuàng)建PreparedStatement對象
Connection 接口中的方法 說明
PreparedStatement prepareStatement(String sql) 指定預(yù)編譯的 SQL 語句,
SQL 語句中使用占位符 ? 創(chuàng)建一個(gè)語句對象

5.4 PreparedStatement接口常用方法

常用方法 說明
int executeUpdate(); 執(zhí)行insert update delete語句.
ResultSet executeQuery(); 執(zhí)行select語句. 返回結(jié)果集對象 Resulet

5.5 使用PreparedStatement的步驟

  1. 編寫 SQL 語句,未知內(nèi)容使用?占位:
"SELECT * FROM jdbc_user WHERE username=? AND password=?";
  1. 獲得 PreparedStatement 對象
  2. 設(shè)置實(shí)際參數(shù):setXxx( 占位符的位置, 真實(shí)的值)
  3. 執(zhí)行參數(shù)化 SQL 語句
  4. 關(guān)閉資源
setXxx重載方法 說明
void setDouble(int parameterIndex, double x) 將指定參數(shù)設(shè)置為給定 Java double 值。
void setInt(int parameterIndex, int x) 將指定參數(shù)設(shè)置為給定 Java int 值。
void setString(int parameterIndex, String x) 將指定參數(shù)設(shè)置為給定 Java String 值。
void setObject(int parameterIndex, Object x) 使用給定對象設(shè)置指定參數(shù)的值。

5.6 使用PreparedStatement完成登錄案例

  • 使用 PreparedStatement 預(yù)處理對象,可以有效的避免SQL注入
image-20210227201914874.png
  • 步驟:
    1.獲取數(shù)據(jù)庫連接對象
    2.編寫SQL 使用? 占位符方式
    3.獲取預(yù)處理對象 (預(yù)編譯對象會(huì)將Sql發(fā)送給數(shù)據(jù)庫 進(jìn)行預(yù)編譯)
    4.提示用戶輸入用戶名 & 密碼
    5.設(shè)置實(shí)際參數(shù):setXxx(占位符的位置, 真實(shí)的值)
    6.執(zhí)行查詢獲取結(jié)果集
    7.判斷是否查詢到數(shù)據(jù) 8.關(guān)閉資源
public class TestLogin02 {
    /**
     * 使用預(yù)編譯對象 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 預(yù)編譯對象
        //4.1 編寫SQL 使用 ? 占位符方式
        String sql = "select * from jdbc_user where username = ? and password = ?";
        PreparedStatement ps = connection.prepareStatement(sql);
        //4.2 設(shè)置占位符參數(shù)
        ps.setString(1,name);
        ps.setString(2,pass);
        //5. 執(zhí)行查詢 處理結(jié)果集
        ResultSet resultSet = ps.executeQuery();
        if(resultSet.next()){
            System.out.println("登錄成功! 歡迎您: " + name);
        }else{
            System.out.println("登錄失敗!");
        }
        //6.釋放資源
        JDBCUtils.close(connection,statement,resultSet);
    }
}

5.7 PreparedStatement的執(zhí)行原理

  • 分別使用 Statement對象 和 PreparedStatement對象進(jìn)行插入操作
  • 代碼示例
public class TestPS {
    public static void main(String[] args) throws SQLException {
        Connection con = JDBCUtils.getConnection();
        //獲取 Sql語句執(zhí)行對象
        Statement st = con.createStatement();
        //插入兩條數(shù)據(jù)
        st.executeUpdate("insert into jdbc_user values(null,'張三','123','1992/12/26')");
        st.executeUpdate("insert into jdbc_user values(null,'李四','123','1992/12/26')");
        //獲取預(yù)處理對象
        PreparedStatement ps = con.prepareStatement("insert into jdbc_user values(?,?,?,?)");
        //第一條數(shù) 設(shè)置占位符對應(yīng)的參數(shù)
        ps.setString(1, null);
        ps.setString(2, "長海");
        ps.setString(3, "qwer");
        ps.setString(4, "1990/1/10");
        //執(zhí)行插入
        ps.executeUpdate();
        //第二條數(shù)據(jù)
        ps.setString(1, null);
        ps.setString(2, "小斌");
        ps.setString(3, "1122");
        ps.setString(4, "1990/1/10");
        //執(zhí)行插入
        ps.executeUpdate();
        //釋放資源
        st.close();
        ps.close();
        con.close();
    }
}
image-20210227201932158.png

5.8 Statement 與 PreparedStatement的區(qū)別?

  1. Statement用于執(zhí)行靜態(tài)SQL語句,在執(zhí)行時(shí),必須指定一個(gè)事先準(zhǔn)備好的SQL語句。
  2. PrepareStatement是預(yù)編譯的SQL語句對象,語句中可以包含動(dòng)態(tài)參數(shù)“?”,在執(zhí)行時(shí)可以為“?”動(dòng)態(tài)設(shè)置參數(shù)
    值。
  3. PrepareStatement可以減少編譯次數(shù)提高數(shù)據(jù)庫性能。

6. JDBC 控制事務(wù)

  • 之前我們是使用 MySQL 的命令來操作事務(wù)。接下來我們使用 JDBC 來操作銀行轉(zhuǎn)賬的事務(wù)。

6.1 數(shù)據(jù)準(zhǔn)備

-- 創(chuàng)建賬戶表
CREATE TABLE account(
    -- 主鍵
    id INT PRIMARY KEY AUTO_INCREMENT,
    -- 姓名
    NAME VARCHAR(10),
    -- 轉(zhuǎn)賬金額
    money DOUBLE
);
-- 添加兩個(gè)用戶
INSERT INTO account (NAME, money) VALUES ('tom', 1000), ('jack', 1000);

6.2 事務(wù)相關(guān)API

  • 我們使用 Connection中的方法實(shí)現(xiàn)事務(wù)管理
方法 說明
void setAutoCommit(boolean
autoCommit)
參數(shù)是 true 或 false 如果設(shè)置為 false,表示關(guān)閉自動(dòng)提交,相當(dāng)于開啟事務(wù)
void commit() 提交事務(wù)
void rollback() 回滾事務(wù)

6.3 開發(fā)步驟

  1. 獲取連接
  2. 開啟事務(wù)
  3. 獲取到 PreparedStatement , 執(zhí)行兩次更新操作
  4. 正常情況下提交事務(wù)
  5. 出現(xiàn)異常回滾事務(wù)
  6. 最后關(guān)閉資源

6.4 代碼示例

public class JDBCTransaction {
    //JDBC 操作事務(wù)
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement ps = null;
        try {
//1. 獲取連接
            con = JDBCUtils.getConnection();
//2. 開啟事務(wù)
            con.setAutoCommit(false);
//3. 獲取到 PreparedStatement 執(zhí)行兩次更新操作
//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轉(zhuǎn)賬后 出現(xiàn)異常
            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. 正常情況下提交事務(wù)
            con.commit();
            System.out.println("轉(zhuǎn)賬成功!");
        } catch (SQLException e) {
            e.printStackTrace();
            try {
//5. 出現(xiàn)異?;貪L事務(wù)
                con.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
//6. 最后關(guān)閉資源
            JDBCUtils.close(con,ps);
        }
    }
}

來自拉鉤教育-JAVA就業(yè)集訓(xùn)營

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容