事务(Transaction)
分为编程式事务和声明式事务,此处说明的是编程时事务:
演示银行转账的功能(以此为例):
1.创建一张表示账号的表
CREATE TABLE t_account(
id INT PRIMARY KEY AUTO_INCREMENT,
a_name VARCHAR(50),
balance DECIMAL(11,2)//11表示一共有几位,2表示有几位小数
)
2.向表中插入几个用户
INSERT INTO t_account(id,a_name,balance) VALUES(NULL,'sunwukong',1000);
INSERT INTO t_account(id,a_name,balance) VALUES(NULL,'zhubajie',1000);
INSERT INTO t_account(id,a_name,balance) VALUES(NULL,'shaheshang',1000);
SELECT * FROM t_account;
3.#sunwukong向shaheshang转账100元
#从sunwukong的账号减去100元
UPDATE t_account SET balance = balance - 100 WHERE a_name='sunwukong';
#给shaheshang的账号加上100元
UPDATE t_account SET balance = balance +100 WHERE a_name = 'shaheshang';
重新设置为1000元:
UPDATE t_account SET balance =1000;
4.从java代码中演示上面的案例:
1.创建Dao类
1 public class AcountDao { 2 public void update(String name,double money){ 3 //准备两个变量 4 Connection conn = null; 5 PreparedStatement ps = null; 6 //准备SQL模板 7 String sql = "UPDATE t_account SET balance = balance + ? WHERE a_name = ?"; 8 9 try { 10 conn = JDBCUtil.getConnection(); 11 //获取PreparedStatement 12 ps = conn.prepareStatement(sql); 13 //填充占位符 14 ps.setDouble(1, money); 15 ps.setString(2, name); 16 17 //执行SQL语句 18 ps.executeUpdate(); 19 20 } catch (SQLException e) { 21 // TODO Auto-generated catch block 22 e.printStackTrace(); 23 }finally{ 24 JDBCUtil.close(conn, ps, null); 25 } 26 } 27 }
2.测试该DAO
public class TestTransaction { private AcountDao accountDao = new AcountDao(); @Test public void test() { //从sunwukong账户向shaheshang账户转账100元! //1.从sunwukong账户扣除100元 accountDao.update("sunwukong", -100); //2.向shaheshang账户添加100元 accountDao.update("shaheshang", 100); } }
//显然上面是可以正常执行的!
但是如果上面的程序在suwukong减去100元之后,shaheshang加钱之前,出现了异常,如下所示:
//从sunwukong账户向shaheshang账户转账100元! //1.从sunwukong账户扣除100元 accountDao.update("sunwukong", -100); int i =10/0;//添加一个异常 //2.向shaheshang账户添加100元 accountDao.update("shaheshang", 100);
- 在开发中我们的一个业务往往需要同时操作多个表,这些操作往往是不可分割,业务中的对数据库的多次操作,
要么同时成功,要么全都失败。
- 事务的特性(ACID):
原子性(atomicity)
一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
一致性(consistency)
事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation)
一个事务的执行不能被其他事务干扰。
即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability)
持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
接下来的其他操作或故障不应该对其有任何影响。
- 操作事务的基本步骤:
1.开启事务
- 开启事务以后,我们只后的所有操作将都会在同一个事务当中
2.操作数据库
- 开启事务以后再去操作数据库,所有操作将不会直接提交到数据库中
3.提交事务
- 将修改应用到数据库
4.回滚事务
- 数据库操作过程中出现异常了,回滚事务,回滚事务以后,数据库变成开启事务之前的状态
- mysql中的事务控制
#开启事务
START TRANSACTION
#回滚事务
ROLLBACK
#提交事务
COMMIT
- JDBC中的事务主要通过Connection对象来控制的
1.开启事务
void setAutoCommit(boolean autoCommit) throws SQLException;
- 设置事务是否自动提交,默认是自动提交
- 设置事务手动提交
conn.setAutoCommit(false);
2.提交事务
void commit() throws SQLException;
- 提交事务
conn.commit()
3.回滚事务
void rollback() throws SQLException;
- 回滚事务
conn.rollback()
- 事务控制的格式:
1 · //创建一个Connection 2 Connection conn = null; 3 4 try{ 5 6 //获取Connection 7 conn = JDBCUtils.getConnection(); 8 9 //开启事务 10 conn.setAutoCommit(false); 11 12 //对数据库进行操作 13 14 //操作成功,提交事务 15 conn.commit(); 16 17 }catch(Exception e){ 18 e.printStackTrace(); 19 20 //回滚事务 21 try { 22 conn.rollback(); 23 } catch (SQLException e1) { 24 e1.printStackTrace(); 25 } 26 27 }finally{ 28 JDBCUtils.close(conn, null, null); 29 }
- 注意:我们在同一个事务中使用的数据库连接(Connection)必须是同一个,否则事务还是不作用!
所以此时原来的AcountDAO中的update方法要改为如下所示:
1 public class AcountDao { 2 public void update(Connection conn,String name,double money){ 3 //准备两个变量 4 PreparedStatement ps = null; 5 //准备SQL模板 6 String sql = "UPDATE t_account SET balance = balance + ? WHERE a_name = ?"; 7 8 try { 9 //获取PreparedStatement 10 ps = conn.prepareStatement(sql); 11 //填充占位符 12 ps.setDouble(1, money); 13 ps.setString(2, name); 14 15 //执行SQL语句 16 ps.executeUpdate(); 17 18 } catch (SQLException e) { 19 // TODO Auto-generated catch block 20 e.printStackTrace(); 21 }finally{ 22 //此时也不能在这里关闭数据库连接了,而是在外边统一关闭 23 JDBCUtil.close(null, ps, null); 24 } 25 } 26 }
1 @Test 2 public void test() { 3 Account account=new Account(); 4 5 Connection conn = JDBCUtil.getConnection(); 6 try { 7 conn.setAutoCommit(false); 8 account.update(conn,"孙悟空", -100); 9 int i=10/0; 10 account.update(conn,"猪八戒", 100); 11 conn.commit(); 12 } catch (SQLException e) { 13 try { 14 conn.rollback();//fa 15 } catch (SQLException e1) { 16 // TODO Auto-generated catch block 17 e1.printStackTrace(); 18 } 19 } 20 if(conn!=null){ 21 try { 22 conn.close(); 23 } catch (SQLException e) { 24 // TODO Auto-generated catch block 25 e.printStackTrace(); 26 } 27 } 28 }