事务(Transaction)

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左右

sql批处理(batch)的简单使用

 DBUtil工具类--->资源目录--->工具类--->数据库操作工具类

原文地址:https://www.cnblogs.com/lixiang1993/p/7453809.html