JDBC 簡述與基礎語法


JDBC

  • JDBC(Java DataBase Connectivity,Java 數據庫連接):通過Java 語言操作數據庫。
  • JDBC 本質是官方(Sun 公司)定義的一套操作所有關係型數據庫的規則(接口)。各個數據庫廠商去實現這套接口,提供數據庫驅動(Jar 包)。可以使用這套接口(JDBC)編程,運行時的代碼其實是驅動 Jar 包中的實現類。
    在這裏插入圖片描述

1. 快速入門

  • 通過 Java 代碼向數據庫 User 表插入一條記錄。

a. 準備數據庫

  • 準備數據庫和表
-- 創建庫
CREATE DATABASE regino;
USE regino;

-- 創建用戶表
CREATE TABLE USER(
  id INT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(50),
  PASSWORD VARCHAR(50)
);
INSERT INTO USER(username, PASSWORD)VALUES('admin','123'),('tom','123'),('jack','123');

在這裏插入圖片描述

b. 導入 Jar 包

  • 創建 Java 工程,導入 MySQL 驅動 Jar 包
    在這裏插入圖片描述

c. 基礎語法

  1. 註冊驅動
    確認 Java 平臺操作的數據庫是 MySQL
  2. 建立連接
    Java 平臺與數據庫通信的會話通道
  3. 編寫 SQL
    String sql = ...
  4. 獲取 SQL 執行對象
  5. 執行 SQL 並返回結果
  6. 處理結果
  7. 釋放資源

d. 示例(重點)

package com.company;

import com.mysql.jdbc.Driver;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class JDBCTest {
    public static void main(String[] args) throws Exception {
        // 1.註冊驅動
        DriverManager.registerDriver(new Driver());
        // 2.建立連接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/regino", "root", "root");
        // 3.編寫sql(在Java編寫sql 結尾;可以省略)
        String sql = "insert into user values(null,'regino','666')";
        // 4.獲取sql執行對象
        Statement statement = connection.createStatement();
        // 5.執行sql並返回結果
        int i = statement.executeUpdate(sql);
        // 6.處理結果
        if (i > 0) {
            System.out.println("添加成功");
        } else {
            System.out.println("添加失敗");
        }
        // 7.釋放資源
        statement.close();
        connection.close();
    }
}

在這裏插入圖片描述

  • MySQL 的默認端口是 3306,可以在 my.ini 配置文件中修改。

2. API 詳解

  • 相關 API 都是由 Sun 公司提供的,在 java.sql 包下。

a. DriverManager 類

  • DriverManager 是驅動管理對象。提供了以下兩個功能:

i. 註冊驅動

  • 有以下幾種方式:
① 新建 Driver 對象(不推薦)
  • static void registerDriver(Driver driver)
  • 通過查看 MySQL Driver 實現類的源碼發現內部的靜態代碼已經提供了註冊驅動功能,相當於註冊了兩次。
    在這裏插入圖片描述
② 通過反射加載到 JVM(推薦)
  • Class.forName("com.mysql.jdbc.Driver");
  • 類加載後就會觸發初始化操作。
③ 省略(MySQL 5.5 以上支持)
  • SPI 服務提供接口(Service Provider Interface):可以完成隱式註冊。
  • 執行加載驅動的語句後,靜態代碼塊的代碼中就出現了 DriverManager。
  • 早期的 DriverManager 不知道從哪裏去尋找驅動類,所以需要手動註冊;從 MySQL 5.0 開始 DriverManager 語句就可以省略了。該語句雖然可以不寫,但是建議還是寫上,符合市場的習慣。

ii. 建立連接

  • static Connection getConnection(String url, String user, String password)
    • 參數說明:
      • url:連接指定數據庫地址,用到的是 JDBC 協議。
        • MySQL 格式:jdbc:mysql://IP地址:端口/數據庫名
          實例(默認端口號 3306):jdbc:mysql://localhost:3306/reginojdbc:mysql:///regino(鏈接的是本地的數據庫且端口號沒有被修改)
      • user:用戶名
      • password:密碼

b. Connection 類

  • Connection 是數據庫連接對象。提供了以下兩個功能:

