1、JDBC簡介
1.1、客戶端操作MySQL資料庫的方式
- 使用DOS命令列方式
- 使用第三方客戶端來訪問MySQL:SQLyog、Navicat、....
- 通程序式來訪問MySQL資料庫
- 而通過Java來訪問MySQL資料庫,就是JDBC的概念

1.2、JDBC的概念
- 什么是JDBC
- Java Data Base Connectivity:Java資料庫連接
- JDBC作用
- 通過JDBC可以讓Java程式操作資料庫
- JDBC本質
- 官方(SUN)公司定義的一套操作所有關系型資料庫的規則,即介面(API)
- 各個資料庫廠商去實作這套介面,提供資料庫驅動jar包
- 我們可以使用這套介面(JDBC)編程,真正執行的代碼是驅動jar包中的實作類
- JDBC的好處
- 只需要會呼叫JDBC介面中的方法即可,使用簡單
- 使用同一套Java代碼,進行少量的修改就可以訪問其他JDBC支持的資料庫
2、JDBC API詳解
2.1、JDBC四個核心物件
- JDBC的使用步驟
- 注冊驅動
- 獲取資料庫連接
- 獲取SQL陳述句物件
- 執行SQL陳述句并回傳結果
- 處理結果
- 釋放資源
- JDBC四個核心物件
- JDBC互動圖
2.2、JDBC注冊驅動
-
Java程式需要通過資料庫驅動才能連接到資料庫,因此需要注冊驅動
- 在注冊驅動之前需要先匯入驅動的Jar包

