一、環境搭建(復習)

- 首先建立lib目錄然后要把對應的jar包導進來
- 然后就是jdbc.properties檔案
user=root
password=123456
url=jdbc:mysql://localhost:3306/test
driverClass=com.mysql.jdbc.Driver
- 然后就是資料庫的工具類了JDBCUtils
package com.md.util;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtils {
/**
* 獲取資料庫的連接
* @return
* @throws Exception
*/
public static Connection getConnection() throws Exception {
//1.加載組態檔
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
//2.讀取配置資訊
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
//3.加載驅動
Class.forName(driverClass);
//4.獲取連接
Connection conn = DriverManager.getConnection(url,user,password);
return conn;
}
/**
* 關閉資源
* @param conn
* @param ps
* @return
*/
public static void closeResource(Connection conn , PreparedStatement ps) {
try {
if(ps != null)
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(conn != null)
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 關閉資源 , 有結果集呼叫這個
* @param conn
* @param ps
* @param rs
*/
public static void closeResource(Connection conn , PreparedStatement ps , ResultSet rs) {
try {
if(ps != null)
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(conn != null)
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(rs != null)
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
- 測驗連接是否成功
二、資料庫事務介紹
- 事務:一組邏輯操作單元,使資料從一種狀態變換到另一種狀態,
- 事務處理(事務操作):保證所有事務都作為一個作業單元來執行,即使出現了故障,都不能改變這種執行方式,當在一個事務中執行多個操作時,要么所有的事務都被提交(commit),那么這些修改就永久地保存下來;要么資料庫管理系統將放棄所作的所有修改,整個事務回滾(rollback)到最初狀態,
- 為確保資料庫中資料的一致性,資料的操縱應當是離散的成組的邏輯單元:當它全部完成時,資料的一致性可以保持,而當這個單元中的一部分操作失敗,整個事務應全部視為錯誤,所有從起始點以后的操作應全部回退到開始狀態
三、JDBC事務處理
-
資料一旦提交,就不可回滾,
-
資料什么時候意味著提交?
- 當一個連接物件被創建時,默認情況下是自動提交事務:每次執行一個 SQL 陳述句時,如果執行成功,就會向資料庫自動提交,而不能回滾,
- 關閉資料庫連接,資料就會自動的提交,如果多個操作,每個操作使用的是自己單獨的連接,則無法保證事務,即同一個事務的多個操作必須在同一個連接下,
-
JDBC程式中為了讓多個 SQL 陳述句作為一個事務執行:
- 呼叫 Connection 物件的 setAutoCommit(false); 以取消自動提交事務
- 在所有的 SQL 陳述句都成功執行后,呼叫 commit(); 方法提交事務
- 在出現例外時,呼叫 rollback(); 方法回滾事務
若此時 Connection 沒有被關閉,還可能被重復使用,則需要恢復其自動提交狀態 setAutoCommit(true),尤其是在使用資料庫連接池技術時,執行close()方法前,建議恢復自動提交狀態,
資料定義:DDL:用于定義SQL模式、基本表、視圖和索引的創建和撤消操作,CREATE ALTER DROP TRUNCATE COMMENT RENAME,
資料操縱:DML:資料操縱分成資料查詢和資料更新兩類,資料更新又分成插入、洗掉、和修改三種操作
DDL操作一旦執行,都會自動提交,即使setAutoCommit(false)也無效
DML設定setAutoCommit(false)可以取消自動提交事務
1. 案例:
用戶AA向用戶BB轉賬100
普通版:
@Test
public void testUpdate() {
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(sql1,"AA");
// 模擬網路例外
System.out.println( 10 / 0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(sql2,"BB");
}
// 通用的增刪改
public void update(String sql,Object ... args){
Connection conn = null;
PreparedStatement ps = null;
try {
//1.獲取資料庫的連接
conn = JDBCUtils.getConnection();
//2.獲取PreparedStatement的實體 (或:預編譯sql陳述句)
ps = conn.prepareStatement(sql);
//3.填充占位符,sql的下標從1開始,而陣列下標從0開始
for(int i = 0;i < args.length;i++){
ps.setObject(i + 1, args[i]);
}
//4.執行sql陳述句
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}finally{
//5.關閉資源
JDBCUtils.closeResource(conn, ps);
}
}
此時AA賬號就少了100,而BB賬戶也沒有多,所以
2. 升級版(重點):
考慮到事務的操作
此時要避免上面的情況,
- 由于我們平時使用的DML操作,所以DDL不用考慮
- 連接物件創建時,呼叫 Connection 物件的 setAutoCommit(false)取消自動提交事務,以及后面執行完sql提交事務commit(),和出現例外時回滾rollback()
- 最后重要的是,之前我們寫的每執行一條sql都建立資料庫連接以及關閉資料庫連接,這樣每執行一條sql都自動提交了,所以執行時只有建立一次資料庫連接才行
@Test
public void testUpdate(){
Connection conn = null;
try {
// 0. 獲取資料庫連接
conn = JDBCUtils.getConnection();
// 1. 取消資料的自動提交
conn.setAutoCommit(false);
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(conn,sql1,"AA");
// 模擬網路例外
System.out.println( 10 / 0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn,sql2,"BB");
System.out.println("轉賬成功");
// 2. 提交資料
conn.commit();
} catch (Exception e) {
e.printStackTrace();
// 3. 執行出現例外,就回滾
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally {
// 只有整個操作成功之后才關閉資料庫的連接
JDBCUtils.closeResource(conn, null);
}
}
// 升級版,主要修改一下方面
// 1. conn從外面傳進來
// 2. 關閉時不關閉conn連接
public int update(Connection conn,String sql,Object ... args){
PreparedStatement ps = null;
try {
//1.獲取PreparedStatement的實體 (或:預編譯sql陳述句)
ps = conn.prepareStatement(sql);
//2.填充占位符,sql的下標從1開始,而陣列下標從0開始
for(int i = 0;i < args.length;i++){
ps.setObject(i + 1, args[i]);
}
//3.執行sql陳述句,回傳影響行數
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
//4.關閉資源,此時沒有關閉資料庫連接,保證使用的是只有一個連接
JDBCUtils.closeResource(null, ps);
}
return 0;
}
此時就解決了上面的問題,即使中間出現例外,不會出現少錢的情況
四、事務的ACID屬性
- 原子性(Atomicity)
原子性是指事務是一個不可分割的作業單位,事務中的操作要么都發生,要么都不發生, - 一致性(Consistency)
事務必須使資料庫從一個一致性狀態變換到另外一個一致性狀態, - 隔離性(Isolation)
事務的隔離性是指一個事務的執行不能被其他事務干擾,即一個事務內部的操作及使用的資料對并發的其他事務是隔離的,并發執行的各個事務之間不能互相干擾, - 持久性(Durability)
持久性是指一個事務一旦被提交,它對資料庫中資料的改變就是永久性的,接下來的其他操作和資料庫故障不應該對其有任何影響
五、資料庫的并發問題
- 對于同時運行的多個事務, 當這些事務訪問資料庫中相同的資料時, 如果沒有采取必要的隔離機制, 就會導致各種并發問題:
- 臟讀: 對于兩個事務 T1, T2, T1 讀取了已經被 T2 更新但還沒有被提交的欄位,之后, 若 T2 回滾, T1讀取的內容就是臨時且無效的,
- 不可重復讀: 對于兩個事務T1, T2, T1 讀取了一個欄位, 然后 T2 更新了該欄位,之后, T1再次讀取同一個欄位, 值就不同了,
- 幻讀: 對于兩個事務T1, T2, T1 從一個表中讀取了一個欄位, 然后 T2 在該表中插入了一些新的行,之后, 如果 T1 再次讀取同一個表, 就會多出幾行,
- 資料庫事務的隔離性: 資料庫系統必須具有隔離并發運行各個事務的能力, 使它們不會相互影響, 避免各種并發問題,
- 一個事務與其他事務隔離的程度稱為隔離級別,資料庫規定了多種事務隔離級別, 不同隔離級別對應不同的干擾程度, 隔離級別越高, 資料一致性就越好, 但并發性越弱,
六、四種隔離級別
- 資料庫提供的4種事務隔離級別:

- Oracle 支持的 2 種事務隔離級別:READ COMMITED, SERIALIZABLE, Oracle 默認的事務隔離級別為: READ COMMITED ,
- Mysql 支持 4 種事務隔離級別,Mysql 默認的事務隔離級別為: REPEATABLE READ,
七、在MySql中設定隔離級別
-
每啟動一個 mysql 程式, 就會獲得一個單獨的資料庫連接. 每個資料庫連接都有一個全域變數 @@tx_isolation, 表示當前的事務隔離級別,
-
查看當前的隔離級別:
SELECT @@tx_isolation; -
設定當前 mySQL 連接的隔離級別:
set transaction isolation level read committed; -
設定資料庫系統的全域的隔離級別:
set global transaction isolation level read committed; -
補充操作:
-
創建mysql資料庫用戶:
create user tom identified by 'abc123'; -
授予權限
#授予通過網路方式登錄的tom用戶,對所有庫所有表的全部權限,密碼設為abc123. grant all privileges on *.* to tom@'%' identified by 'abc123'; #給tom用戶使用本地命令列方式,授予atguigudb這個庫下的所有表的插刪改查的權限, grant select,insert,delete,update on atguigudb.* to tom@localhost identified by 'abc123';
-
八、升級版的通用方法
考慮到了事務
1. 查詢一
主要修改了Connection conn是從外面傳進來的,而且關閉時沒有關閉conn
那個回傳集合的也是這樣的修改
// 通用的針對于不同表的查詢:回傳一個物件 (version 2.0)
public <T> T getInstance(Connection conn,Class<T> clazz, String sql, Object... args) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 2.預編譯sql陳述句,得到PreparedStatement物件
ps = conn.prepareStatement(sql);
// 3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
// 4.執行executeQuery(),得到結果集:ResultSet
rs = ps.executeQuery();
// 5.得到結果集的元資料:ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
// 6.1通過ResultSetMetaData得到columnCount,columnLabel;通過ResultSet得到列值
int columnCount = rsmd.getColumnCount(); // 得到列數
if (rs.next()) {
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {// 遍歷每一個列
// 獲取列值,相當于值
Object columnVal = rs.getObject(i + 1);
// 獲取列的別名:列的別名,使用類的屬性名充當,相當于屬性
String columnLabel = rsmd.getColumnLabel(i + 1);
// 6.2使用反射,給物件的相應屬性賦值
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnVal);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 7.關閉資源
JDBCUtils.closeResource(null, ps, rs);
}
return null;
}
2. 增刪改
public int update(Connection conn,String sql,Object ... args){
PreparedStatement ps = null;
try {
//1.獲取PreparedStatement的實體 (或:預編譯sql陳述句)
ps = conn.prepareStatement(sql);
//2.填充占位符,sql的下標從1開始,而陣列下標從0開始
for(int i = 0;i < args.length;i++){
ps.setObject(i + 1, args[i]);
}
//3.執行sql陳述句,回傳影響行數
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
//4.關閉資源,此時沒有關閉資料庫連接,保證使用的是只有一個連接
JDBCUtils.closeResource(null, ps);
}
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/114390.html
標籤:Java
上一篇:雙劍合璧Nacos結合Sentinel實作流量安全控制(一):Sentinel是什么?Sentinel核心庫和控制臺
