1. 程式人生 > 其它 >MySQL筆記10:JDBC

MySQL筆記10:JDBC

10. JDBC(重點)

10.1 資料庫驅動

驅動:音效卡、顯示卡、資料庫...

驅動由資料庫產商提供,用於連線應用程式和資料庫

10.2 JDBC

JDBC: SUN公司提供的,用於簡化開發人員對資料庫的統一操作的,Java操作資料庫的規範

具體實現由資料庫廠商實現,開發人員只需要掌握操作

匯入包:java.sql, javax.sql, 資料驅動包

10.3 第一個JDBC程式

  • 建立測試資料庫
CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;

USE jdbcStudy;

CREATE TABLE `users`(
id INT PRIMARY KEY,
NAME VARCHAR(40),
PASSWORD VARCHAR(40),
email VARCHAR(60),
birthday DATE
);

INSERT INTO `users`(id,NAME,PASSWORD,email,birthday)
VALUES(1,'zhansan','123456','[email protected]','1980-12-04'),
(2,'lisi','123456','[email protected]','1981-12-04'),
(3,'wangwu','123456','[email protected]','1979-12-04')
  1. 建立一個普通專案
  2. 匯入jar包: 建立lib - 複製jar包到lib目錄下 - 右鍵 Add as Library
  3. 編寫測試程式碼

程式碼步驟

  1. 載入驅動
  2. 連線資料庫 DriverManager
  3. 獲得執行sql的物件 Statement (不安全的物件)
  4. 獲得返回的結果集
  5. 釋放連線
// 第一個JDBC程式
public class JdbcDemo01 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 載入驅動
        Class.forName("com.mysql.jdbc.Driver"); // 載入驅動的固定寫法

        // 2. 使用者資訊和url
        // useUnicode=true&characterEncoding=utf8&useSSL=true 支援中文編碼,設定字元編碼方式,使用安全的連線
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String username = "root";
        String password = "123456";

        // 3. 連線成功 資料庫物件 Connection代表資料庫
        Connection connection = DriverManager.getConnection(url, username, password);

        // 4. 執行SQL的物件 Statement
        Statement statement = connection.createStatement();

        // 5. 執行SQL的物件 去 執行SQL, 可能存在結果, 檢視返回結果
        String sql = "SELECT * FROM users";

        // 返回結果集,結果集中封裝了全部的查詢結果
        ResultSet resultSet = statement.executeQuery(sql);

        while (resultSet.next()) {
            System.out.println("id = " + resultSet.getObject("id")); //使用列名查詢內容
            System.out.println("name = " + resultSet.getObject("NAME"));
            System.out.println("password = " + resultSet.getObject("PASSWORD"));
            System.out.println("email = " + resultSet.getObject("email"));
            System.out.println("birthday = " + resultSet.getObject("birthday"));
            System.out.println("========================");
        }

        // 6. 釋放連線
        resultSet.close();
        statement.close();
        connection.close();
    }
}

DriverManager

// DriverManager.registerDriver(new com.mysql.jdbc.Driver()); 中間有靜態程式碼塊,之間可以直接建立物件
Class.forName("com.mysql.jdbc.Driver"); // 載入驅動的固定寫法 

// connection代表資料庫
Connection connection = DriverManager.getConnection(url, username, password);

connection.setAutoCommit(); // 資料庫設定自動提交
connection.rollback(); 		// 事務回滾
connection.commit(); 		// 事務提交

URL

// 協議://主機地址:埠/資料庫名?引數1&引數2&引數3
// mysql預設埠 3306
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";

// Oralce 預設埠 1521
// jdbc:oralce:thin:@localhost:1521:sid

Statement / PrepareStatement 執行SQL的物件

String sql = "SELECT * FROM users"; // 編寫sql

statement.executeQuery(); 	// 查詢操作,返回一個結果集resultSet
statement.executeUpdate(); 	// 用於更新, 插入, 刪除 返回SQL匹配到的行數
statement.execute(); 		// 執行任何SQL

ResultSet查詢的結果集:封裝了所有的查詢結果

  • 獲得制定的資料型別
resultSet.getObject(); // 在不知道列型別時使用
resultSet.getInt();
resultSet.getFloat();
resultSet.getString();
...
  • 遍歷,指標
resultSet.next(); 		// 移動到下一個
resultSet.afterLast();	// 移動到最後
resultSet.beforeFirst();// 移動到最前面
resultSet.previous();	// 移動到前一行
resultSet.absolute(row);// 移動到指定行

釋放資源

resultSet.close();
statement.close();
connection.close();

10.4 Statement物件

JDBC中的statement物件用於向資料庫傳送SQL語句,想完成對資料庫的增刪改查,只需要通過這個物件向資料庫傳送增刪改查語句即可。
Statement物件的executeUpdate方法,用於向資料庫傳送增、刪、改的sql語句, executeUpdate執行完後, 將會返回一個整數(即增刪改語句導致了資料庫幾行資料發生了變化)。
Statement.executeQuery方法用於向資料庫發生查詢語句,executeQuery方法返回代表查詢結果的ResultSet物件。