-
JDBC注冊驅動
-
java.sql.DriverManager類用于注冊驅動,提供如下方法注冊驅動
-
static void registerDriver(Driver driver) // 向DriverManager 注冊給定驅動程式
-
-
-
示例代碼
-
public class Demo01 { public static void main(String[] args) throws Exception { // 注冊驅動 DriverManager.registerDriver(new com.mysql.jdbc.Driver()); } }
-
-
提示
- MySQL 5 之后的驅動包,可以省略注冊驅動的步驟
- 自動加載jar包中META-INF/services/java.sql.Driver檔案中的驅動類
2.3、獲取Connection連接
-
Connection 介紹
-
表示Java程式與資料庫之間的連接,只有拿到Connection才能操作資料庫
-
DriverManager類中的靜態方法 描述 static Connection getConnection(String url, String user, String password) 連接到給定資料庫URL,并回傳連接 -
引數說明
- String url:連接資料庫的URL,用于說明資料庫的位置
- String user:資料庫的賬號
- String password:資料庫的密碼
-
連接資料庫的URL地址格式
- 協議名:子協議://服務器名或IP地址:埠號/資料庫名
-
MySQL寫法
- jdbc:mysql://localhost:3306/day03
-
如果是本地服務器,埠號是默認的3306,則可以簡寫
- jdbc:mysql:///day03
-
2.4、獲取Statement物件
-
在java.sql.Connection介面中有如下方法獲取到Statement物件
-
Statement createStatement() // 創建一個Statement物件來將SQL陳述句發送到資料庫
-
-
代碼案例
-
// 1.注冊驅動 // 2.獲取連接 Connection conn = DriverManager.getConnection("jdbc:mysql:///day03", "root", "123"); // 3.獲取Statement Statement stmt = conn.createStatement();
-
3、JDBC實作對單表資料增加、洗掉、修改
-
我們要對資料庫進行增、刪、改、查,需要使用
Statement物件來執行SQL陳述句 -
Statement的API介紹
-
ResultSet executeQuery(String sql) // 用于執行查詢陳述句; 回傳查詢到的結果集 int executeUpdate(String sql) // 用于執行除查詢外的SQL; 回傳影響的行數
-
4、JDBC獲取資料(Result類)
4.1、ResultSet的原理
-
ResultSet用于保存執行查詢SQL陳述句的結果,我們不能一次性去除所有的資料,需要一行一行的去除
-
ResultSet內部有一個指標,記錄獲取到哪行資料
-
獲取查詢結果
-
boolean next(): /* (1) 將游標從當前位置向前移動一行 (2)判斷當前行是否為有效行 回傳值: true:有效行,當前行有資料 false:無效行,當前行沒有資料 */ -
應用案例
-
while (rs.next()) { rs.getXxx(欄位名); // 取出資料 }
-
-
4.2、ResultSet獲取資料的API
-
ResultSet獲取資料的API是有規律的get后面加資料型別,我們統稱getXXX()
-
方法名 說明 boolean getBoolean(String columnLabel) 獲取boolean值 byte getByte(String columnLabel) 獲取byte值 double getDouble(String columnLabel) 獲取double值 int getInt(String columnLabel) 獲取int值 long getLong(String columnLabel) 獲取long值 String getString(String columnLabel) 獲取String值
4.3、ResultSet的getXXX方法與MySQL中資料型別的對應

- 注意
- 這只是一個建議,不按這個表的對應關系也可以,只要資料型別可以自動轉換,如:int型別,使用String去取,也是可以的,但如果是String型別,使用int去取就不行
5、JDBC事務處理
-
JDBC操作銀行轉賬的事務
- Connection介面中與事務有關的方法

-
使用步驟
- 1.注冊驅動
- 2.獲取連接
- 3.開啟事務
- 4.獲取Statement物件
- 5.執行SQL陳述句
- 6.提交或者回滾事務
- 7.關閉資源
-
demo
-
package _02MySQL.Day03_JDBC.demo05_事務處理_重點; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * JDBC事務處理 * * 資料準備: * CREATE TABLE tb_account ( * id INT PRIMARY KEY AUTO_INCREMENT, * NAME VARCHAR(10), * balance DOUBLE * ); * *-- 添加資料 * INSERT INTO tb_account (NAME, balance) VALUES ('張三', 1000), ('李四', 1000); */ public class Demo05 { public static void main(String[] args) throws SQLException{ // 1. 注冊驅動 Connection connection = null; try { // 2. 獲取資料庫連接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/day03", "root", "123"); // 3. 開啟事務 connection.setAutoCommit(false); // 關閉自動提交 // 4. 獲取Statement物件 Statement statement = connection.createStatement(); // 5. 執行SQL String sql1 = "update tb_account set balance = balance - 500 where name = '張三'"; String sql2 = "update tb_account set balance = balance + 500 where name = '李四'"; statement.executeUpdate(sql1); statement.executeUpdate(sql2); // 模擬失敗 // ... // 6. 提交事務 System.out.println("提交事務!"); connection.commit(); } catch (Exception e) { // 6. 有例外,事務回滾 if (connection != null) { connection.rollback(); } }finally { // 7. 關閉資源 if (connection != null) { connection.close(); } } } }
-
6、JDBC實作用戶登錄
-
模擬用戶輸出賬號和密碼登錄網站
-
案例分析
- 1.使用資料庫中保存用戶的賬號和密碼
- 2.讓用戶輸入賬號和密碼
- 3.使用SQL根據用戶的賬號和密碼去資料庫查詢資料
- 4.如果查詢到資料,說明登錄成功
- 5.如果查詢不到資料,說明登錄失敗
-
Demo
-
package com.itheima.demo06_JDBC實作用戶登錄; import java.sql.*; import java.util.Scanner; /** * JDBC實作用戶登錄案例 */ public class Demo06 { public static void main(String[] args) throws SQLException { Scanner scanner = new Scanner(System.in); String userName = null; String password = null; // 1. 接收用戶名 while (true) { System.out.println("請輸入用戶名:"); String line = scanner.nextLine(); if ("".equals(line)) { continue; } userName = line; break; } // 2.接收用戶密碼 while (true) { System.out.println("請輸入密碼:"); String line = scanner.nextLine(); if ("".equals(line)) { continue; } password = line; break; } // 3.根據用戶名和密碼,查詢用戶 String sql = "SELECT * FROM USER WHERE NAME = '" + userName + "' AND PASSWORD = '" + password + "';"; String jdbcUrl = "jdbc:mysql://localhost:3306/day03"; Connection conn = DriverManager.getConnection(jdbcUrl, "root", "root"); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); // 4.根據查詢結果,判斷是否登錄成功 // 4.1 如果查詢到資料,顯示登錄成功 if (rs.next()) { System.out.println("登錄成功!歡迎您," + userName); } else { // 4.2 如果查詢不到資料,顯示登錄失敗 System.out.println("登錄失敗!用戶名或密碼錯誤..."); } } }
-
7、SQL注入攻擊
7.1、SQL注入問題
-
在我們前面JDBC實作登錄案例中,當我們輸入以下密碼的時候,可以發現賬號和密碼都不對竟然登錄成功了!
-
請輸入用戶名: hehe 請輸入密碼: a'or'1'='1
-
-
字串拼接,把輸入的字串全都將其視為sql陳述句,導致statement物件查詢直接為真,條件不起作用!
-
問題分析
-
"SELECT * FROM user WHERE name='" + name + "' AND password='" + password + "';"; // 將用戶輸入的賬號密碼拼接后 "SELECT * FROM user WHERE name='hehe' AND password='a'or'1'='1';"
-
-
SQL注入攻擊的原理
- 按照正常道理來說,在密碼處輸入的所有內容,都應該認為是密碼的組成
- 但是現在Statement物件在執行sql陳述句時,將密碼的一部分內容當作查詢條件來執行了,
7.2、解決SQL注入
-
PreparedStatement預編譯執行者物件
- 預編譯:SQL陳述句子在執行前就已經編譯好了,執行速度更快
- 安全性更高:沒有字串拼接的SQL陳述句,所以避免SQL注入的問題
- 代碼的可讀性更好,是因為沒有字串拼接
-
PreparedStatement使用
- SQL陳述句中的引數使用?作為參為輻
- 給?占位符賦值
-
設定引數
- setXxx(引數1,引數2):Xxx代表資料型別
- 引數1:第幾個?(編號從1開始)
- 引數2:?的實際引數
-
執行SQL陳述句
- int executeUpdate()
- 執行insert、update、delete陳述句
- ResultSet executeQuery()
- 執行select陳述句
- int executeUpdate()
-
demo
-
String sql = "SELECT * FROM USER WHERE NAME=? AND PASSWORD=?;"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, “zhangsan”); pstmt.setString(2, “6666”);
-
-
?
7.3、使用PreparedStatement改寫登錄案例
-
package _02MySQL.Day03_JDBC.demo08_PreparedStatement改寫登錄案例; import java.sql.*; import java.util.Scanner; /** * PreparedStatement改寫登錄案例 */ public class Demo08 { public static void main(String[] args) throws SQLException { // 1. 注冊驅動 // 2. 獲取連接 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/day03", "root", "123"); // 3. 獲取PreparedStatement物件 String sql = "select * from day03.user where phoneNumber = ? and password = ?"; PreparedStatement preparedStatement = connection.prepareStatement(sql); // 準備引數 Scanner scanner = new Scanner(System.in); String phoneNumber = null; String password = null; do { System.out.print("請輸入賬號:"); phoneNumber = scanner.nextLine(); } while ("".equals(phoneNumber)); do { System.out.print("請輸入密碼: "); password = scanner.nextLine(); } while ("".equals(password)); // 4. 執行SQL陳述句 preparedStatement.setString(1, phoneNumber); preparedStatement.setString(2, password); ResultSet resultSet = preparedStatement.executeQuery(); // 5. 處理結果 if (resultSet.next()) { System.out.println("歡迎您,尊敬的" + phoneNumber + "用戶"); }else { System.out.println("賬號或密碼錯誤!"); } // 6. 釋放資源 connection.close(); } }
8、JDBC連接池
8.1、常規資料庫連接的問題
- JDBC訪問資料庫的步驟
- 創建資料庫連接
- 運行SQL陳述句
- 關閉連接
- 上述動作每次資料庫訪問都會執行這樣的重復動作
- 而每次創建資料庫連接的問題
- 獲取資料庫連接需要消耗比較多的資源,而每次操作都要重新獲取新的連接物件,執行一次操作就把連接關閉,而資料庫創建連接通常需要消耗相對較多的資源,這樣資料庫連接物件的使用率低
8.2、資料庫連接池簡介
- 現實生活中每日三餐,我們并不會吃一餐飯就將碗丟掉,而是吃完飯后將碗放到碗柜中,下一餐接著使用,目的是重復利用碗,資料庫連接也可以重復使用,可以減少資料庫連接的創建次數,提高資料庫連接物件的使用率
- 連接池的概念
- 連接池就是一個容器,連接池中保存了一些資料庫連接,這些連接是可以重復使用的
- 連接池的原理
- 1.啟動連接池,連接池就會初始化一些連接
- 2.當用戶需要使用資料庫連接,直接從連接池中取出
- 3.當用戶使用完連接,會將連接重新放回連接池中
- 連接池的好處
- 連接池中保存一些連接,這些連接可以重復使用,降低資料資源的消耗
8.3、常用連接池的介紹
-
javax.sql.DataSource表示資料庫連接池,也是JDK中提供的一個介面,沒有具體的實作,它的實作由連接池的廠商去實作,我們只需要學習這個工具如何使用
-
public interface DataSource{ Connection getConnection(); ... }
-
-
常用的連接池實作組件有以下這些
- 阿里巴巴-德魯伊Druid連接池
- Druid是阿里巴巴開源平臺上的一個專案
- C3P0是一個開源的連接池,目前使用它的開源專案有Hibernate,Spring等
- DBCP(DataBase Connection Pool)資料庫連接池,是Tomcat使用的連接池組件
- 阿里巴巴-德魯伊Druid連接池
8.3、Druid連接池簡介
-
Druid是阿里巴巴開發的號稱為監控而生的資料庫連接池,Druid是目前最好的資料庫連接池,在功能、性能、擴展性方面,都超過了其他資料連接池,同時加入了日志監控,可以很好的監控資料庫連接池和SQL的執行情況,
- Druid已經在阿里巴巴部署了超過600個應用,經過一年多生產大環境部署的嚴苛考驗
-
Druid常用的配置引數
-
方法名 說明 initialSize 剛啟動連接池時,連接池中包含連接的數量 maxActive 連接池中最多可以放多少個連接 maxWait 獲取連接時最大等待時間,單位毫秒
-
-
com.alibaba.druid.pool.DruidDataSourceFactory類創建連接池的方法
-
public static DataSource createDataSource(Properties properties) // 創建一個連接池,連接池的引數使用properties中的資料
-
-
我們可以看到Druid連接池在創建的時候需要一個Properties物件來設定引數,所以我們使用properties檔案來保存對應的引數,Druid連接池的組態檔名稱隨便,放到src目錄下面方便加載
-
druid.properties檔案內容
-
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/day17 username=root password=root initialSize=5 maxActive=10 maxWait=3000
-
8.4、Druid連接池使用步驟
- 1.匯入druid-1.0.0.jar的jar包
- 2.賦值druid.properties檔案到src下,并設定對應引數
- 3.加載properties檔案的內容到Properties物件中
- 4.創建Druid連接池,使用組態檔中的引數
- 5.從Druid連接池中取出連接
- 6.執行SQL陳述句
- 7.關閉資源
9、JDBC案例(在Java程式中實作對資料庫的增刪改查)
-
Demo類
-
package _02MySQL.Day03_JDBC.demo10_JDBC增刪改查練習; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; /** * JDBC增刪改查練習---- 完成商品品牌資料的增刪改查操作 * 查詢資料:查詢所有資料 * 添加資料:添加品牌 * 修改資料:根據id修改 * 洗掉資料:根據id洗掉 */ public class Demo10 { /* 資料準備: 創建一個商品品牌的資料庫表 tb_brand 創建一個Brand物體類 */ public static void main(String[] args) throws Exception { // 1. 增加資料 // addBrand(); // 2. 洗掉資料 deleteBrand(); // 3. 修改資料 editBrand(); // 4. 查詢資料 seekBrand(); } public static void addBrand() throws SQLException { // 獲取連接 Connection connection = DataSourceUtils.getConnection(); // 獲取preparedStatement物件 String sql = "insert into day03.tb_brand (brand, description, headquarters) values" + "(?, ?, ?)"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, "格力空調"); preparedStatement.setString(2, "美好生活格力造"); preparedStatement.setString(3, "China"); // 得到statement物件回傳的影響行數 int i = preparedStatement.executeUpdate(); System.out.println(i); // 將連接回傳給連接池 connection.close(); } // 根據id洗掉資料 public static void deleteBrand() throws SQLException { // 獲取連接 Connection connection = DataSourceUtils.getConnection(); // 撰寫洗掉資料的SQL, 并獲取preparedStatement物件 String sql = "delete from day03.tb_brand where id = ?"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1, 5); // 執行SQL, 回傳影響的行數 int i = preparedStatement.executeUpdate(); System.out.println(i); // 將連接回傳給連接池 connection.close(); } // 根據id編輯資料 public static void editBrand() throws SQLException { // 獲取連接 Connection connection = DataSourceUtils.getConnection(); // 撰寫sql,并獲取preparedStatement物件 String sql = "update day03.tb_brand set brand = ?, description = ?, headquarters = ? where id = ?"; PreparedStatement preparedStatement = connection.prepareStatement(sql); // 補全SQL preparedStatement.setString(1, "華強北"); preparedStatement.setString(2, "天上地下我華強北說第二,誰敢說第一"); preparedStatement.setString(3, "中國深圳"); preparedStatement.setInt(4, 1); // 執行SQL陳述句, 回傳影響的行數 int i = preparedStatement.executeUpdate(); System.out.println(i); // 關閉連接,將連接回傳給連接池 connection.close(); } public static void seekBrand() throws SQLException { // 獲取連接 Connection connection = DataSourceUtils.getConnection(); // 獲取Statement物件 String sql = "select * from day03.tb_brand"; PreparedStatement preparedStatement = connection.prepareStatement(sql); // 得到資料集合 ResultSet resultSet = preparedStatement.executeQuery(); // 處理資料 List<Brand> brandList = new ArrayList<>(); // 存盤資料 while (resultSet.next()) { int id = resultSet.getInt("id"); String brand = resultSet.getString("brand"); String description = resultSet.getString("description"); String headquarters = resultSet.getString("headquarters"); brandList.add(new Brand(id, brand, description, headquarters)); } brandList.forEach((brand) -> System.out.println("data = "https://www.cnblogs.com/OnlyOnYourself-lzw/p/+ brand)); // 將連接回傳給連接池 connection.close(); } }
-
-
DataSourceUtils工具類(獲取Datasource)
-
package _02MySQL.Day03_JDBC.demo10_JDBC增刪改查練習; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; /** * 工具類 * -- 創建Druid連接池 */ public class DataSourceUtils { private static DataSource dataSource; static { // 利用靜態代碼塊加載創建連接池的操作 try { // 1. 載入組態檔 // 獲取properties組態檔 InputStream inputStream = DataSourceUtils.class.getResourceAsStream("brand.properties"); Properties properties = new Properties(); properties.load(inputStream); // 2. 創建連接 dataSource = DruidDataSourceFactory.createDataSource(properties); }catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }
-
-
Brand物體類
-
package _02MySQL.Day03_JDBC.demo10_JDBC增刪改查練習; public class Brand { private int id; private String brand; private String description; private String headquarters; public Brand(int id, String brand, String description, String headquarters) { this.id = id; this.brand = brand; this.description = description; this.headquarters = headquarters; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getHeadquarters() { return headquarters; } public void setHeadquarters(String headquarters) { this.headquarters = headquarters; } @Override public String toString() { return "Brand{" + "id=" + id + ", brand='" + brand + '\'' + ", description='" + description + '\'' + ", headquarters='" + headquarters + '\'' + '}'; } }
-
-
brand.properties組態檔
-
driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/day03 username=root password=123 initialSize=5 maxActive=10 maxWait=3000
-
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/498693.html
標籤:Java
上一篇:JVM詳解


