- 原子性(Atomicity): 事務中所有操作是不可再分割的原子單位。事務中所有操作要嘛全部執行成功,要嘛全部執行失敗。
- 一致性(Consistency): 事務執行後,數據庫狀態與其他業務規則保持一致。如轉帳業務,無論事務執行成功與否,餐與轉帳的兩個帳號餘額之河應該是不變得。
- 隔離性(Isolation): 隔離性是指在併發操作中,不同事務之間應該隔離開來,使每個併發中的事務不會相互干擾。
- 持久性(Durability): 一旦事務提交成功,事務中所有的數據操作都必須被持久化到數據庫中,即使提交事務後,數據庫馬上崩潰,再數據庫重啟時,也必須能保證通過某種機制恢復數據。
MySQL 每執行一條SQL語句,都是一個單獨的事務。如果需要再一個事務中包含多條 SQL 語句,那麼需要開啟事務和結束事務。
- 開啟事務: start transaction;
- 結束事務: commit 和 rollback;
- commit 表示提交,即事務中的多條 SQL 語句所做出的影響會持久化到數據庫中。
- rollback: 表示回滾,即回滾到事務的起點,之前做的所有操作都被撤銷。
在 jdbc 中處理事務,都是通過 Connection 完成。 同一事務中所有的操作,都在使用同一個 Connection 對象。 Connection 的三個方法與事務相關:
- setAutoCommit(boolean): 設置是否為自動提交事務,如果 true(默認值就是 true) 表示自動提交(代表沒有事務),也就是每條執行的 SQL 語句都是一個單獨的事務,如果設置 false, 那麼就相當於開啟事務。con.setAutoCommit(false) 表示開啟事務。
- commit(): 提交結束事務。con.commit() 表示提交事務。
- rollback(): 回滾結束事務。con.rollback() 表示回滾事務。
- jdbc 處理事務的代碼格式:
try {
con.setAutoCommit(false); //開啟事務
...
...
con.commit(); //try 的最後提交事務
} catch(Exception e) {
con.rollback(); //回滾事務
}
//AccountDao.java
import java.sql.Connection;
import java.sql.PreparedStatement;
import demo3.JdbcUtils;
public class AccountDao {
/**
* 修改指定用戶的餘額
* @param name
* @param balance
*/
public void updateBalance(Connection con, String name, double balance) {
try {
/*
* 1. 給出 sql 模板,創建 pstmt
*/
String sql = "UPDATE account SET balance=balance+? where name=?";
PreparedStatement pstmt = con.prepareStatement(sql);
/*
* 2. 對參數進行賦值
*/
pstmt.setDouble(1, balance);
pstmt.setString(2, name);
/*
* 3. 執行
*/
pstmt.executeUpdate();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
//Demo.java
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Test;
import demo3.JdbcUtils;
/**
* 演示轉帳
* @author jack870131
*
* 所有對 Connection 的操作都在 service 層處理
*/
public class Demo6 {
/**
* 轉帳
* @param from
* @param to
* @param money
* @throws SQLException
*/
public void transfer(String from, String to, double money) throws SQLException {
//對事務的操作必須使用 Connection 對象
Connection con = null;
try {
con = JdbcUtils.getConnection();
//開啟事務
con.setAutoCommit(false);
AccountDao dao = new AccountDao();
dao.updateBalance(con, from, -money); //給 from 減去相應金額
if(true) {
throw new RuntimeException();
}
dao.updateBalance(con, to, +money); //給 to 加上相應金額
//提交事務
con.commit();
con.close();
} catch(Exception e) {
//回滾事務
try {
con.rollback();
con.close();
} catch (SQLException e1) {
throw new RuntimeException(e);
}
}
}
@Test
public void fun1() throws SQLException {
transfer("jack", "vivian", 100);
}
}