JDBC学习笔记——事务、存储过程以及批量处理
1、事务
1.1、事务的基本概念和使用示例
数据库事务,是指作为单个逻辑工作单元执行的一系列操作,要么完整地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。
JDBC可以操作Connection的setAutoCommit()方法,给它false参数,提示数据库启动事务,在下达一连串的SQL命令后,自行调用Connection的commit()方法,提示数据库确认(Commit)操作。如果中间发生错误,则调用rollback(),提示数据库撤销(ROLLBACK)所有执行。同时,如果仅想要撤回某个SQL执行点,则可以设置存储点(SAVEPOINT)。一个示范的事务流程如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
Connection conn = ...;Savepoint point = null;try { conn.setAutoCommit(false); Statement stmt = conn.createStatement(); stmt.executeUpdate("INSERT INTO ..."); ... point = conn.setSavepoint(); stmt.executeUpdate("INSERT INTO ..."); ... conn.commit();} catch (SQLException e) { e.printStackTrace(); if (conn != null) { try { if (point == null) { conn.rollback(); } else { conn.rollback(point); conn.releaseSavepoint(point); } } catch (SQLException ex) { ex.printStackTrace(); } }} finally { ... if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException ex) { ex.printStackTrace(); } }} |
需要说明的是,JDBC操作事务的前提条件是数据库支持事务,如果数据库本身不支持事务,那么我们即使调用setAutoCommit(false)也无法启动事务。对于MYSQL来说,MyIsam数据库引擎不支持事务操作,InnoDB数据库引擎支持事务操作。
1.2、隔离级别
要理解隔离级别,首先要了解多个事务并行时,可能引发的数据不一致问题有哪些,常见的事务并行引发问题有以下几类:
更新遗失
更新遗失是指某个事务对字段进行更新的信息,因另一个事务的介入而遗失更新的效力,一个简单的示例如下:
- 事务A更新数据表字段为AAA;
- 事务B更新数据表字段为BBB;
- 事务A提交;
- 事务B提交。
这个序列就是典型的更新丢失,因为第三步所做的所有修改全部会丢失。如果要避免更新遗失问题,可以设置隔离级别为"read uncommitted",这样A事务已更新但未确认的数据,B事务仅可做读取操作,但不可做更新操作。这样上面的四个步骤就是不合法的,必须A事务完全提交,B事务才能做更新操作。
脏读
"read uncommitted"隔离级别保证了在A事务提交之前,B事务不能做更改操作,但是没有阻止B事务做读取操作,但是这个其实是有问题的:如果A事务更新字段为"AAA",B事务读取值为"AAA"并使用,然后A事务回滚事务,那么B事务读取的"AAA"就属于脏数据。如果要避免脏读问题,可以设置隔离级别为"Read Commited",也就是事务读取的数据必须是其他事务已经确认的数据。
不可重复读
不可重复度是指两次读取同一字段的数据不一致,例如:事务A读取字段为"AAA",事务B更新数据为"BBB",事务B提交,事务A读取字段为"BBB",事务A连续的两次读取,字段值不一样。要避免这种问题,可以设置数据库隔离级别为"Repeatable Read",对于这种隔离界别,事务A读取字段为"AAA"后,其他事务在事务A提交前只可读取该字段,不可更新该字段。
幻读
幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的"全部数据行"。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入"一行新数据"。那么,以后就会发生操作第一个事务的用户发现表中还是有没有修改的数据行,就好象发生了幻觉一样。要解决幻读问题,必须设置隔离级别为Serializable,Serializable是数据库隔离级别的最高级别,串行化读,事务只能一个一个执行,避免了脏读、不可重复读、幻读,但是执行效率慢,需要谨慎使用。
1.3、悲观锁、乐观锁
悲观锁是采用一种悲观的态度来对待事务并发问题,认为系统中的并发更新会非常频繁,并且事务失败了以后重来的开销很大,这样一来,我们就需要采用真正意义上的锁来进行实现。悲观锁的实现,往往依靠数据库提供的锁机制。悲观锁的基本思想就是每次一个事务读取某一条记录后,就会把这条记录锁住,这样其它的事务要想更新,必须等以前的事务提交或者回滚解除锁。
乐观锁,顾名思义就是保持一种乐观的态度,我们认为系统中的事务并发更新不会很频繁,即使冲突了也没事,大不了重新再来一次。它的基本思想就是每次提交一个事务更新时,我们先看看要修改的东西从上次读取以后有没有被其它事务修改过,如果修改过,那么更新就会失败。乐观锁的实现方式大多是基于数据版本 ( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。 读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提 交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
总的来说,悲观锁的机制依赖数据库的锁机制,较安全,而乐观锁机制通过应用程序控制,性能较好。
2、存储过程
JDBC可以调用存储过程,要调用存储过程,首先我们应该创建存储过程:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
//创建表,并插入数据create table g(num int,value varchar(10)); insert into g values(1, '1'),(10, '10'),(60, '60'),(100, '100');//创建存储过程DELIMITER $CREATE PROCEDURE p1(IN n int, OUT avg double, OUT min int)BEGIN select avg(num) from g where num > n INTO avg; select min(num) from g where num > n INTO min;END$DELIMITER ; |
JDBC调用存储过程的接口与增删改查的不同,JDBC调用存储过程应该使用CallableStatement,简单示例如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
private static void ps() throws SQLException{ Connection conn = null; CallableStatement cs = null; try{ conn = JdbcUtils.getConnection(); cs = conn.prepareCall("call p1(?,?,?)"); cs.registerOutParameter(2, Types.DOUBLE);//设置out参数 cs.registerOutParameter(3, Types.INTEGER);//设置out参数 cs.setInt(1, 18);//设置in参数 cs.executeUpdate(); System.out.println(cs.getInt(2) + " " + cs.getInt(3)); } finally{ JdbcUtils.free(null, cs, conn); } } |
3、批次更新
如果需要对对数据库进行大量数据更新,使用循环多次操作更新是比较浪费性能的,对于这种场景,我们可以使用addBatch()方法来收集SQL,并使用executeBatch()方法将收集的SQL批次更新,例如:
|
1
2
3
4
5
|
Statement stmt = conn.createStatement();while(someCondition) { stmt.addBatch("INSERT INTO ...");}stmt.executeBatch(); |
JDBC学习笔记——事务、存储过程以及批量处理的更多相关文章
- JDBC 学习笔记(十一)—— JDBC 的事务支持
1. 事务 在关系型数据库中,有一个很重要的概念,叫做事务(Transaction).它具有 ACID 四个特性: A(Atomicity):原子性,一个事务是一个不可分割的工作单位,事务中包括的诸操 ...
- JDBC学习笔记二
JDBC学习笔记二 4.execute()方法执行SQL语句 execute几乎可以执行任何SQL语句,当execute执行过SQL语句之后会返回一个布尔类型的值,代表是否返回了ResultSet对象 ...
- JDBC学习笔记一
JDBC学习笔记一 JDBC全称 Java Database Connectivity,即数据库连接,它是一种可以执行SQL语句的Java API. ODBC全称 Open Database Conn ...
- MyBatis 学习笔记(七)批量插入ExecutorType.BATCH效率对比
MyBatis 学习笔记(七)批量插入ExecutorType.BATCH效率对比一.在mybatis中ExecutorType的使用1.Mybatis内置的ExecutorType有3种,默认的是s ...
- VSTO学习笔记(五)批量编辑Excel 2010 x64
原文:VSTO学习笔记(五)批量编辑Excel 2010 x64 近期因为工作的需要,经常要批量处理大量的Excel文件,如果纯手工一个个修改,非常的麻烦,于是写了这么一个帮助类,希望能对你有所帮助. ...
- MySQL学习笔记-事务相关话题
事务机制 事务(Transaction)是数据库区别于文件系统的重要特性之一.事务会把数据库从一种一致状态转换为另一个种一致状态.在数据库提交工作时,可以确保其要么所有修改都已经保存了,要么所有修改都 ...
- JDBC 学习笔记(十)—— 使用 JDBC 搭建一个简易的 ORM 框架
1. 数据映射 当我们获取到 ResultSet 之后,显然这个不是我们想要的数据结构. 数据库中的每一个表,在 Java 代码中,一定会有一个类与之对应,例如: package com.gerrar ...
- JDBC 学习笔记(六)—— PreparedStatement
1. 引入 PreparedStatement PreparedStatement 通过 Connection.createPreparedStatement(String sql) 方法创建,主要用 ...
- JDBC 学习笔记(四)—— 自定义JDBC框架+Apache—DBUtils框架+事务管理+操作多表
本文目录: 1.自定义JDBC框架 ——数据库元数据:DataBaseMetaData 2.自定义JDBC框架 ——数据库元数据:DataBaseMetaData ...
随机推荐
- iOS将汉字转换为拼音
将汉字转换为拼音 - (NSString *)chineseToPinyin:(NSString *)chinese withSpace:(BOOL)withSpace { CFStringRef h ...
- JavaScript的Math对象
原文 简书原文:https://www.jianshu.com/p/8776ec9cfb58 大纲 前言 1.Math对象的值属性 2.Math对象的函数属性 3.Math对象的函数的使用 前言 Ma ...
- 使用 Google Guava 美化你的 Java 代码:1~4 【转】
文章转载自:http://my.oschina.net/leejun2005/blog/172328 1.使用Google Collections,Guava,static imports编写漂亮代码 ...
- Nutch+Hadoop集群搭建 分类: H3_NUTCH 2015-01-18 10:55 362人阅读 评论(0) 收藏
转载自:http://www.open-open.com/lib/view/open1328670771405.html 1.Apache Nutch Apache Nutch是一个用于网络搜索 ...
- 单选框radio改变事件详解(用的jquery的radio的change事件)
单选框radio改变事件详解(用的jquery的radio的change事件) 一.总结 1.用的jquery的radio的change事件:当元素的值发生改变时,会发生 change 事件,radi ...
- centos-mirrors
http://mirrors.aliyun.com/centos/7.2.1511/os/x86_64/Packages/ http://mirrors.aliyun.com/centos/7.2.1 ...
- IDEA使用从Eclipse过来的快捷键
1.Eclipse中的ctrl+shift+o --------> Ctrl + Alt + O 2.Eclipse中快捷键是Ctrl+O ---------> MacOS 下是 cmd+ ...
- wait()、notify()、notifyAll()与线程通信方式总结
1.通过wait().notify().notifyAll()进行线程通信 线程通信的目标是使线程间能够互相发送信号.另一方面,线程通信使线程能够等待其他线程的信号.例如,线程B可以等待线程A的一个信 ...
- active set method(激活集方法)
在优化问题的求解中,如果待优化(最大最小)的目标函数,其解集受限于一组约束条件, g1(x)≥0,-,gk(x)≥0 约束条件定义着可行域(feasible region),对于可行域中的任一点 x ...
- gdal以GA_Update方式打开jpg文件的做法
作者:朱金灿 来源:http://blog.csdn.net/clever101 gdal库是不支持以GA_Update方式打开jpg文件的,原因在于gdal_1_10_1\frmts\jpeg文件夹 ...