1、什么是事务?
以MySQL为例,看一下事务的概念。
MySQL 事务主要用于处理操作量大,复杂度高的数据。
不得不想一想,MySQL对事务处理的支持有条件限制吗?
在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
事务用来管理 insert,update,delete 语句
2、事务有啥特点?
一般来说,事务是必须满足4个条件(ACID)::原子性(Atomicity,或称不可分割性)、一致性
(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
那么这句话是什么意思呢?(还是得自行理解o﹏o,解释性的内容放在下面了)
【1】原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
【2】一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
【3】隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
【4】持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
3、举个栗子???
还是举个栗子吧。
最常见的事务处理的例子就是:A通过手机银行执行转账操作,想要将卡a中的2000元转到卡b,那么在MySQL数据库层面用SQL语句操作时,通常是这样的,
//1-更新银行卡a的余额:减少2000元
update table a set balance=XXX where adminname=XXX
//2-更新银行卡b的余额:增加2000元
update table b set balance=XXX where adminname=XXX
再考虑一下如何用Java代码操作。通常,用JDBC来实现数据库操作的步骤通常是,
/**(这里咱假设已经有了JDBCUtils工具类,基础的就不写了...)**/
//给定SQL语句
String sql1="update table a set balance=XXX where adminname=XXX"
String sql2="update table b set balance=XXX where adminname=XXX"
//声明变量
Connection connection=null;
//考虑到要执行两个sql语句,这里就要创建两个Statement实例
PrepareStatement statement1=null;//用于执行sql1
PrepareStatement statement2=null;//用于执行sql2
//SQL异常处理
try{
//1-获取数据库连接
connection= JDBCUtils.getConnection();
//2-构建Statement实例
statement1 = connection.prepareStatement(sql1);
statement2 = connection.prepareStatement(sql2);
//3-执行更新操作
statement1.executeUpdate();
statement2.executeUpdate();
}catche(Exception e){
//遇到异常,一通操作???
} finally{
//数据读写完毕,一通操作(释放资源)
JDBCUtils.close(statement1,null);
JDBCUtils.close(statement2,connection);
}
若是采用上述操作的话,就会出现一个问题,一旦上述步骤在某个地方抛出了异常,是无法确定整个转账的步骤执行了多少的。
【1】最理想的情况 (*^▽^*)
没有抛出异常,转账成功,皆大欢喜
【2】不太理想的情况 (* ̄︶ ̄)
statement1抛出了异常:卡a的钱没有扣除,卡b上的钱也没有增加
好像是————转个了寂寞
【3】最不理想的情况 o(╥﹏╥)o
statement2抛出了异常:意味着statement1执行成功,也就是:扣除了卡a的钱,但是卡b上余额并没有被修改
约等于————白白损失了2000块作为系统测试的费用了
4、事务处理
这时候,我们就想起了事务处理的概念,它的四个特点——原子性、一致性、隔离性、持久性,简直就是为上面的这种情况量身定做的(实际上也确实是)。
使用JDBC进行事务处理,基本步骤就变成了…
先来定义两个静态方法来分别进行卡a的扣费处理、卡b的增加余额处理。
//卡a的扣费处理
public static void updateA(Connection connection){
String sql1="update table a set balance=XXX where adminname=XXX"
PreparedStatement statement = null;
try {
statement = connection.prepareStatement(sql1);
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (statement!=null)
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//卡b的增加余额处理
public static void updateB(Connection connection){
String sql2="update table b set balance=XXX where adminname=XXX"
PreparedStatement statement = null;
try {
statement = connection.prepareStatement(sql2);
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (statement!=null)
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
然后,我们通过Connection实例开启事务处理,分情况执行事务的提交(自然是转账顺利的时候)、回滚操作(转账不太顺利或者转了个寂寞的时候)。
// 执行事务处理
public static void executeTranscation(){
Connection connection=null;
try {
connection=JDBCUtils.getConnection();
//开启事务处理
connection.setAutoCommit(false);
//卡a的扣费处理
updateA(connection);
//卡b的增加余额处理
updateB(connection);
//提交事务——(* ̄︶ ̄),没报错,提交事务
connection.commit();
} catch (SQLException e) {
//转账失败,执行事务回滚(•́へ•́╬)
try {
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
//释放资源
JDBCUtils.close(null,connection);
}
}
好了,这下银行卡转账就相对比较安全了。