Java筆記17 - JDBC程式設計
阿新 • • 發佈:2020-07-14
JDBC簡介
- java程式訪問資料庫的標準介面
- JDBC介面通過JDBC驅動實現真正對資料庫的訪問
- 編寫一套程式碼, 訪問不同的資料庫
- App.class -> java.sql.* -> mysql-xxx.jar -> (TCP) -> mysql
JDBC查詢
- java.sql.*放的是一組介面
- 用哪個資料庫, 就用哪個資料庫的實現類
- 某個資料庫實現了JDBC介面的jar包稱為JDBC依賴
JDBC連線
- Connection代表一個JDBC連線, 相當於Java程式到資料庫的連線.
- 開啟一個Connection, 需要準備URL, 使用者名稱, 口令
DriverManager
- JDBC是一種昂貴的資源, 使用後要及時釋放, 使用
try (resource)
自動釋放JDBC連線
JDBC查詢詳解
- 第一步: 使用
Connection
提供的createStatement()
方法建立一個Statement
物件, 用於執行一個查詢; - 第二步: 執行
Statement
物件提供的executeQuery("SELECT * FROM students")
並傳入SQL語句. 查詢執行並獲得返回的結果集 - 第三部: 反覆呼叫
ResultSet
的next()
方法並讀取每一行結果
try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) { System.out.println(conn); try (Statement stmt = conn.createStatement()) { System.out.println(stmt); try (ResultSet rs = stmt.executeQuery("SELECT id, name, gender FROM students WHERE gender='F'")) { System.out.println(rs); while (rs.next()) { long id = rs.getLong(1); // 索引從1開始 System.out.println(id); } } } }
SQL注入
- 使用精心構造的字串, 拼接出不同SQL.
PreparedStatement
可以完全避免SQL注入- 始終使用
?
作為佔位符, 並把資料連同SQL本身傳給資料庫, 保證每次傳給資料庫的SQL語句先溝通那個. 只是站位資料不同 - 高效利用資料對查詢的查詢.
String sql = "SELECT * FROM user WHERE lgoin=? AND pass=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1, name);
ps.setObject(2, pass);
- 必須首先呼叫
setObject()
設定每個佔位符?
的值, 最後仍然獲取的仍然是ResultSet
物件
資料型別
-
JDBC定義了一組常量表示如何對映SQL資料型別, 經常用到的轉換:
-
SQL資料型別 / Java資料型別
-
BIT,BOOL / boolean
-
INTEGER / int
-
BIGINT / long
-
REAL / float
-
FLOAT,DOUBLE / double
-
CHAR,VARCHAR / String
-
DECIMAL / BigDecimal
-
DATE / java.sqal.Date,LocalDate
-
TIME / java.sql.Time,LocalTime
-
只有最新的JDBC驅動才支援
LocalDate
和LocalTime
JDBC更新
插入
-
使用
PreparedStatement
執行sql語句, 使用executeUpdate()
-
返回成功插入的數量
-
建立
PreparedStatement
時, 同時指定RETURN_GENERATED_KEYS
關鍵字, 自增後就能返回, 驅動就能返回自增關鍵則了. -
返回的是多行成功值
-
插入, 更新, 刪除都是使用
executeUpdate()
-
只是使用的sql語句不同
JDBC事務
- 資料庫事務具有ACID特性
- 資料庫從安全性考慮, 對事物進行了四種安全特性
- 資料庫事務, 保證程式結果正常
- JDBC中執行事務, 就是多條sql包裹在一個數據庫事務中
Connection conn = openConnection();
try {
// 關閉自動提交
conn.setAutoCommit(false);
// 執行多個sql
insert();
update();
delete();
conn.commit();
} catch (SQLException e) {
// 回滾
conn.rollback();
} finally {
conn.setAutoCommit(true);
conn.close();
}
- 資料庫預設使用
REPEATABLE_READ
JDBC-Batch
- 一次性生成批量優惠卷等場景
- 迴圈生成
PreparedStatement
效率低 - 反覆呼叫
addPatch
, 相當於給一個sql加上了多組引數, 變成多行sql
- 因為是多行, 返回一個數組, 表示每一條影響的sql數量
try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
try (PreparedStatement ps = conn.prepareStatement(
"INSERT INTO students (name, gender, grade, score) VALUES (?, ?, ?, ?)"
))
{
for (String name: names) {
ps.setString(1, name);
ps.setInt(2, 0); // gender
ps.setInt(3, 4); // grade
ps.setInt(4, 99); // score
ps.addBatch(); // 新增到batch
}
int[] ns = ps.executeBatch();
for (int n : ns) {
System.out.println(n + " inserted.");
}
}
}
JDBC連線池
- JDBC執行緒連線是昂貴的操作
- jdbc執行緒池標準介面
javax.sql.DataSource
- 建立
DataSource
是非常昂貴操作, 所以通常DataSource
例項總是作為一個全域性變數儲存, 並貫穿整個應用程式
config.setJdbcUrl(JDBC_URL);
config.setUsername(JDBC_USER);
config.setPassword(JDBC_PASSWORD);
config.addDataSourceProperty("connectionTimeout", "1000"); // 連線超時 1s
config.addDataSourceProperty("idleTimeout", "60000"); // 空閒超時 60s
config.addDataSourceProperty("maximumPoolSize", "10"); // 最大連線數 10
DataSource ds = new HikariDataSource(config);
try (Connection conn = ds.getConnection()) {
// ...
}
- 第一次呼叫
ds.getConnection()
會先在連線池內部建立一個Connection
- 呼叫
conn.close()
時, 並不會真的關閉, 釋放到連線池中. - 再次呼叫
getConnection()
並不會建立, 而是返回空閒的連線