i. 獲取 SQL 執行對象

  • Statement createStatement()
    有缺陷
  • PreparedStatement prepareStatement(String sql)
    PreparedStatement 是 Statement 的子接口,性能和安全性有所提升。詳見:JDBC 的連接池
  • 一個事務中可以創建多個 statement,但是多個用戶(即連接)用不了一個事務。

ii. 事務管理

  • 執行完畢提交事務,出現異常回滾事務,提交或者回滾都是結束事務。詳見後文中的事務操作案例。
① 關閉自動提交(開啓事務)
  • void setAutoCommit(boolean autoCommit)
    • 參數:
      • true:自動提交(默認值)
      • false:手動提交
② 提交事務
  • void commit()
③ 回滾事務
  • void rollback()

> 補充:關閉連接的作用

  • IO 流關閉資源的目的就是釋放文件資源,因爲文件資源是有限的。
  • JDBC 的 close 方法的作用就是釋放連接 Connection 對象。因爲 Connection 代表 Java 程序與 MySQL 數據庫的連接,這種連接數是有限制的,默認情況 MySQL 最高的併發 Connection 數量是 100,所以用完畢之後一定要釋放資源。

c. Statement 類

  • Statement 是執行 SQL 的對象。

i. 執行所有類型 SQL 語句

  • boolean execute(String sql)
  • 返回值:如果執行的 SQL 語句是查詢語句則返回 true,其他語句一律是 false,所以該返回值沒有任何的意義

ii. 僅執行 DML 類型 SQL 語句(常用)

  • int executeUpdate(String sql)
  • 參數:DML 類型 SQL(即增刪改 insert、update、delete)
  • 返回影響行數

iii. 僅執行 DQL 類型 SQL 語句(常用)

  • ResultSet executeQuery(String sql)
  • 參數:DQL 類型 SQL(即 select 結果)
  • 返回結果集

d. ResultSet 類

  • ResultSet 是結果集對象,封裝查詢結果,這個結果集會把本次 SQL 查詢的數據全部加載到 ResultSet中。
    在這裏插入圖片描述

i. 指針下移

  • boolean next():遊標向下移動一個單位,並且返回是否還有數據,如果遊標沒法向下移動代表已經是數據末尾。相當於迭代器,遍歷時使用循環:while(resultSet.next()){}
  • 返回值:
    • true:表示此行有數據
    • false:表示此行沒有數據

ii. 獲取數據

  • T getXxx(int 列編號)(不常用,需要查表才知道對應的列名是什麼)
  • T getXxx(String 列名)(常用)
  • 補充:獲取所有類型
    • Object getObject(String 列名)
    • String getString(String 列名)

> 補充:用 ResultSet 遍歷時的常見錯誤

  • 遊標默認指向第一行(即列名),沒有數據無法獲取。錯誤示例如下:
    @Test
    public void testResultSet() throws Exception {
        //2. 獲取連接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///regino", "root", "root");
        //3. 創建sql的運輸器
        Statement st = connection.createStatement();
        //4. 準備sql語句 , 執行sql
        String sql = "select * from user";

        // resultSet是一個結果集,這個結果集會把本次sql查詢的數據全部加載到ResultSet中。
        ResultSet rs = st.executeQuery(sql);

        // 下一句會報錯:java.sql.SQLException: Before start of result set
        System.out.println("編號:" + rs.getInt("id") + "用戶名:" + rs.getString("username") + rs.getString("password"));

        //next() 遊標向下移動一個單位,並且返回是否還有數據,如果遊標沒法向下移動代表已經是數據末尾。
        while (rs.next()) {
            System.out.println("編號:" + rs.getInt("id") + "用戶名:" + rs.getString("username") + rs.getString("password"));
        }
        //5. 關閉資源
        st.close();
        connection.close();
    }

