【转】JDBC学习笔记(7)——事务的隔离级别&批量处理

转自:http://www.cnblogs.com/ysw-go/

数据库事务的隔离级别

对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:
脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
不可重复读: 对于两个事务 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.
幻读: 对于两个事务 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.
数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题. 
一个事务与其他事务隔离的程度称为隔离级别. 数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱

数据库提供的 4 种事务隔离级别:

Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE. Oracle 默认的事务隔离级别为: READ COMMITED 
Mysql 支持 4 中事务隔离级别. Mysql 默认的事务隔离级别为: REPEATABLE READ

具体代码实现:

  1 /**
  2      * ID1 给 ID2 500钱
  3      * 关于事务:
  4      * 1.如果多个操作,每个使用自己单独的连接,则无法保证事务 例 test1演示
  5      * 2.具体步骤:
  6      *     1) 事务开始前,取消Connection 的默认的自动提交  setAutoCommit(false);
  7      *     2) 如果事务的操作都成功,那么就提交事务
  8      *     3)否则在 try-catch块中回滚
  9      * try {
 10      *
 11      * conn.setAutoCommit(false);
 12      * ...
 13      *     conn.commit();
 14      * }catch{
 15      * ...
 16      *     conn.rollback();
 17      * }
 18      */
 19     @Test
 20     public void test2(){
 21
 22         Connection conn = null;
 23         try {
 24             conn = JDBC_Tools.getConnection();
 25             //System.out.println(conn.getAutoCommit());
 26
 27             // 1) 取消自动提交
 28             conn.setAutoCommit(false);
 29
 30             String sql = "UPDATE rent set money = "
 31                     + "money - 500 where id = ?";
 32
 33             // 2) 如果事务的操作都成功,那么就提交事务
 34             update(conn,sql, 1);
 35
 36             //int i = 1 / 0;
 37
 38             sql = "UPDATE rent set money = "
 39                     + "money + 500 where id = ?";
 40             update(conn,sql, 2);
 41             conn.commit();
 42         } catch (Exception e) {
 43             e.printStackTrace();
 44
 45             // 3)否则在 try-catch块中回滚
 46             try {
 47                 conn.rollback();
 48             } catch (SQLException e1) {
 49                 e1.printStackTrace();
 50             }
 51
 52         }finally{
 53             JDBC_Tools.relaseSource(conn, null);
 54         }
 55     }
 56 public static void update(Connection conn,String sql,Object...objs){
 57
 58         PreparedStatement ps =null;
 59         try {
 60             ps = conn.prepareStatement(sql);
 61
 62             for(int i = 0;i<objs.length;i++){
 63                 ps.setObject(i+1, objs[i]);
 64             }
 65             ps.executeUpdate();
 66         } catch (Exception e) {
 67             e.printStackTrace();
 68         }finally{
 69             JDBC_Tools.relaseSource(null, ps);
 70         }
 71     }
 72
 73     @Test
 74     public void test1() {
 75
 76         String sql = "UPDATE rent set money = "
 77                 + "money - 500 where id = ?";
 78         DAO.update(sql, 1);
 79
 80         int i = 1 / 0; //一旦出现异常, ID1 减了500,但是 ID2 的钱并没有增加
 81
 82         sql = "UPDATE rent set money = "
 83                 + "money + 500 where id = ?";
 84         DAO.update(sql, 2);
 85     }设置隔离级别
 86
 87  public static <E> E getForValue(String sql){
 88
 89         //1. 得到结果集,该结果只有一行一列
 90         Connection conn = null;
 91         PreparedStatement ps = null;
 92         ResultSet rs = null;
 93         try {
 94             //1. 获取数据库连接
 95             conn = JDBC_Tools.getConnection();//System.out.println(conn.getTransactionIsolation());
 96             conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
 97             //2. 获取 PreparedStatement 对象
 98             ps = conn.prepareStatement(sql);
 99             //2. 取得结果
100             rs = ps.executeQuery();
101             if(rs.next()){
102                 return (E)rs.getObject(1);
103             }
104         }catch(Exception e){
105                 e.printStackTrace();
106         }finally{
107         JDBC_Tools.relaseSource(rs,conn, ps);
108         }
109         return null;
110     }

