JavaEE學習日誌持續更新----> 必看!JavaEE學習路線(文章總彙)
Java學習日誌(三十五)
事務
事務概述
事務指的是邏輯上的一組操作,組成這組操作的各個單元要麼全都成功,要麼全都失敗。
事務作用:保證在一個事務(一個connection)中多次SQL操作要麼全都成功,要麼全都失敗。 防止出現轉賬吞錢的現象。
數據庫有事務概念,執行sql語句,就會開啓事務,執行成功會提交事務,執行失敗會回滾事務:
- mysql數據庫事務默認都是自動的,自動開啓事務,自動提交事務,自動回滾事務
- oracle數據庫事務默認都是手動的,手動開啓事務,手動提交事務,手動回滾事務
注意:事務一旦結束(提交,回滾),數據就永久保存在數據庫
在Connection接口中,有操作事務的方法:
-
void setAutoCommit(boolean autoCommit)
將此連接的自動提交模式設置爲給定狀態。參數: autoCommit:true啓用自動提交模式(默認); false禁用自動提交模式,(執行sql語句之前)手動開啓事務。
-
void commit()
一組sql一句都執行成功,提交事務 -
void rollback()
一組sql中有一條執行失敗,回滾事務;把數據回滾到數據開啓之前
使用原生JDBC完成轉賬案例
首先,創建一張賬戶表
# 創建一個表:賬戶表.
CREATE DATABASE day05;
# 使用數據庫
USE day05;
# 創建賬號表
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
money DOUBLE
);
# 初始化數據
INSERT INTO account VALUES (NULL,'jack',10000);
INSERT INTO account VALUES (NULL,'rose',10000);
INSERT INTO account VALUES (NULL,'tom',10000);
代碼示例:使用原生JDBC完成轉賬案例
public class Demo01JDBC {
public static void main(String[] args) {
Connection conn = null;
Statement stat = null;
try {
//註冊驅動
Class.forName("com.mysql.jdbc.Driver");
//獲取數據庫連接對象Connection
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day05", "root", "root");
//開啓事務
conn.setAutoCommit(false);
//獲取執行者對象Statement
stat = conn.createStatement();
//執行sql語句獲取結果
int row1 = stat.executeUpdate("UPDATE account SET money=money-1000 where NAME='jack';");
System.out.println(0/0);
int row2 = stat.executeUpdate("UPDATE account SET money=money+1000 where NAME='rose';");
//處理結果
if (row1 > 0 && row2 > 0) {
System.out.println("轉賬成功");
//提交事務
conn.commit();
}
} catch (Exception e) {
System.out.println("轉賬失敗");
e.printStackTrace();
/*
回滾事務
*/
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}finally {
//釋放資源
if(stat!=null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
使用DbUtils和C3P0完成轉賬案例
QueryRunner的構造方法:
QueryRunner()
:空參構造,調用update/query方法執行sql語句時,必須傳遞Connection對象。QueryRunner(DataSource ds)
:帶連接池的構造方法,看不到Connection,也就不能操作事務。
QueryRunner的成員方法:用於執行增刪改的update方法。
int update(Connection conn,String sql,Object... params)
空參構造使用。
代碼示例:使用DBUtils+C3P0連接池完成轉賬案例
public class Demo02DBUtils {
public static void main(String[] args) {
//1.創建QueryRunner對象
QueryRunner qr = new QueryRunner();
//2.使用C3P0連接池獲取Connection
Connection conn = C3P0UtilsXML.getConnection();
//3.使用QueryRunner對象中的方法update
String sql1 = "UPDATE account SET money=money-1000 where NAME='jack';";
String sql2 = "UPDATE account SET money=money+1000 where NAME='rose';";
try {
//開啓事務
conn.setAutoCommit(false);
int row1 = qr.update(conn, sql1);
int row2 = qr.update(conn, sql2);
//4.處理結果
if (row1 > 0 && row2 > 0) {
System.out.println("轉賬成功");
//提交事務
conn.commit();
}
} catch (Exception e) {
System.out.println("轉賬失敗");
e.printStackTrace();
//回滾事務
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
//釋放資源
DbUtils.closeQuietly(conn);
}
}
}
三層思想
三層思想概述
三層思想轉賬案例_dao層
創建轉賬案例的Dao層:用於對account表進行增刪改查
注意:
一張表–>一個dao
定義兩個方法:
一個減錢,一個加錢
代碼示例:dao層
public class AccountDao {
/*
定義減錢方法
參數:
Connection conn:兩個方法使用同一個Connection,從而保證使用同一個事務
String fromName:付款人姓名
double money:轉賬金額
返回值:
int:影響數據庫的有效行數
String sql1 = "UPDATE account SET money=money-1000 where NAME='jack';";
String sql2 = "UPDATE account SET money=money+1000 where NAME='rose';";
*/
public int fromAccount(Connection conn, String fromName, double money) throws SQLException {
//創建QueryRunner對象
QueryRunner qr = new QueryRunner();
//調用update方法執行sql語句,接收結果
int row = qr.update(conn,"UPDATE account SET money=money-? where NAME=?;",money,fromName);
//返回方法
return row;
}
/*
定義加錢方法
參數:
Connection conn:兩個方法使用同一個Connection,從而保證使用同一個事務
String toName:收款人姓名
double money:轉賬金額
返回值:
int:影響數據庫的有效行數
String sql1 = "UPDATE account SET money=money-1000 where NAME='jack';";
String sql2 = "UPDATE account SET money=money+1000 where NAME='rose';";
*/
public int toAccount(Connection conn, String toName, double money) throws SQLException {
//創建QueryRunner對象
QueryRunner qr = new QueryRunner();
//調用update方法執行sql語句,接收結果
int row = qr.update(conn,"UPDATE account SET money=money+? where NAME=?;",money,toName);
//返回方法
return row;
}
}
三層思想轉賬案例_service層
轉賬案例的Service層:接收web層傳遞的數據,調用dao層的方法,接收結果;把結果返回給web層
定義一個轉賬方法:
- 參數接收web傳遞的數據(付款人姓名,收款人姓名,轉賬金額)
- 使用C3P0連接池,獲取Connection
- 開啓事務
- 創建AccountDao對象,調用減錢價錢方法,接收結果
- 對結果進行判斷
- 執行成功,提交事務;有異常回滾事務
- 把結果返回給web層
- 釋放資源
代碼示例:service層
public class AccountService {
//定義一個轉賬方法
public boolean transferAccount(String fromName, String toName, double money) {
//使用C3P0連接池,獲取Connection
Connection conn = C3P0UtilsXML.getConnection();
//定義返回的結果
boolean flag = false;
try {
//開啓事務
conn.setAutoCommit(false);
//創建AccountDao對象
AccountDao dao = new AccountDao();
//調用減錢價錢方法,接收結果
int row1 = dao.fromAccount(conn, fromName, money);
int row2 = dao.toAccount(conn, toName, money);
//對結果進行判斷
if (row1>0&&row2>0){
flag = true;
//執行成功,提交事務
conn.commit();
}
} catch (Exception e) {
e.printStackTrace();
//有異常回滾事務
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}finally {
//資源釋放
DbUtils.closeQuietly(conn);
}
//把結果返回給web層
return flag;
}
}
三層思想轉賬案例_web層
創建轉賬案例的web層
- 使用Scanner獲取用戶輸入的數據(付款人姓名,收款人姓名,轉賬金額)
- 創建AccountService對象
- 調用轉賬方法,接收轉賬結果
- 對結果進行判斷,給用戶展示結果
代碼示例:web層
public class AccountWeb {
public static void main(String[] args) {
//使用Scanner獲取用戶輸入的數據(付款人姓名,收款人姓名,轉賬金額)
Scanner sc = new Scanner(System.in);
System.out.print("請輸入付款人姓名:");
String fromName = sc.next();
System.out.print("請輸入收款人姓名:");
String toName = sc.next();
System.out.print("請輸入轉賬金額:");
double money = sc.nextDouble();
//創建AccountService對象
AccountService service = new AccountService();
//調用轉賬方法,接收轉賬結果
boolean b = service.transferAccount(fromName, toName, money);
//對結果進行判斷,給用戶展示結果
if (b) {
System.out.println("轉賬成功");
} else {
System.out.println("轉賬失敗");
}
}
}
轉賬結果: