有時我們使用JDBC操作數據庫時,會出現同時執行兩條Insert或update或delete之類的語句。
例如:有兩個人Martin和John,Martin有1000元,John有700元。假設某天Martin要轉100元給John,那麼數據庫應該會這樣執行:
Update Martin set monry = money -100 ;
Update John set money = money +100 ;
當兩個語句都執行了,纔可以算完成一個轉賬。否則其中一條語句沒執行,將會出現嚴重後果。而事物就是在這種背景下使用。
事務應該具有4個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱爲ACID特性。
原子性(atomicity)。一個事務是一個不可分割的工作單位,事務中包括的諸操作要麼都做,要麼都不做。
一致性(consistency)。事務必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。
隔離性(isolation)。一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。
持久性(durability)。持久性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。
下面就模仿事物中的原子性:SQL語句要麼全部執行,要麼全部都不執行。
下面模擬:
1,以上面的例子建立數據庫:
create table bank(
id int primary key auto_increment ,
name varchar(20) not null ,
money double not null default 0.00
) ;
insert into bank (name ,money) values("Martin" , 1000) ;
insert into bank (name ,money) values("John" , 700) ;
(圖1)
2,我們模擬在update語句執行的過程中遇到了異常,這個異常可能是斷電,數據庫出現異常或自然災害等。我們用1/0這個異常來代替
public class JDBC {
public static void main(String[] args) {
Connection conn = null ;
Statement stat = null ;
try {
Class.forName("com.mysql.jdbc.Driver") ;
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc" , "root","jian") ;
String sql1 = "update bank set money=money-100 where name='Martin'" ;
String sql2 = "update bank set money=money-100 where name='John'" ;
stat = conn.createStatement() ;
stat.executeUpdate(sql1) ;
//模擬異常出現
int x = 1/0 ;
stat.executeUpdate(sql2) ;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally{
if(null!=conn){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null ;
}
if(null!=stat){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
stat = null ;
}
}
}
}
沒有事物,相對於圖1發生了更新的異常
(圖2)
3,使用了事務
public class JDBC {
public static void main(String[] args) {
Connection conn = null ;
Statement stat = null ;
try {
Class.forName("com.mysql.jdbc.Driver") ;
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc" , "root","jian") ;
String sql1 = "update bank set money=money-100 where name='Martin'" ;
String sql2 = "update bank set money=money-100 where name='John'" ;
//設置不主動提交事務
conn.setAutoCommit(false);
stat = conn.createStatement() ;
stat.executeUpdate(sql1) ;
//模擬異常出現
int x = 1/0 ;
stat.executeUpdate(sql2) ;
//當兩個update都完成了,在提交事務
conn.commit();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch(ArithmeticException e){
//出現異常則回滾事務
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
finally{
if(null!=conn){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null ;
}
if(null!=stat){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
stat = null ;
}
}
}
}
(圖3)
當使用了事務後,結果如圖3所示,相對於圖2沒有發生任何更新,在出現異常後回滾事務。