1.银行转账案例
案例:银行转账:从张无忌账户上给赵敏转1000块钱.
准备:account(账户表):
id name(账号,唯一) balance(余额)
1 张无忌 20000
2 赵敏 0
转账操作步骤:
①:查询张无忌的账户余额是否大于等于1000.
SELECT * FROM account WHERE name = '王健林' AND balance >= 100;
余额小于1000 : 温馨提示:亲,你的余额不足.
余额大于等于1000: GOTO 2;
②:从王健林的账户余额中减少100.
UPDATE account SET balance = balance - 1000 WHERE name = '王健林';
③:在马云的账户余额中增加100.
UPDATE account SET balance = balance + 1000 WHERE name = '马云';
public class TransactionTest extends TestCase {
// 案例:银行转账:从王健林账户上转100个亿给马云
public void testTranscation() throws Exception {
// 1.查询王健林账户的余额是否大约100亿
String sql = "SELECT * FROM t_account WHERE name =? AND balance >= ?";
Connection conn = JdbcUtil.getConn();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "王健林");
ps.setInt(2, 2000);
ResultSet rs = ps.executeQuery();
int wBalance = 0;
if (rs.next()) {
wBalance = rs.getInt("balance");
System.out.println(rs.getString("name") + "还有" + wBalance + "的余额");
} else {
throw new RuntimeException("余额不足");
}
// 2.从王健林的账户中余额减少100
sql = "UPDATE t_account SET balance= ? WHERE name= ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, wBalance - 100);
ps.setString(2, "王健林");
ps.executeUpdate();
// 3.查询马云的余额
int mBalance = 0;
sql = "SELECT * FROM t_account WHERE name =?";
ps = conn.prepareStatement(sql);
ps.setString(1, "马云");
rs = ps.executeQuery();
if (rs.next()) {
mBalance = rs.getInt("balance");
System.out.println(rs.getString("name") + "还有" + mBalance + "的余额");
} else {
throw new RuntimeException("没有马云账户");
}
// 4.给马云的账户余额中增加100
sql = "UPDATE t_account SET balance= ? WHERE name= ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, mBalance + 100);
ps.setString(2, "马云");
ps.executeUpdate();
// 关闭资源
JdbcUtil.close(conn, ps, rs);
}
}
悲剧的事情来了:当程序执行到第②步和第③步之间,突然停电了.
使用异常来模拟停电:System.out.println(1/0);
2.JDBC的事务操作
事务(Transaction,简写为tx):
-
在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另一种状态。
-
为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:
(1)当每个逻辑操作单元全部完成时,数据的一致性可以保持;
(2)而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。 -
事务的操作:先定义开始一个事务,然后对数据作修改操作,这时如果提交(commit),这些修改就永久地保存下来,如果回退(rollback),数据库管理系统将放弃您所作的所有修改而回到开始事务时的状态。
-
事务:指构成单个逻辑工作单元的操作集合。
-
事务处理:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),要么整个事务回滚(rollback)到最初状态。
简单讲:事务其实就是多个操作,把多个操作看成是一个不可分割的整体,整体中的多个要成功都成功,要失败都失败。
在红楼梦中: 一损俱损,就是这个思想.
事务的ACID属性:
1. 原子性(Atomicity):原子在化学中,是最小单位,不可以再分割了。
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
2. 一致性(Consistency):包装数据的完整性。
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被破坏)
3. 隔离性(Isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
4. 持久性(Durability):
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
事务的操作:
事务成功: 提交事务(commit),如果事务不提交,在数据库中数据永远都不会改变;
事务失败: 出现异常的时候,事务失败.事务回滚:rollback(取消之前所有的操作,回到事务最初的状态),释放锁资源。
操作事务的模板:
try{
取消事务的自动提交机制,设置为手动提交. connection对象.setAutoCommit(false);
操作1
操作2
异常
操作3
....
手动提交事务 connection对象.commit();
}catch(Exception e){
//处理异常
回滚事务 connection对象.rollback();
}
上面案例的事务处理
public void testTranscation() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConn();
conn.setAutoCommit(false);// 取消事务的自动提交机制
String sql = "SELECT * FROM t_account WHERE name =? AND balance >= ?";
ps = conn.prepareStatement(sql);
ps.setString(1, "王健林");
ps.setInt(2, 2000);
rs = ps.executeQuery();
int wBalance = 0;
if (rs.next()) {
wBalance = rs.getInt("balance");
System.out.println(rs.getString("name") + "还有" + wBalance + "的余额");
} else {
throw new RuntimeException("余额不足");
}
// 2.从王健林的账户中余额减少100
sql = "UPDATE t_account SET balance= ? WHERE name= ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, wBalance - 100);
ps.setString(2, "王健林");
ps.executeUpdate();
// 3.查询马云的余额
int mBalance = 0;
sql = "SELECT * FROM t_account WHERE name =?";
ps = conn.prepareStatement(sql);
ps.setString(1, "马云");
rs = ps.executeQuery();
if (rs.next()) {
mBalance = rs.getInt("balance");
System.out.println(rs.getString("name") + "还有" + mBalance + "的余额");
} else {
throw new RuntimeException("没有马云账户");
}
System.out.println(1 / 0); // 模拟停电
// 4.给马云的账户余额中增加100
sql = "UPDATE t_account SET balance= ? WHERE name= ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, mBalance + 100);
ps.setString(2, "马云");
ps.executeUpdate();
conn.commit();//提交事务
System.out.println("转账成功");
} catch (Exception e) {
try {
conn.rollback(); //事务回滚
System.out.println("转账失败");
} catch (Exception e2) {
e2.printStackTrace();
}
} finally {
// 关闭资源
JdbcUtil.close(conn, ps, rs);
}
}
事务相关的:
- 1.默认情况下,事务在执行完DML操作就自动提交.
- 2.查询操作,其实是不需要事务的.但是,一般的,我们在开发中都把查询放入事务中.
- 3.开发中,代码完全正确,没有异常,但是就是数据库中数据不变.
意识:没有提交事务. - 4.在MySQL中,只有InnoDB存储引擎支持事务,支持外键,MyISAM不支持事务.
- 5.以后事务我们不应该在DAO层处理,应该在service层控制.
- 6.事务在讲解Hibernate,MyBatis,Spring,项目的时候都会再讲.
网友评论