來自拉鉤教育-JAVA就業(yè)集訓(xùn)營
1. JDBC 概述
1.1 客戶端操作數(shù)據(jù)庫的方式
- 方式1: 使用第三方客戶端來訪問 MySQL:SQLyog

image-20210227201534174.png
- 方式2: 使用命令行

image-20210227201545052.png
- 我們今天要學(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)類。
- JDBC就是由sun公司定義的一套操作所有關(guān)系型數(shù)據(jù)庫的規(guī)則(接口),而數(shù)據(jù)庫廠商需要實(shí)現(xiàn)這套接口,提供數(shù)據(jù)庫
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)包
- 將MySQL驅(qū)動(dòng)包添加到j(luò)ar包庫文件夾中,Myjar文件夾,用于存放當(dāng)前項(xiàng)目需要的所有jar包

image-20210227210508051.png
- 在 idea中 配置jar包庫的位置

image-20210227201714935.png
- 創(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" |
- 代碼示例
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");
}
}
- 為什么這樣可以注冊驅(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ù)庫連接對象 |
- getConnection方法 3個(gè) 連接參數(shù)說明
| 連接參數(shù) | 說明 |
|---|---|
| user | 登錄用戶名 |
| password | 登錄密碼 |
| url mySql | URL的格式 jdbc:mysql://localhost:3306/db4 |
- 對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
- 代碼示例
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.釋放資源
- 需要釋放的對象:ResultSet 結(jié)果集,Statement 語句,Connection 連接
- 釋放原則:先開的后關(guān),后開的先關(guān)。ResultSet ==> Statement ==> Connection
- 放在哪個(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é)
- 獲取驅(qū)動(dòng)(可以省略)
- 獲取連接
- 獲取Statement對象
- 處理結(jié)果集(只在查詢時(shí)處理)
- 釋放資源
3. JDBC實(shí)現(xiàn)增刪改查
3.1 JDBC工具類
-
什么時(shí)候自己創(chuàng)建工具類?
- 如果一個(gè)功能經(jīng)常要用到,我們建議把這個(gè)功能做成一個(gè)工具類,可以在不同的地方重用。
- “獲得數(shù)據(jù)庫連接”操作,將在以后的增刪改查所有功能中都存在,可以封裝工具類JDBCUtils。提供獲取連接對象的方法,從而達(dá)到代碼的重復(fù)利用。
-
工具類包含的內(nèi)容
- 可以把幾個(gè)字符串定義成常量:用戶名,密碼,URL,驅(qū)動(dòng)類
- 得到數(shù)據(jù)庫的連接:getConnection()
- 關(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注入演示
向jdbc_user表中 插入兩條數(shù)據(jù)
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)用戶的登錄。
- 步驟
得到用戶從控制臺(tái)上輸入的用戶名和密碼來查詢數(shù)據(jù)庫
寫一個(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 問題分析
- 什么是SQL注入?
- 我們讓用戶輸入的密碼和 SQL 語句進(jìn)行字符串拼接。用戶輸入的內(nèi)容作為了 SQL 語句語法的一部分,改變了 原有SQL 真正的意義,以上問題稱為 SQL 注入 .
- 如何實(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
- 如何解決
- 要解決 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的步驟
- 編寫 SQL 語句,未知內(nèi)容使用?占位:
"SELECT * FROM jdbc_user WHERE username=? AND password=?";
- 獲得 PreparedStatement 對象
- 設(shè)置實(shí)際參數(shù):setXxx( 占位符的位置, 真實(shí)的值)
- 執(zhí)行參數(shù)化 SQL 語句
- 關(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ū)別?
- Statement用于執(zhí)行靜態(tài)SQL語句,在執(zhí)行時(shí),必須指定一個(gè)事先準(zhǔn)備好的SQL語句。
- PrepareStatement是預(yù)編譯的SQL語句對象,語句中可以包含動(dòng)態(tài)參數(shù)“?”,在執(zhí)行時(shí)可以為“?”動(dòng)態(tài)設(shè)置參數(shù)
值。 - 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ā)步驟
- 獲取連接
- 開啟事務(wù)
- 獲取到 PreparedStatement , 執(zhí)行兩次更新操作
- 正常情況下提交事務(wù)
- 出現(xiàn)異常回滾事務(wù)
- 最后關(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);
}
}
}