案例:银行转账:从张无忌账户上给赵敏转1000块。 准备:account(账户表): --------------------------------------------------------------- id name(账号,唯一) balance(余额) 1 张无忌 20000 2 赵敏 0 --------------------------------------------------------------- 操作步骤: 1):检查张无忌的账户余额是否大于等于1000. SELECT * FROM account WHERE name = '张无忌' AND balance >= 1000; 2):从张无忌的账户余额中减少1000. UPDATE account SET balance = balance - 1000 WHERE name = '张无忌'; 3):再在赵敏的账户余额中增加1000. UPDATE account SET balance = balance + 1000 WHERE name = ' 赵敏'; 如果:在第二步和第三步之间如果程序中断,怎么办? (通过异常来模拟)
代码演示: public void test1(){
String sql = "select * from account where name=? AND balance>?";
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","rootroot");
System.out.println(conn);
//1.查询张无忌账户余额是否大于1000
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "张无忌");
ps.setDouble(2, 1000.0);
ResultSet rs = ps.executeQuery();
if(!rs.next()){
throw new RuntimeException("账户余额不足");
}
//2.从张无忌账户中转出1000
sql = "update account set balance=balance-? where name=?";
ps = conn.prepareStatement(sql);
ps.setDouble(1, 1000.0);
ps.setString(2, "张无忌");
ps.executeUpdate();
//---------模拟停电了--------------
//int a = 1/0;
//--------------------------
//3.从赵敏账户中增加1000
sql = "update account set balance=balance+? where name=?";
ps = conn.prepareStatement(sql);
ps.setDouble(1, 1000.0);
ps.setString(2, "赵敏");
ps.executeUpdate();
} catch (ClassNotFoundException |SQLException |RuntimeException ex) {
ex.printStackTrace();
}
} 事务(Transaction,简写为tx): 在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另一种状态。 我们把多个密不可分的操作看做是一个整体,那么该整体就称之为一个事务. -------------------------------------------------- 事务的ACID属性: 1. 原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 2. 一致性(Consistency)事务必须使数据库从一个一致性状态变换到另外一个一致性状态,但是不最终数据不能被破坏,两个账户的总余额是不能改变的. 3. 隔离性(Isolation):MySQL再讲 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 4. 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响 -------------------------------------------------- 事务:指构成单个逻辑工作单元的操作集合 事务处理:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),要么整个事务回滚(rollback)到最初状态 处理事务的两个动作: 提交:commit: 当整个事务中,所有的逻辑单元都正常执行成功. ---->提交事务.---数据已经提交,不能更改. 回滚:rollback: 当整个事务中,有一个逻辑单元执行失败, ---->回滚事务. 撤销该事务中的所有操作,释放锁--->恢复到最初的状态.
1):默认情况下,在JDBC中执行DML操作就会自动提交事务,此时我们得设置事务的手动提交机制(取消事务的自动提交). 2):查询操作,不涉及数据的更改,所以不需要事务. 3):MySQL中InnoDB存储引擎支持事务,MyISAM不支持. alter table account engine = 'MyISAM'; ---> "InnoDB" 意识:如果是DML操作时,没有异常,代码也正确,但是数据改变不了,首先去想到事务没有提交。
操作事务的模板:
try{
//取消事务自动提交:
connection对象.setAutoCommit(false);
操作1
操作2
操作3
//提交事务
Connection对象.commit();
}catch(Exception e){
//处理异常
//回滚事务
Connection对象.rollback();
}finally{
释放资源
}
上述例子修改后代码演示: @Test
public void test2() throws ArithmeticException{
String sql = "select * from account where name=? AND balance>?";
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","rootroot");
//取消事务自动提交
conn.setAutoCommit(false);
System.out.println(conn);
//1.查询张无忌账户余额是否大于1000
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "张无忌");
ps.setDouble(2, 1000.0);
ResultSet rs = ps.executeQuery();
if(!rs.next()){
throw new RuntimeException("账户余额不足");
}
//2.从张无忌账户中转出1000
sql = "update account set balance=balance-? where name=?";
ps = conn.prepareStatement(sql);
ps.setDouble(1, 1000.0);
ps.setString(2, "张无忌");
ps.executeUpdate();
//---------模拟停电了--------------
int a = 1/0;
//--------------------------
//3.从赵敏账户中增加1000
sql = "update account set balance=balance+? where name=?";
ps = conn.prepareStatement(sql);
ps.setDouble(1, 1000.0);
ps.setString(2, "赵敏");
ps.executeUpdate();
conn.commit();
} catch ( Exception ex) {
ex.printStackTrace();
try {
conn.rollback();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
|