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')
- 建立一個普通專案
- 匯入jar包: 建立lib - 複製jar包到lib目錄下 - 右鍵 Add as Library
- 編寫測試程式碼
程式碼步驟
- 載入驅動
- 連線資料庫 DriverManager
- 獲得執行sql的物件 Statement (不安全的物件)
- 獲得返回的結果集
- 釋放連線
// 第一個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
程式碼實現
- 開啟事務
conn.setAutoCommit(false)
- 一組業務執行完畢,提交事務
- 可以在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");