JDBC的簡單介紹
概念
- JDBC : Java Database Connectivity,java連線資料庫,使用Java運算元據庫的技術。
- 本質 : 其實就是sun公司定義的一套操作所有關係型資料庫的規則,即介面。
各個資料庫廠商去實現這套介面,提供資料庫操作驅動jar包,我們可以使用這套
介面進行(JDBC)程式設計,真正執行的程式碼是驅動jar包的實現類。
快速入門
本人使用的是jdk10和mysql-connector-java-5.1.45-bin.jar。
步驟:
- 匯入驅動jar包
- 專案根目錄建立資料夾libs。
- 將mysql-connector-java-5.1.45-bin.jar複製到libs目錄下
- 右擊libs下,Add as Library將jar包匯入專案中
- 註冊驅動
- 獲取資料庫連線物件(Collection)
- 定義sql
- 獲取執行sql語句的物件 Statement
- 執行sql,接受返回結果(ResultSet)
- 處理結果
-
釋放資源
// 2. 註冊驅動 Class.forName("com.mysql.jdbc.Driver"); // 3. 獲取連線物件Collection Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "nrblwbb7"); // 4. 定義sql String sql = "UPDATE STUDENT SET AGE = 23 WHERE NAME = `王智`"; // 5. 獲取執行sql語句的物件 Statement Statement statement = conn.createStatement(); // 6. 執行sql,接受返回結果(ResultSet) int result = statement.executeUpdate(sql); // 7. 處理結果 System.out.println(result); // 8. 釋放資源 statement.close(); conn.close();
詳解各個物件
- DriverManager:驅動管理物件
- Connection:資料庫連線物件
- Statement:執行sql物件
- ResultSet:結果集物件
- PreparedStatement :執行sql物件
DriverManager
功能:
- 註冊驅動:告訴程式該使用哪一個資料庫驅動jar
- 使用的方法:static void registerDriver(Driver driver) :註冊與給定的驅動程式 DriverManager 。
- 寫程式碼使用:Class.forName(“com.mysql.jdbc.Driver”);
-
通過檢視原始碼發現:在com.mysql.jdbc.Driver類中存在靜態程式碼塊
static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can`t register driver!"); } }
注意:mysql5之後的驅動jar包可以省略註冊驅動的步驟。
- 獲取資料庫連線
- 方法:static Connection getConnection(String url, String user, String password)
- 引數:
- url:指定連線的路徑
- 語法:jdbc:mysql://ip地址(域名):埠號/資料庫名稱
- 例子:jdbc:mysql://localhost:3306/db
- 細節:如果連線的是本機mysql伺服器,並且mysql服務預設埠是3306,則url可以簡寫為:jdbc:mysql:///資料庫名稱
- jdbc是連線資料庫的協議,mysql是連線mysql資料庫的子協議
- user:使用者名稱
- password:密碼
- url:指定連線的路徑
Collection連線物件
功能:
- 獲取執行sql的物件
- Statement createStatement()
- PreparedStatement prepareStatement(String sql)
- 管理事務
- 開啟事務:setAutoCommit(boolean autoCommit) :呼叫該方法設定引數為false,即開啟事務
- 提交事務:commit()
- 回滾事務:rollback()
Statement執行靜態sql物件
功能:
- 執行sql
- boolean execute(String sql) :可以執行任意的sql (瞭解)
- int executeUpdate(String sql) :執行DML(insert、update、delete)語句、DDL(create,alter、drop)語句
- 返回值:影響的行數,可以通過這個影響的行數判斷DML語句是否執行成功 返回值>0的則執行成功,反之,則失敗。
- ResultSet executeQuery(String sql) :執行DQL(select)語句
插入舉例:
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
// 1. 載入驅動
Class.forName("com.mysql.jdbc.Driver");
// 2. 獲取連線物件
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3?useSSL=true", "root", "nrblwbb7");
// 3. 建立sql
String sql = "INSERT INTO ACCOUNT VALUES (NULL,`網址`,2000)";
// 4. 獲取執行sql物件
stmt = conn.createStatement();
// 5. 執行sql
int count = stmt.executeUpdate(sql);
// 6. 處理結果
System.out.println("影響的行數為:" + count);
if(count > 0){
System.out.println("插入成功");
}else{
System.out.println("操作失敗");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 7. 釋放資源
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
修改舉例:
public static void main(String[] args) {
/**
* 由於Connection和Statement實現了AutoCloseable介面,所以可以使用自動關閉
*/
try(
// 2.獲取Connection物件
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/db3?useSSL=true",
"root",
"nrblwbb7") ;
// 3. 獲取Statement物件
Statement stmt = conn.createStatement()
){
// 1.載入驅動
Class.forName("com.mysql.jdbc.Driver");
// 4. 建立sql
String sql = "UPDATE ACCOUNT SET BALANCE = 1500 WHERE NAME = `網址`";
// 5. 執行sql
int count = stmt.executeUpdate(sql);
// 6. 處理結果
System.out.println(count);
if(count > 0){
System.out.println("修改成功");
}else{
System.out.println("操作失敗或者記錄未更改");
}
// 7. 關閉連線,由於try..with..resource會自動關閉,所以無須擔心
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
刪除舉例:
public static void main(String[] args) {
/**
* 由於Connection和Statement實現了AutoCloseable介面,所以可以使用自動關閉
*/
try(
// 2.獲取Connection物件
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/db3?useSSL=true",
"root",
"nrblwbb7") ;
// 3. 獲取Statement物件
Statement stmt = conn.createStatement()
){
// 1.載入驅動
Class.forName("com.mysql.jdbc.Driver");
// 4. 建立sql
String sql = "DELETE FROM ACCOUNT WHERE NAME = `網址`";
// 5. 執行sql
int count = stmt.executeUpdate(sql);
// 6. 處理結果
System.out.println(count);
if(count > 0){
System.out.println("刪除成功");
}else{
System.out.println("刪除失敗");
}
// 7. 關閉連線,由於try..with..resource會自動關閉,所以無須擔心
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
ResultSet:結果及物件,封裝查詢結果
功能:
- 封裝查詢結果:
- boolean next(): 遊標向下移動一行,判斷當前行是否是最後一行末尾(是否有資料),如果是,則返回false,如果不是則返回true
- getXxx(引數):獲取資料
- Xxx:代表資料型別 如: int getInt() , String getString()
- 引數:
- int:代表列的編號,從1開始 如: getString(1)
- String:代表列名稱。 如: getDouble(“balance”)
- 注意:
- 使用步驟:
- 遊標向下移動一行
- 判斷是否有資料
- 獲取資料
- 使用步驟:
舉個簡單例子:
@Test
public void selectDemo(){
try {
// 1.載入驅動
Class.forName("com.mysql.jdbc.Driver");
// 2.建立sql語句
String sql = "select * from account";
try(
// 3.獲取連線物件
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/db3?useSSL=true",
"root",
"root");
// 4.獲取執行sql語句物件
Statement stmt = conn.createStatement()
){
// 5.執行sql
ResultSet rs = stmt.executeQuery(sql);
// 6.處理結果集
while(rs.next()){
// 獲取id
int id = rs.getInt("id");
// 獲取姓名
String name = rs.getString("name");
// 獲取賬戶餘額
double balance = rs.getDouble("balance");
// 正確的處理應該是封裝為物件,進行處理,這就簡單展示下
System.out.println("id = " + id + ", name = " + name + ", balance = " + balance);
}
} catch (SQLException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
PreparedStatement:執行動態sql的物件
功能:
- 執行動態的sql
- SQL隱碼攻擊問題:在拼接sql時,有一些sql的特殊關鍵字參與字串的拼接。會造成安全性問題
- 輸入使用者隨便,輸入密碼:a` or `a` = `a
- sql:select * from user where username = `fhdsjkf` and password = `a` or `a` = `a`
- 解決sql注入問題:使用PreparedStatement物件來解決
- 預編譯的SQL:引數使用?作為佔位符
- 步驟:
- 匯入驅動jar包 mysql-connector-java-5.1.45-bin.jar
- 註冊驅動
- 獲取資料庫連線物件 Connection
- 定義sql
- 注意:sql的引數使用?作為佔位符。 如:select * from user where username = ? and password = ?;
- 獲取執行sql語句的物件 PreparedStatement Connection.prepareStatement(String sql)
- 給?賦值:
- 方法: setXxx(引數1,引數2)
- 引數1:?的位置編號 從1 開始
- 引數2:?的值
- 方法: setXxx(引數1,引數2)
- 執行sql,接受返回結果,不需要傳遞sql語句
- 處理結果
- 釋放資源
- 注意:後期都會使用PreparedStatement來完成增刪改查的所有操作
- 可以防止SQL隱碼攻擊
- 效率更高
- SQL隱碼攻擊問題:在拼接sql時,有一些sql的特殊關鍵字參與字串的拼接。會造成安全性問題
封裝工具類
src下db.properties的資訊:
# 驅動
driver=com.mysql.jdbc.Driver
# url
url=jdbc:mysql://127.0.0.1:3306/db3?useSSL=true
# 使用者
username=root
# 密碼
password=root
封裝的工具類如下:
public class JDBCUtils {
private static String driver;
private static String url;
private static String username;
private static String password;
static{
try {
// 讀取配置檔案
InputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties pro = new Properties();
pro.load(inputStream);
// 靜態變數賦值
driver = pro.getProperty("driver");
url = pro.getProperty("url");
username = pro.getProperty("username");
password = pro.getProperty("password");
// 註冊驅動
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 獲取連線物件
* @return 返回連線物件
*/
public static Connection getConn() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
/**
* 關閉資源
* @param stmt
* @param conn
*/
public static void close(Statement stmt,Connection conn){
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 關閉資源
* @param rs
* @param stmt
* @param conn
*/
public static void close(ResultSet rs, Statement stmt, Connection conn){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
事務管理
- 事務:一個包含多個步驟的業務操作。如果這個業務操作被事務管理,則這多個步驟要麼同時成功,要麼同時失敗。
- 操作:
- 開啟事務
- 提交事務
- 回滾事務
- 使用Connection物件來管理事務
- 開啟事務:setAutoCommit(boolean autoCommit) :呼叫該方法設定引數為false,即開啟事務
- 在執行sql之前開啟事務
- 提交事務:commit()
- 當所有sql都執行完提交事務
- 回滾事務:rollback()
- 在catch中回滾事務
測試:
public static void main(String[] args) { Connection conn = null; PreparedStatement pstmt1 = null; PreparedStatement pstmt2 = null; try { // 1.建立sql,轉賬的sql String sql1 = "UPDATE ACCOUNT SET BALANCE = BALANCE - ? WHERE ID = ?"; String sql2 = "UPDATE ACCOUNT SET BALANCE = BALANCE + ? WHERE ID = ?"; // 2.獲取連線物件 conn = JDBCUtils.getConn(); // 開啟事務 conn.setAutoCommit(false); // 3.獲取執行sql物件,進行預編譯 pstmt1 = conn.prepareStatement(sql1); pstmt2 = conn.prepareStatement(sql2); // 4.替換佔位符 pstmt1.setDouble(1,500); pstmt1.setInt(2,1); pstmt2.setDouble(1,500); pstmt2.setInt(2,2); // 5.執行sql pstmt1.executeUpdate(); // int i = 3 / 0; pstmt2.executeUpdate(); // 提交事務 conn.commit(); } catch (Exception e) { if(conn != null) { try { // 事務回滾 conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } e.printStackTrace(); } finally { JDBCUtils.close(pstmt1,conn); JDBCUtils.close(pstmt2,null); } }
- 開啟事務:setAutoCommit(boolean autoCommit) :呼叫該方法設定引數為false,即開啟事務