【转】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小数据还成,大量的数据也真呵呵了;
【转】JDBC学习笔记(7)——事务的隔离级别&批量处理的更多相关文章
- JDBC学习笔记(7)——事务的隔离级别&批量处理
数据库事务的隔离级别 对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 ...
- 数据库事务的四大特性以及4种事务的隔离级别-以及对应的5种JDBC事务隔离级别
本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务 ...
- 数据库事务的四大特性以及事务的隔离级别(mysql)
本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指 ...
- 数据库事务的四大特性以及事务的隔离级别-与-Spring事务传播机制&隔离级别
数据库事务的四大特性以及事务的隔离级别 本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ ...
- MySQL事务的隔离级别
为什么需要隔离 当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性,在介绍数据库提供的各种隔离级别之前,我们先看看如果不考虑事务的隔离性,会发生的几种 ...
- MySQL数据库事务的四大特性以及事务的隔离级别
一.事务的四大特性(ACID) 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: 1.原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因 ...
- JDBC 学习笔记(十一)—— JDBC 的事务支持
1. 事务 在关系型数据库中,有一个很重要的概念,叫做事务(Transaction).它具有 ACID 四个特性: A(Atomicity):原子性,一个事务是一个不可分割的工作单位,事务中包括的诸操 ...
- JDBC 学习笔记(二)—— 大数据+存储过程+批处理+事务管理
本文目录: 1.使用JDBC处理大数据 2.使用JDBC处理大文本 3.使用JDBC处理二进制数据 4.Oracle中大数据处理 5 ...
- JDBC学习笔记一
JDBC学习笔记一 JDBC全称 Java Database Connectivity,即数据库连接,它是一种可以执行SQL语句的Java API. ODBC全称 Open Database Conn ...
随机推荐
- Macaca 自动化框架 [Python 系列]
介绍 Macaca是一套完整的自动化测试解决方案,基于node.js开发.由阿里巴巴公司开源: 地址:http://macacajs.github.io/macaca/ 特点: 同时支持PC端和移动端 ...
- 一份关于组建.NET Core开源团队的倡议书
组建这个.NET Core开源团队,旨在为社区出一份力,对自己能力也是一个提升,是一个即利于他人,也利于自己的想法和行动.如果你有很多想法,如果你需要认识更多志同道合的朋友,如果你想展示自己的才华,如 ...
- express 4
http://www.expressjs.com.cn/4x/api.html#app中间件 路由 模板 跨域 json cookie session
- loadrunner:Auto Correlate自动定位瓶颈
Auto correlate可以根据数据趋势,自动分析出可能影响目标性能的数据项,通过它可以很方便地找出哪些数据之间有明显的相互依赖性. 下面以分析影响平均响应时间的windows资源为例,讲解Aut ...
- Scut游戏引擎改造兼容Codis。
原生的Scut引擎是采用redis来做数据缓存层,引擎在以异步的方式(时间可配置,默认100ms)实现数据同步.为了提高redis的可扩展性.高可用性,把redis换成codis,因为codis有部分 ...
- calendar.js(日历组件封装)
最近一直闲来无事,便寻思着做一下自己的个人项目,也想说能使用现在比较流行的一些mvvm框架来做,于是就选用了这样的一个技术栈vue2.0+vue-router+vuex+webpack来做,做得也是多 ...
- (转)centos6.5安装VNC
在Linux下用VNC远程桌面是个很不错的玩意.但在CentOS中默认没有安装VNC的.可以用下面语句查询,如果出现下面情况说明没有安装vnc #rpm -q tigervnc tigervnc-se ...
- linux最小安装
(1)系统安装类型选择及自定义额外包组 进入如图2-28所示界面.上半部分是系统定制的不同的系统安装类型选择项,默认是“Desktop”,这里我们选择“Minimal”,即最小化安装,下半部分是在上面 ...
- Spring+SpringMVC+MyBatis+easyUI整合优化篇(三)代码测试
日常啰嗦 看到标题你可能会问为什么这一篇会谈到代码测试,不是说代码优化么?前两篇主要是讲了程序的输出及Log4j的使用,Log能够帮助我们进行bug的定位,优化开发流程,而代码测试有什么用呢?其实测试 ...
- Vijos1523贪吃的九头龙【树形DP】
贪吃的九头龙 传说中的九头龙是一种特别贪吃的动物.虽然名字叫"九头龙",但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头 ...