1、演示转账的功能:
(1)创建一张表示学生表表
CREATE TABLE student(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
account DECIMAL(11,2)
)
(2)向表中插入几个用户
INSERT INTO student(id,name,account ) VALUES(NULL,'stu1',1000);
INSERT INTO student(id,name,account ) VALUES(NULL,'stu2',1000);
SELECT * FROM student;
(3)stu1向stu2转账100元
从stu1的账号减去100元
UPDATE student SET account = account - 100 WHERE name='stu1';
#给shaheshang的账号加上100元
UPDATE student SET account = account +100 WHERE name = 'stu2';
重新设置为1000元:
UPDATE student SET account =1000;
2、用代码演示上述过程
1 //StudentDao类 2 public static int updateAge(Connection conn,Integer id,Integer account) { 3 Connection conn = null; 4 PreparedStatement ps = null; 5 String sql = "update student set account=account+? where id=?"; 6 int i = 0; 7 try { 8 conn = DBUtil.getConn(); 9 ps = conn.prepareStatement(sql); 10 ps.setInt(1, account); 11 ps.setInt(2, id); 12 i = ps.executeUpdate(); 13 } catch (SQLException e) { 14 e.printStackTrace(); 15 } finally{ 16 DBUtil.close(conn, ps, null); 17 } 18 return i; 19 } 20 //测试类 21 @Test 22 public void testTransaction() { 23 try { 24 StudentDao.updateAccount(conn, 1, -100); 25 //int i=10/0; 26 StudentDao.updateAccount(conn, 2, 100); 27 } catch (SQLException e) { 28 e.printStackTrace(); 29 } 30 }
正常执行,没有问题,但是若将 int i=10/0; 处的注解放开,则id为1的账户减少100元,id为2的账户并没有增加100元,银行赚了。
3、用事务的方式解决上述问题
事务(Transaction)
- 事务的特性(ACID):
- 原子性(atomicity):一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
- 一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
- 隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
- 操作事务的基本步骤:
- 开启事务:开启事务以后,我们以后的所有操作将都会在同一个事务当中
- 操作数据库:开启事务以后再去操作数据库,所有操作将不会直接提交到数据库中
- 提交事务:将修改应用到数据库
- 回滚事务:数据库操作过程中出现异常了,回滚事务,回滚事务以后,数据库变成开启事务之前的状态
- mysql中的事务控制
- 开启事务:START TRANSACTION
- 提交事务:COMMIT
- 回滚事务:ROLLBACK
- JDBC中的事务主要通过Connection对象来控制的
- 开启事务
- void setAutoCommit(boolean autoCommit) throws SQLException;
- //设置事务是否自动提交,默认是自动提交
- //设置事务手动提交
- conn.setAutoCommit(false);
- //提交事务
- void commit() throws SQLException;
- //提交事务
- conn.commit()
- 回滚事务
- void rollback() throws SQLException;
- //回滚事务
- conn.rollback()
- 开启事务
- 事务控制的格式:
1 Connection conn = null;//创建一个Connection 2 try{ 3 conn = JDBCUtils.getConnection();//获取Connection 4 conn.setAutoCommit(false);//开启事务 5 //对数据库进行操作 6 conn.commit();//操作成功,提交事务 7 } catch(Exception e){ 8 e.printStackTrace(); 9 //回滚事务 10 try { 11 conn.rollback(); 12 } catch (SQLException e1) { 13 e1.printStackTrace(); 14 } 15 } finally{ 16 DBUtil.close(conn, null, null); 17 }
注意:在同一个事务中使用的数据库连接(Connection)必须是同一个,否则事务不起作用!
对上面的代码进行修改
1 //StudentDao类 2 public static int updateAccount(Connection conn,Integer id,Integer account) { 3 PreparedStatement ps = null; 4 String sql = "update student set account=account+? where id=?"; 5 int i = 0; 6 try { 7 ps = conn.prepareStatement(sql); 8 ps.setInt(1, account); 9 ps.setInt(2, id); 10 i = ps.executeUpdate(); 11 } catch (SQLException e) { 12 e.printStackTrace(); 13 } finally{ 14 DBUtil.close(null, ps, null); 15 } 16 return i; 17 } 18 //测试类 19 @Test 20 public void testTransaction() { 21 Connection conn = DBUtil.getConn();//获取连接Connection 22 try { 23 conn.setAutoCommit(false);//开启事务 24 StudentDao.updateAccount(conn, 1, -100); 25 int i=10/0; 26 StudentDao.updateAccount(conn, 2, 100); 27 conn.commit();//操作成功,提交事务 28 } catch (SQLException e) { 29 e.printStackTrace(); 30 //回滚事务 31 try { 32 conn.rollback(); 33 } catch (SQLException e1) { 34 e1.printStackTrace(); 35 } 36 } finally{ 37 DBUtil.close(conn, null, null); 38 } 39 }
int i=10/0; 处抛出了错误,但是事务没有提交,并进行了回滚,所以数据库中的数据没有变,运行正常的话,可以进行正常转账。
4、批处理和事务的结合
实际开发中,批处理和事务常常是结合在一起使用。速度会成倍增加
1 @Test 2 public void testTransBatch() { 3 Connection conn = DBUtil.getConn(); 4 PreparedStatement ps = null; 5 try { 6 conn.setAutoCommit(false);//开启事务 7 String sql = "INSERT INTO student(NAME) VALUES(?)"; 8 try { 9 ps = conn.prepareStatement(sql); 10 for (int i = 0; i < 1000; i++) { 11 ps.setString(1, "stu"+i); 12 ps.addBatch(); 13 } 14 long start = System.currentTimeMillis(); 15 ps.executeBatch(); 16 long end = System.currentTimeMillis(); 17 System.out.println((end-start)+"ms"); 18 } catch (SQLException e) { 19 e.printStackTrace(); 20 } 21 conn.commit();//提交事务 22 } catch (SQLException e) { 23 //回滚事务 24 try { 25 conn.rollback(); 26 } catch (SQLException e1) { 27 e1.printStackTrace(); 28 } 29 e.printStackTrace(); 30 } finally{ 31 DBUtil.close(conn, ps, null); 32 } 33 }
运行时间:平均20ms左右
DBUtil工具类--->资源目录--->工具类--->数据库操作工具类