CRUD操作 - create

Statement statement = connection.createStatement();
String sql = "insert into user(...) values(...)";
int num = statement.executeUpdate(sql);
if(num>0){
	System.out.println("插入成功");
}

CRUD操作 - delete

Statement statement = connection.createStatement();
String sql = "insert into user(...) values(...)";
int num = statement.executeUpdate(sql);
if(num>0){
	System.out.println("刪除成功");
}

CURD操作 - update

Statement statement = connection.createStatement();
String sql = "insert into user(...) values(...)";
int num = statement.executeUpdate(sql);
if(num>0){
	System.out.println("修改成功");
}

CURD操作 - read

Statement statement = connection.createStatement();
String sql = "select * from  user where id =1";
ResultSet rs= statement.executeQuery(sql);
if(rs.next()){
	System.out.println("");
}

程式碼實現

1. 配置檔案

在mysql8.0中:Driver位置由com.mysql.jdbc.Driver 變為com.mysql.cj.jdbc.Driver

# 配置檔案
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456

2. 工具類

public class JdbcUtils {
    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;

    static {
        try {
            // 讀取配置檔案為輸入流
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            // 1. 驅動只需要載入一次
            Class.forName(driver);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 獲取連線
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }

    // 釋放連線資源
    public static void release(Connection conn, Statement st, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

        if (st != null) {
            try {
                st.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

3. 增刪改的應用executeUpdate

  • 插入
public class TestInsert {
    public static void main(String[] args) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection(); // 獲得資料庫連線
            st = conn.createStatement(); // 獲得sql的執行物件
            String sql = "INSERT INTO users(id,`NAME`,`PASSWORD`,`email`,`birthday` )" +
                    "VALUES(4, 'chachan', '123456', '[email protected]', '2022-6-1')";
            int i = st.executeUpdate(sql); //執行並返回結果
            if (i > 0) {
                System.out.println("插入成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(conn, st ,rs);
        }

    }
}
  • 刪除

修改sql語句為刪除的語句

String sql = "DELETE FROM users WHERE id = 4";
  • 修改

修改sql語句為更新的語句

String sql = "UPDATE users SET `NAME` = 'cha' WHERE id = 1";

4. 查的應用executeQuery

使用executeQuery查詢,返回結果集

public class TestSelect {
    public static void main(String[] args) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();

            // sql
            String sql = "select * from users where id = 1";
            rs = st.executeQuery(sql); // 查詢使用executeQuery,返回結果集
            while (rs.next()) {
                System.out.println(rs.getString("name"));
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(conn, st, rs);
        }
    }
}

SQL注入的問題

sql存在漏洞,會被攻擊導致資料洩露, SQL資料會因為or的拼接被洩露

package com.chachan53.class2;

import com.chachan53.class2.utils.JdbcUtils;

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

public class SQLInjection {
    public static void main(String[] args) {
        // login("cha","123456");
        login("' or '1=1","' OR '1=1" );
    }

    // 登入業務
    public static void login(String username, String password) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();

            // SELECT * FROM users WHERE `NAME` = '' OR '1=1' AND `PASSWORD` = '' OR '1=1'
            String sql = "SELECT * FROM users WHERE `NAME` = '"+ username +"' AND `PASSWORD` = '"+ password +"'";

            rs = st.executeQuery(sql);
            while (rs.next()) {
                System.out.println(rs.getString("name"));
                System.out.println(rs.getString("password"));
                System.out.println("================");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

輸出結果:

cha
123456
================
lisi
123456
================
wangwu
123456
================

10.5 PreparedStatement物件

可以防止SQL注入,並效率更高

與Statement的區別

  • 使用 ? 佔位符替代引數
  • 預編譯
  • 手動賦值引數(索引從1開始)
// 區別
// 使用 ? 佔位符替代引數
String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`) value(?,?,?,?,?)";
st = conn.prepareStatement(sql); //預編譯sql, 先寫sql, 然後不執行

// 手動給引數賦值, 索引從1開始
st.setInt(1,4);
st.setString(2,"chachan");
st.setString(3,"123456");
st.setString(4,"[email protected]");
// 注意點 sql.Date 資料庫         java.sql.Date()
//       util.Date java用        new Date().getTime() 獲得時間戳
st.setDate(5, new java.sql.Date(new Date().getTime()));

插入資料

刪除、更新修改SQL語句

package com.chachan53.class3;

import com.chachan53.class2.utils.JdbcUtils;

import java.sql.*;
import java.util.Date;

public class TestInsert {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement st = null; // 不同

        try {
            conn = JdbcUtils.getConnection();

            // 區別
            // 使用 ? 佔位符替代引數
            String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`) value(?,?,?,?,?)";
            st = conn.prepareStatement(sql); //預編譯sql, 先寫sql, 然後不執行

            // 手動給引數賦值, 索引從1開始
            st.setInt(1,4);
            st.setString(2,"chachan");
            st.setString(3,"123456");
            st.setString(4,"[email protected]");
            // 注意點 sql.Date 資料庫         java.sql.Date()
            //       util.Date java用        new Date().getTime() 獲得時間戳
            st.setDate(5, new java.sql.Date(new Date().getTime()));

            // 執行
            int i = st.executeUpdate();
            if (i > 0) {
                System.out.println("插入成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(conn, st ,null);
        }
    }
}

查詢資料

執行使用executeQuery,並返回結果

public class TestSelect {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();

            // 區別
            // 使用 ? 佔位符替代引數
            String sql = "select * from users where id = ?";
            st = conn.prepareStatement(sql); //預編譯sql, 先寫sql, 然後不執行

            // 手動給引數賦值, 索引從1開始
            st.setInt(1,1);

            // 執行
            rs = st.executeQuery();
            while (rs.next()) {
                System.out.println(rs.getObject("name"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(conn, st ,null);
        }
    }
}

防止SQL注入

  • PrepareStatement 防止SQL注入, 把傳遞的引數當成字元
  • 其中的轉義字元會被直接轉義
package com.chachan53.class2;

import com.chachan53.class2.utils.JdbcUtils;

import java.sql.*;

public class SQLInjection {
    public static void main(String[] args) {
        login("cha","123456");
        // login("'' or 1=1","123456" );
    }

    // 登入業務
    public static void login(String username, String password) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            // SELECT * FROM users WHERE `NAME` = '' OR '1=1' AND `PASSWORD` = '' OR '1=1'
            String sql = "select * from users where `NAME`= ?  and `PASSWORD`= ? ";

            // PrepareStatement 防止SQL注入, 把傳遞的引數當成字元
            // 其中的轉義字元會被直接轉義
            st = conn.prepareStatement(sql);
            st.setString(1,username);
            st.setString(2,password);
            rs = st.executeQuery();
            while (rs.next()) {
                System.out.println(rs.getString("name"));
                System.out.println(rs.getString("password"));
                System.out.println("================");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

10.6 使用IDEA連線資料庫

社群版

Setting - Plugins - 搜尋database - 安裝

專業版

右側 database

10.7 事務

要麼都成功,要麼都失敗

ACID

程式碼實現

  1. 開啟事務conn.setAutoCommit(false)
  2. 一組業務執行完畢,提交事務
  3. 可以在catch語句中顯示的定義回滾,但預設失敗就會回滾
package com.chachan53.class4;

import com.chachan53.class2.utils.JdbcUtils;

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

public class TestTransaction1 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            // 關閉資料庫的自動提,自動開啟事務
            conn.setAutoCommit(false); // 開啟事務
            String sql1 = "update account set money = money-100 where name = 'A'";
            String sql2 = "update account set money = money+100 where name = 'B'";
            st = conn.prepareStatement(sql1);
            st.executeUpdate();

            // int x = 1/0; // 業務失敗測試

            st = conn.prepareStatement(sql2);
            st.executeUpdate();

            // 業務完畢,提交事務
            conn.commit();

        } catch (SQLException e) {
            try {
                conn.rollback(); // 失敗預設回滾,不寫也會回滾
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            JdbcUtils.release(conn, st, rs);
        }
    }
}

10.8 資料庫連線池

資料庫 - 執行完畢 - 釋放

連線 - 釋放的過程非常浪費系統資源

池化技術:預先準備一些資源,直接連線準備好的

最小連線數:預先準備資源數

最大連線數:業務連線上線,超出上限,業務等待

等待超時:等待時間上限

編寫連線池:實現DataSourse介面

開源資料來源實現(拿來即用)

DBCP、C3P0、Druid(阿里)

使用資料庫連線池後,在專案開發中不需要編寫連線資料的程式碼

本質:無論使用什麼資料來源,本質相同,DataSourse介面不變,使用方法不變

DBCP

jar包:dbcp, pool, logging

使用配置檔案,在工具類中修改

// 讀取配置檔案為輸入流
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties properties = new Properties();
properties.load(in);

// 建立資料來源 工廠模式: 建立物件,設計模式
dataSource = BasicDataSourceFactory.createDataSource(properties);

使用時改變連線方式

C3P0

jar包:c3p0, mchange

使用xml的配置檔案,在工具類時修改配置方法

// 程式碼配置
// dataSource = new ComboPooledDataSource();
// dataSource.setDriverClass();
// dataSource.setUser();
// dataSource.setPassword();
// dataSource.setJdbcUrl();
//
// dataSource.setMaxPoolSize();
// dataSource.setMinPoolSize();

// 配置檔案配置
dataSource = new ComboPooledDataSource("MySQL");