在這裏插入圖片描述
在這裏插入圖片描述

  • 用 next() 方法可以讓遊標向下移動一個單位,並且返回是否還有數據,如果遊標沒法向下移動,則代表已經是數據末尾。正確示例如下:
    @Test
    public void testResultSet() throws Exception {
        //2. 獲取連接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///regino", "root", "root");
        //3. 創建sql的運輸器
        Statement st = connection.createStatement();
        //4. 準備sql語句 , 執行sql
        String sql = "select * from user";

        // resultSet是一個結果集,這個結果集會把本次sql查詢的數據全部加載到ResultSet中。
        ResultSet rs = st.executeQuery(sql);

        //next() 遊標向下移動一個單位,並且返回是否還有數據,如果遊標沒法向下移動代表已經是數據末尾。
        while (rs.next()) {
            System.out.println("編號:" + rs.getInt("id") + "用戶名:" + rs.getString("username") + rs.getString("password"));
        }
        //5. 關閉資源
        st.close();
        connection.close();
    }
  • ResultSet 結果集的數據與查詢條件 select * from 表名 獲得的數據一致,得到所有的字段,就取所有的字段;得到具體的字段,就只能取某個具體的數據是的。例如,select name,age from 表名,ResultSet 結果集只有 name 與 age 兩個字段的數據。

3. CRUD 示例

  1. 註冊驅動
  2. 建立連接
  3. 編寫 SQL
  4. 獲取 SQL 執行對象
  5. 執行 SQL 並返回結果
  6. 處理結果
  7. 釋放資源

a. 創建記錄

  • 向 user 表 添加一條記錄
	// 新增,見配置中的實例
	@Test
	public void testInsert() throws Exception {
        DriverManager.registerDriver(new Driver());
        // 1.註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        // 2.建立連接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/regino", "root", "root");
        // 3.編寫sql(在Java編寫sql,結尾的;可以省略)
        String sql = "insert into user values(null,'regino','666')";
        // 4.獲取sql執行對象
        Statement statement = connection.createStatement();
        // 5.執行sql並返回結果
        int i = statement.executeUpdate(sql);
        // 6.處理結果
        if (i > 0) {
            System.out.println("添加成功");
        } else {
            System.out.println("添加失敗");
        }
        // 7.釋放資源
        statement.close();
        connection.close();
    }

b. 修改記錄

  • 向 user 表 修改一條記錄
    // 修改
    @Test
    public void testUpdate() throws Exception {
        // 1.註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        // 2.建立連接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/regino", "root", "root");
        // 3.編寫sql
        String sql = "update user set username = '勒布朗' where id = 5";
        // 4.獲取sql執行對象
        Statement statement = connection.createStatement();
        // 5.執行sql並返回結果
        int i = statement.executeUpdate(sql);
        // 6.處理結果
        if (i > 0) {
            System.out.println("修改成功");
        } else {
            System.out.println("修改失敗");
        }
        // 7.釋放資源
        statement.close();
        connection.close();
    }

c. 刪除記錄

  • 向 user 表刪除一條記錄
    // 刪除
    @Test
    public void testDelete() throws Exception {
        // 1.註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        // 2.建立連接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/regino", "root", "root");
        // 3.編寫sql
        String sql = "delete from user where id = 7";
        // 4.獲取sql執行對象
        Statement statement = connection.createStatement();
        // 5.執行sql並返回結果
        int i = statement.executeUpdate(sql);
        // 6.處理結果
        if (i > 0) {
            System.out.println("刪除成功");
        } else {
            System.out.println("刪除失敗");
        }
        // 7.釋放資源
        statement.close();
        connection.close();
    }

d. 查詢記錄

  • 向 user 表查詢所有記錄
    // 查詢
    @Test
    public void testFindAll() throws Exception {
        // 1.註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        // 2.建立連接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/regino", "root", "root");
        // 3.編寫sql
        String sql = "select * from user";
        // 4.獲取sql執行對象
        Statement statement = connection.createStatement();
        // 5.執行sql並返回結果
        ResultSet resultSet = statement.executeQuery(sql);
        // 6.處理結果
        while (resultSet.next()) {
            // 獲取數據
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("password");
            System.out.println("編號:" + id + "  用戶名:" + username + "  密碼:" + password);
        }
        // 7.釋放資源
        resultSet.close();
        statement.close();
        connection.close();
    }
  • JDBC 可以直接創建庫或表,但是一般只是用 JDBC 來操作數據庫的數據,即增刪改查。

4. 工具類 JdbcUtils

a. 目的

  • 通過上面案例需求,發現每次去執行 SQL 語句都需要註冊驅動,獲取連接,得到 Statement,以及釋放資源。經過了很多重複的勞動,爲了簡化書寫,一勞永逸,可以將重複的代碼定義到一個工具類中。

b. 步驟分析

public class JdbcUtils{
    // 1.註冊驅動(保證一次)
    static{
        
    }
    
