有时我们使用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没有发生任何更新,在出现异常后回滚事务。