在 MySql 中设置隔离级别

具体代码实现:

 1 public static <E> E getForValue(String sql){
 2
 3         //1. 得到结果集,该结果只有一行一列
 4         Connection conn = null;
 5         PreparedStatement ps = null;
 6         ResultSet rs = null;
 7         try {
 8             //1. 获取数据库连接
 9             conn = JDBC_Tools.getConnection();//System.out.println(conn.getTransactionIsolation());
10             conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
11             //2. 获取 PreparedStatement 对象
12             ps = conn.prepareStatement(sql);
13             //2. 取得结果
14             rs = ps.executeQuery();
15             if(rs.next()){
16                 return (E)rs.getObject(1);
17             }
18         }catch(Exception e){
19                 e.printStackTrace();
20         }finally{
21         JDBC_Tools.relaseSource(rs,conn, ps);
22         }
23         return null;
24     }

启动一个 mysql 程序, 就会获得一个单独的数据库连接. 每个数据库连接都有一个全局变量 @@tx_isolation, 表示当前的事务隔离级别. MySQL 默认的隔离级别为 Repeatable Read
查看当前的隔离级别: SELECT @@tx_isolation;
设置当前 mySQL 连接的隔离级别:  
set  transaction isolation level read committed;
设置数据库系统的全局的隔离级别:
set global transaction isolation level read committed;

JDBC批量执行

当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率

/**
     * 向mysql的testJ数据表中插入100000条记录
     * 测试如何插入用时最短
     * 版本一:使用Statement
     */

版本一:我们使用Statement进行事务的操作

 1     @Test
 2     public void testBatchWithStatement(){
 3         Connection connection=null;
 4         Statement statement=null;
 5         String sql;
 6         try {
 7             connection=JDBCTools.getConnection();
 8             //放到一个事务里面
 9             JDBCTools.beginTx(connection);
10             statement=connection.createStatement();
11             long begin=System.currentTimeMillis();
12             for(int i=0;i<100000;i++){
13                 sql="insert into testj values("+
14                 (i+1)+", ‘name_"+ i+"‘, ‘2016-05-08‘)";
15                 statement.execute(sql);
16             }
17             long end=System.currentTimeMillis();
18             System.out.println("Time:"+(end-begin));
19             JDBCTools.commit(connection);
20         } catch (Exception e) {
21             e.printStackTrace();
22             JDBCTools.rollback(connection);
23         }finally{
24             JDBCTools.release(null, statement, connection);
25         }
26     }

运行结果:

Time:8991

结论一:我们使用Statement插入100000条记录用时8991;

版本二:我们使用PreparedStatement进行事务的操作

 1 @Test
 2     public void testBatchWithPreparedStatement() {
 3         Connection connection = null;
 4         PreparedStatement preparedStatement = null;
 5         String sql;
 6         try {
 7             connection = JDBCTools.getConnection();
 8             // 放到一个事务里面
 9             JDBCTools.beginTx(connection);
10             sql = "isnert into testJ values(?,?,?)";
11             preparedStatement = connection.prepareStatement(sql);
12             long begin = System.currentTimeMillis();
13             for (int i = 0; i < 100000; i++) {
14                 preparedStatement.setInt(1, i + 1);
15                 preparedStatement.setString(2, "name_" + i);
16                 preparedStatement.setDate(3,
17                         new Date(new java.util.Date().getTime()));
18                 preparedStatement.execute();
19             }
20             long end = System.currentTimeMillis();
21             System.out.println("Time:" + (end - begin));
22             JDBCTools.commit(connection);
23         } catch (Exception e) {
24             e.printStackTrace();
25             JDBCTools.rollback(connection);
26         } finally {
27             JDBCTools.release(null, preparedStatement, connection);
28         }
29     }

运行结果:
Time:8563

结论2:因为我这里使用的是mysql数据库进行的操作,插入大量数据的时间性能方面的影响不是很大,如果我们换成oracle数据库或其他大型的关系型数据库,事务执行用时相比版本一的1/4;