    // 2.提供獲取連接的靜態方法
    public static Connection getConnection(){
        return null;
    }
    
    // 3.提供釋放資源的方法
    public void close(){
        
    }  
}

c. 普通方案

import java.sql.*;

public class JdbcUtils {
    // 1.註冊驅動
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            // e.printStackTrace();
            throw new RuntimeException("加載MySQL驅動失敗");
        }
    }

    // 2.提供獲取連接的靜態方法
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/regino", "root", "root");
    }

    // 3.提供釋放資源的方法
    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        //傳的值如果是null就會有NPE
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

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

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

    // 重載關閉方法
    public static void close(Statement statement, Connection connection) {
        close(null, statement, connection);
    }
}

> 補充:Connection 導包問題

  • 注意導入的是 JDK 中的 Connection:(用 MySQL 的 Connection 再強轉後也可以使用,但是如果更換數據庫就要改源碼)
    在這裏插入圖片描述
  • 確認 Connection、Statement 都是 java.sql 包下的。

d. 升級方案

i. 抽取配置文件

  • Java 代碼與數據庫配置信息產生了耦合,將可能會修改的參數抽取到配置文件 properties 中。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/regino
jdbc.user=root
jdbc.password=root

ii. 編寫工具類

import java.sql.*;
import java.util.ResourceBundle;

public class JdbcUtils {
    // 聲明變量
    private static String driver = null;
    private static String url = null;
    private static String user = null;
    private static String password = null;

    // 加載jdbc.properties配置文件,初始化變量
    static {
        // new Properties().load();
        // sun公司專門提供了一從src目錄下加載properties類型的工具類 ResourceBundle
        ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
        driver = jdbc.getString("jdbc.driver");
        url = jdbc.getString("jdbc.url");
        user = jdbc.getString("jdbc.user");
        password = jdbc.getString("jdbc.password");
    }

    // 1.註冊驅動
    static {
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            // e.printStackTrace();
            throw new RuntimeException("加載MySQL驅動失敗");
        }
    }

    // 2.提供獲取連接的靜態方法
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, user, password);
    }

    // 3.提供釋放資源的方法
    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

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

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

    // 重載關閉方法
    public static void close(Statement statement, Connection connection) {
        close(null, statement, connection);
    }
}
> 補充:判斷非空的作用
  • 關閉資源一般放到 try…catch 中的 finally 塊中,Connection 和 Statement 都要在 try…catch 塊外初始化爲 null。如果配置文件 jdbc.properties 中出現錯誤,執行 connection = JdbcUitls.getConnection(); 時就會出現異常,然後直接跳到 catch 塊中,所以需要在 finally 塊中關閉資源,因爲不管怎麼樣都會執行 finally 塊中的代碼。
  • 示例如下:
    @Test
    public void whyNull() throws Exception {
        Connection connection = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            //2. 獲取連接
            connection = JdbcUtils.getConnection();  //  執行該語句的時候就會出現異常,
            //3. 創建sql的運輸器
            st = connection.createStatement();
            //4. 準備sql語句 , 執行sql
            String sql = "select name,balance from account";

            // resultSet是一個結果集,這個結果集會把本次sql查詢的數據全部加載到ResultSet中。
            rs = st.executeQuery(sql);

            //next() 遊標向下移動一個單位,並且返回是否還有數據,如果遊標沒法向下移動代表已經是數據末尾。
            if (rs.next()) {
                System.out.println("姓名:" + rs.getString("name") + " 餘額:" + rs.getDouble("balance"));
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //出現異常的時候資源還是還關閉,沒因爲在finally塊中
            JdbcUtils.close(rs, st, connection);
        }
    }

e. 測試 JdbcTest

import org.junit.Test;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class JdbcTest {
    @Test
    public void findById() throws Exception {
        Connection connection = JdbcUtils.getConnection();
        String sql = "select * from user where id =1";
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery(sql);
        while (resultSet.next()) {
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("password");
            System.out.println("編號:" + id + "  用戶名:" + username + "  密碼:" + password);
        }
        JdbcUtils.close(resultSet, statement, connection);
    }
}

在這裏插入圖片描述

5. 事務操作案例

  • 事務:如果一個包含多個步驟的業務操作,被事務管理,那麼這些操作要麼同時成功,要麼同時失敗。
  • MySQL 操作:
    1. 開啓事務
      begin | start transaction;
    2. 提交事務
      commit;
    3. 回顧事務
      rollback;
  • Java 操作(使用 Connection 對象)
    1. 關閉自動提交(開啓事務)
      void setAutoCommit(false);
    2. 提交事務
      void commit();
    3. 回顧事務
      void rollback();

a. 主要需求

  • 通過 Java 代碼實現轉賬案例

b. 導入賬戶表

-- 創建庫
create database regino;

-- 使用庫
use regino;

-- 創建數據表
CREATE TABLE account (  -- 賬戶表
	id INT PRIMARY KEY AUTO_INCREMENT,
	`name` VARCHAR(32),
	money DOUBLE 
);

-- 添加數據
INSERT INTO account (`name`, money) VALUES ('小明', 1000), ('花花', 1000);

在這裏插入圖片描述

c. 編寫轉賬代碼(重點)

  • TX 是 transaction 的簡寫
import org.junit.Test;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

public class TXTest {

    @Test
    public void testTX() {
        Connection connection = null;
        Statement statement = null;
        try {
            // 1.獲取連接(JdbcUtils工具類)
            connection = JdbcUtils.getConnection();
            // 2.開啓事務
            connection.setAutoCommit(false);
            statement = connection.createStatement();

            // 3.花花扣錢
            String xiangSql = "update account set money = money - 100 where id = 2";
            int xiangResult = statement.executeUpdate(xiangSql);
            if (xiangResult > 0) {
                System.out.println("花花支付成功");
            }
            // 4.小明加錢
            String dieSql = "update account set money = money + 100 where id = 1";
            int dieResult = statement.executeUpdate(dieSql);
            if (dieResult > 0) {
                System.out.println("小明收款成功");
            }
            // 5.提交事務
            connection.commit();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                // 6.回滾事務
                connection.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        } finally {
            // 7.釋放資源
            JdbcUtils.close(statement, connection);
        }
    }
}

在這裏插入圖片描述
在這裏插入圖片描述

> 補充:故障測試

import org.junit.Test;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

public class TXTest {

    @Test
    public void testTX() {
        Connection connection = null;
        Statement statement = null;
        try {
            // 1.獲取連接(JdbcUtils工具類)
            connection = JdbcUtils.getConnection();
            // 2.開啓事務
            connection.setAutoCommit(false);
            statement = connection.createStatement();

            // 3.花花扣錢
            String xiangSql = "update account set money = money - 100 where id = 2";
            int xiangResult = statement.executeUpdate(xiangSql);
            if (xiangResult > 0) {
                System.out.println("花花支付成功");
            }
            // 機器故障
            int a = 1 / 0;
            // 4.小明加錢
            String dieSql = "update account set money = money + 100 where id = 1";
            int dieResult = statement.executeUpdate(dieSql);
            if (dieResult > 0) {
                System.out.println("小明收款成功");
            }
            // 5.提交事務
            connection.commit();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                // 6.回滾事務
                connection.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        } finally {
            // 7.釋放資源
            JdbcUtils.close(statement, connection);
        }
    }
}

在這裏插入圖片描述

  • 機器故障,雙方存款未變:
    在這裏插入圖片描述

6. 綜合案例:用戶登錄

a. 主要需求

  • 用戶輸入賬號和密碼,實現登錄網站功能。

b. 需求分析

在這裏插入圖片描述

  • 可以升級爲 MVC 三層架構:
    在這裏插入圖片描述

c. 代碼實現

i. 創建 Web 工程,並導入 Jar 包

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

ii. 導入頁面資源

在這裏插入圖片描述

iii. 複製 JdbcUtils 工具類

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/regino
jdbc.user=root
jdbc.password=root
package com.regino.util;

import java.sql.*;
import java.util.ResourceBundle;

public class JdbcUtils {
    // 聲明變量
    private static String driver = null;
    private static String url = null;
    private static String user = null;
    private static String password = null;

    // 加載jdbc.properties配置文件,初始化變量
    static {
        // new Properties().load();
        // sun公司專門提供了一從src目錄下加載properties類型的工具類 ResourceBundle
        ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
        driver = jdbc.getString("jdbc.driver");
        url = jdbc.getString("jdbc.url");
        user = jdbc.getString("jdbc.user");
        password = jdbc.getString("jdbc.password");
    }