版本三:批处理插入数据

 1 @Test
 2     public void testBatchWithBatch() {
 3         Connection connection = null;
 4         PreparedStatement preparedStatement = null;
 5         String sql=null;
 6         try {
 7             connection = JDBCTools.getConnection();
 8             // 放到一个事务里面
 9             JDBCTools.beginTx(connection);
10             sql = "insert into testJ values(?,?,?)";
11             preparedStatement = connection.prepareStatement(sql);
12             long begin = System.currentTimeMillis();
13             for (int i = 0; i < 100000; i++) {
14                 preparedStatement.setInt(1, i + 1);
15                 preparedStatement.setString(2, "name_" + i);
16                 preparedStatement.setDate(3,
17                         new Date(new java.util.Date().getTime()));
18                 //积攒SQL
19                 preparedStatement.addBatch();
20                 //当积攒到一定程度,就统一执行,并且清空先前积攒的SQL
21                 if((i+1)%300==0){
22                     //执行
23                     preparedStatement.executeBatch();
24                     //清空
25                     preparedStatement.clearBatch();
26                 }
27             }
28             //如果插入的记录数不是300的整倍数,再执行一次
29             if(100000%300!=0){
30                 //执行
31                 preparedStatement.executeBatch();
32                 //清空
33                 preparedStatement.clearBatch();
34             }
35             long end = System.currentTimeMillis();
36             System.out.println("Time:" + (end - begin));
37             JDBCTools.commit(connection);
38         } catch (Exception e) {
39             e.printStackTrace();
40             JDBCTools.rollback(connection);
41         } finally {
42             JDBCTools.release(null, preparedStatement, connection);
43         }
44     }

运行结果:4587(又提高了,但是还是不明显)
结论三:批处理事务建议采用版本三的方式,再次建议使用oracle数据库做这个插入数据事务的实验,mysql小数据还成,大量的数据也真呵呵了;

时间: 2024-10-08 19:14:58

【转】JDBC学习笔记(7)——事务的隔离级别&批量处理的相关文章

JDBC学习笔记(7)——事务的隔离级别&amp;批量处理

数据库事务的隔离级别 对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.不可重复读: 对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.幻读: 对于两个事物 T1, T2, T1 从一个表中读取了一个字段,

数据库事务的四大特性以及事务的隔离级别详解

作者 : fjdingsd 来源 : 博客园 本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响. ⑵ 一致性(Consistency) 一致性是指事务必须使数据库从一个一致性

事务的特性及事务的隔离级别(转)

原文:http://www.cnblogs.com/fjdingsd/p/5273008.html 本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响. ⑵ 一致性(Consi

数据库事务的四个基本特征以及事务的隔离级别

一.数据库事务的四个基本特征 事务是作为一个逻辑单元执行的一系列操作,一个逻辑工作单元必须有四个属性,称为 ACID(原子性.一致性.隔离性和持久性)属性,只有这样才能成为一个事务. 1.原子性(Atomicity):事务中包含的操作被看做一个逻辑单元,这个 逻辑单元中的操作要么全部成功,要么全部失败. 2.一致性(Consistency):一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态. 拿转账来说,假设用户A和用户B两

数据库事务的四大特性以及事务的隔离级别

本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响. ⑵ 一致性(Consistency) 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执

数据库事务的四大特性以及事务的隔离级别(mysql)

本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响. ⑵ 一致性(Consistency) 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执

数据库的四大特性及事务的隔离级别

如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性试纸事务包含所有的操作要么全部成功,要么全部失败回滚,事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对是数据库有任何影响. ⑵ 一致性(Consistency) 一致性是指事务必须使数据库从一个一致性的状态转为为另一个一致性的状态,也就是说一个事务执行之前和执行之后都必须处于一致性的状态. 比如银行转账,加上A用户和B用户两者账户的钱一共有500,那么不管A用户和B用户

数据库事务的四大特性以及事务的隔离级别-与-Spring事务传播机制&amp;隔离级别

本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响. ⑵ 一致性(Consistency) 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执

事务与隔离级别笔记

SQL Server 2008 R2 事务与隔离级别实例讲解 笔记 1.事务是数据库的工作单元,可视为一个原子操作,要么成功,要么什么也不曾发生 事务操作的三种命令: a.