    // 1.註冊驅動
    static {
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            // e.printStackTrace();
            throw new RuntimeException("加載MySQL驅動失敗");
        }
    }

    // 2.提供獲取連接的靜態方法
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, user, password);
    }

    // 3.提供釋放資源的方法
    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

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

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

    // 重載關閉方法
    public static void close(Statement statement, Connection connection) {
        close(null, statement, connection);
    }
}

在這裏插入圖片描述

  • 注意:properties 在 src 包下。

iv. login.jsp 引出 LoginServlet

在這裏插入圖片描述

v. LoginServlet

package com.regino.web;

import com.regino.util.JdbcUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 統一編碼
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        // 1.接收請求
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        try {
            // 2.操作JDBC
            // 2.1 獲取連接
            Connection connection = JdbcUtils.getConnection();
            // 2.2 編寫sql
            // String sql = "select * from user where username ='admin' and password ='123'";
            String sql = "select * from user where username ='" + username + "' and password ='" + password + "'";
            System.out.println(sql);
            // 2.3 獲取sql執行對象
            Statement statement = connection.createStatement();
            // 2.4 執行sql並返回結果
            ResultSet resultSet = statement.executeQuery(sql);

            // 3.判斷是否登錄成功
            if (resultSet.next()) {// 成功
                String loginUsername = resultSet.getString("username");
                request.getSession().setAttribute("loginUsername", loginUsername);
                response.sendRedirect(request.getContextPath() + "/list.jsp");
            } else {// 失敗
                request.setAttribute("error", "用戶名或密碼錯誤");
                request.getRequestDispatcher("/login.jsp").forward(request, response);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
> 補充:MVC 三層架構實現

在這裏插入圖片描述

① EncodeFilter
package com.regino.web;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter("/EncodeFilter")
public class EncodeFilter implements Filter {

    public void init(FilterConfig config) throws ServletException {

    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException {
        // 向下轉型
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 如果是post請求,統一request編碼
        if (request.getMethod().equalsIgnoreCase("post")) {
            request.setCharacterEncoding("utf-8");
        }
        // 不管是post還是get請求,都要統一response編碼
        response.setContentType("text/html;charset=utf-8");
        // 放行
        chain.doFilter(servletRequest, servletResponse);
    }

    public void destroy() {

    }
}
② LoginServlet
package com.regino.web;

import com.regino.service.LoginService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 1.接收請求
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        LoginService loginService = new LoginService();
        Boolean isCorrect = loginService.findById(username, password);

        if (isCorrect) {
            String loginUsername = username;
            request.getSession().setAttribute("loginUsername", loginUsername);
            response.sendRedirect(request.getContextPath() + "/list.jsp");
        } else {
            request.setAttribute("error", "用戶名或密碼錯誤");
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
    }
}
③ LoginService
package com.regino.service;

import com.regino.dao.LoginDao;

public class LoginService {
    LoginDao loginDao = new LoginDao();

    public boolean findById(String username, String password) {
        return loginDao.findById(username, password);
    }
}
④ LoginDao
package com.regino.dao;

import com.regino.util.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class LoginDao {
    public boolean findById(String username, String password) {
        // 調用數據庫查詢
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            // 2.操作JDBC
            // 2.1 獲取連接
            connection = JdbcUtils.getConnection();
            // 2.2 編寫sql
            String sql = "select * from user where username ='" + username + "' and password ='" + password + "'";
            System.out.println(sql);
            // 2.3 獲取sql執行對象
            statement = connection.createStatement();
            // 2.4 執行sql並返回結果
            resultSet = statement.executeQuery(sql);

            // 3.判斷是否登錄成功
            // 正確則返回至少一條,錯誤則什麼都沒有,所以只要判斷遊標能否下移一次即可
            if (resultSet.next()) {// 成功
                return true;
            } else {// 失敗
                return false;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.close(resultSet, statement, connection);
        }
        return false;
    }
}

vi. list.jsp 顯示登錄人

在這裏插入圖片描述

vii. login.jsp 返回登錄失敗

在這裏插入圖片描述

d. 測試

在這裏插入圖片描述
在這裏插入圖片描述


原文鏈接:https://qwert.blog.csdn.net/article/details/105956040

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