- 浏览: 35407 次
- 性别:
- 来自: 合肥
博客专栏
-
我和Java数据库操作的那...
浏览量:9540
最新评论
-
ivanlw:
楼主用的这个snapito好神奇啊……是直接把图片链接设置成他 ...
ibookmark.me上线了! -
succinite:
使用TortoiseGit, 出现以下错误。fatal: ht ...
一个简单的JAVA后台程序框架 -
mazhiyuan:
引用很显然,之前所说的梦想,并非是真正心中所想,而只不过想“找 ...
也谈梦想 -
cevin15:
说到我心里去了。现在处于离职状态。也是对前途一片迷茫~
也谈梦想 -
lwjlaser:
<div class="quote_title ...
我和JAVA数据库操作的那些事儿(3)
在前面的两篇文章中,第一篇主要是讲了在jdbc编程中容易碰到的几个问题,以及大致的解决方法。第二篇从代码上实现了第一篇的一些方法,并且对批处理进行了一些描述。在批处理的过程中,我并没有和事务的概念放在一起,因为我发现,这两个概念往往会引起混淆。因为jdbc的批处理是jdbc层面上的,而事务是数据库层面上的,如果写一个存储过程的话,这两者是一样的,即到了一批再commit,而在jdbc编程就是两个层面了。比如下面的代码:
public void addEmployees(Connection conn, List<Employee> empList, int batchSize) throws SQLException { long bt = System.currentTimeMillis(); PreparedStatement stmt = null; try { // 设置conn自动提交为false,而由程序在需要的地方进行commit或者rollback conn.setAutoCommit(false); String sql = SqlParser.getInstance().getSql("Employee.insert"); stmt = conn.prepareStatement(sql); int count = 0; for (Employee emp : empList) { stmt.setInt(1, emp.getId()); stmt.setString(2, emp.getName()); stmt.setInt(3, emp.getDepartment().getId()); stmt.setString(4, emp.getDescription()); stmt.addBatch(); count++; if (count % batchSize == 0) { stmt.executeBatch(); // 注意,如果不加conn.commit()方法,即使我用stmt.executeBatch(),也不会真正提交到数据库 // conn.commit(); } } stmt.executeBatch(); // 虽然我在上面多次调用executeBatch(),但都没有真正提交到数据库,只有在conn.commit()之后才算真正提交 conn.commit(); } catch (SQLException e) { conn.rollback(); } finally { long et = System.currentTimeMillis(); System.out.println(String.format("用时%dms", et - bt)); DBUtil.close(stmt); DBUtil.close(conn); } }
需要注意的是:
1. conn.setAutoCommit(false)
这里就是让Connection不自动commit,而由程序来调用。
2. 在我使用stmt.executeBatch()方法之后,如果不使用conn.commit()方法的话,数据并没有真正提交到数据库。
通过上面的代码,我们已经开始使用事务了,还记得上一篇中结束的时候,我提供了测试的速度,现在再来看一下
10条:用时16109ms
50条:用时9314ms
100条:用时8723ms
是不是觉得有点问题?速度好像比较慢,这是怎么回事?原来我没有将conn.setAutoCommit(false)之前,每次stmt的操作都是新开一个事务的,1000条数据,开1000次事务,能不慢吗?现在换上新的代码,再来测试一下:
10条:用时199ms
50条:用时269ms
100条:用时193ms
是不是快了许多呢。
言归正传,说到事务,我觉得有必要把事务的特性说一下,这是很基本的东西,但是很多人包括我自己有时也不太容易讲清楚。
原子性(Atomicity)
一致性(Consistency)
隔离性(Isolation)
持久性(Durability)
原子性:事务中的操作,要么全做成,要么都不做
一致性:事务在完成时,必须使所有的数据都保持一致状态,而且在相关数据中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有 的内部数据结构都应该是正确的。
隔离性:多个并发事务之间不能相互干扰,并发不影响事务的执行
持久性:一旦事务成功完成(Commit),它对数据库的更新应该是持久的。即使在写入磁盘之前,系统发生故障,在下次启动之后,也应保障数据更新的有效
说到原子性和持久性,这两个比较好理解,对于一致性和隔离性,有必要好好想一想。
对于一致性,我的理解有两个层面:
1. 完整性约束,比如员工表和部门表,员工所属的部门必须是部门表的数据
2. 读一致性,比如读取员工表,本来是可以读1000员工,但读的过程可能有一点长,在此过程中,另外一个事务删除了一些员工数据,但读的事务应该仍然能读到1000名员工。
对于隔离性,我觉得多个事务之间不能相互干扰,应该是事务隔离机制里最严格的Serialiable(序列化)级别,关于事务的隔离级别,我写了一篇《图说事务隔离级别 》,可以参考。
对于事务的特性的讨论暂时到此,现在来看一下,使用jdbc如何进行事务?接着我之前的例子,即删除部门的时候,会把员工所属于的部门设置为空,那就是删除部门有两个操作,一是删除部门表数据,二是更新员工表数据,这两个操作应该在一个事务里。代码如下:
public void deleteDepartmentTrans(Connection conn, Department dept) throws SQLException { try { conn.setAutoCommit(false); this.deleteDepartment(conn, dept); this.updateEmpDeptNull(conn, dept); conn.commit(); } catch (SQLException e) { conn.rollback(); } } private void deleteDepartment(Connection conn, Department dept) throws SQLException { PreparedStatement stmt = null; try { String sql = SqlParser.getInstance().getSql("Department.delete"); stmt = conn.prepareStatement(sql); stmt.setInt(1, dept.getId()); stmt.execute(); } finally { DBUtil.close(stmt); DBUtil.close(conn); } } private void updateEmpDeptNull(Connection conn, Department dept) throws SQLException { PreparedStatement stmt = null; try { String sql = SqlParser.getInstance().getSql( "Employee.departmentisnull"); stmt = conn.prepareStatement(sql); stmt.setInt(1, dept.getId()); stmt.execute(); } finally { DBUtil.close(stmt); DBUtil.close(conn); } }
简单说来:
1. conn.setAutoCommit(false),由程序自己设置什么时候commit,什么时候rollback
2. conn.commit(),真正提交到数据库
3. conn.rollback(),在需要的时候进行rollback,即恢复到上一次commit()的状态
对于jdbc3.0来说,新增加了Savepoint,我们来看一下之前批量insert员工表的方法:
public void addEmployees(Connection conn, List<Employee> empList, int batchSize) throws SQLException { long bt = System.currentTimeMillis(); PreparedStatement stmt = null; // 设置conn自动提交为false,而由程序在需要的地方进行commit或者rollback conn.setAutoCommit(false); try { String sql = SqlParser.getInstance().getSql("Employee.insert"); stmt = conn.prepareStatement(sql); int count = 0; for (Employee emp : empList) { stmt.setInt(1, emp.getId()); stmt.setString(2, emp.getName()); stmt.setInt(3, emp.getDepartment().getId()); stmt.setString(4, emp.getDescription()); stmt.addBatch(); count++; if (count % batchSize == 0) { stmt.executeBatch(); // 注意,如果不加conn.commit()方法,即使我用stmt.executeBatch(),也不会真正提交到数据库 conn.commit(); } } stmt.executeBatch(); // 虽然我在上面多次调用executeBatch(),但都没有真正提交到数据库,只有在conn.commit()之后才算真正提交 conn.commit(); } catch (SQLException e) { conn.rollback(); } finally { long et = System.currentTimeMillis(); System.out.println(String.format("用时%dms", et - bt)); DBUtil.close(stmt); DBUtil.close(conn); } }
假如我有1000个员工要写到员工表,每100个一批commit一次,假设在第900多个的时候发生了异常,于是进行rollback,但这次的rollback只是回滚最后一次commit之前的状态,即之前900条员工已经写到数据库了,那么我想要实现要么1000都写成功,要么一条都不成功,就需要Savepoint帮忙了。
public void addEmployees(Connection conn, List<Employee> empList, int batchSize) throws SQLException { long bt = System.currentTimeMillis(); PreparedStatement stmt = null; // 设置conn自动提交为false,而由程序在需要的地方进行commit或者rollback conn.setAutoCommit(false); //在这里设置一个Savepoint,以便在rollback的时候回滚到这时的状态 Savepoint sp = conn.setSavepoint(); try { String sql = SqlParser.getInstance().getSql("Employee.insert"); stmt = conn.prepareStatement(sql); int count = 0; for (Employee emp : empList) { stmt.setInt(1, emp.getId()); stmt.setString(2, emp.getName()); stmt.setInt(3, emp.getDepartment().getId()); stmt.setString(4, emp.getDescription()); stmt.addBatch(); count++; if (count % batchSize == 0) { stmt.executeBatch(); // 注意,如果不加conn.commit()方法,即使我用stmt.executeBatch(),也不会真正提交到数据库 conn.commit(); } } stmt.executeBatch(); // 虽然我在上面多次调用executeBatch(),但都没有真正提交到数据库,只有在conn.commit()之后才算真正提交 conn.commit(); } catch (SQLException e) { //回滚到Savepoint那个点 conn.rollback(sp); } finally { long et = System.currentTimeMillis(); System.out.println(String.format("用时%dms", et - bt)); DBUtil.close(stmt); DBUtil.close(conn); } }
评论
这里我就是要举例说明如果不加commit,不能写到数据库,但在最后commit才会提交到数据库。谢谢回复。
这样的话在测试的时候batchSize无论是多少,都是在1000条的时候执行commit,这样不同的batchSize测试的结果就不准确了
这里我就是要举例说明如果不加commit,不能写到数据库,但在最后commit才会提交到数据库。谢谢回复。
发表评论
-
ibookmark.me上线了!
2012-04-10 23:47 1550记得上次是2009年,接触到了python,gae,于是乎动手 ... -
使用virtualenv开发django应用
2012-01-12 11:03 1789Virtualenv是一个非常好的virtual python ... -
在Amazon EC2上试用play framework
2011-11-11 17:00 1425几个月以前,我在 ... -
一个Log生成工具小项目的实现
2011-11-01 17:32 1386这两天的主要工作是用java写一个log生成工具,用于 ... -
自定义log4j生成的log文件名
2011-11-01 13:57 2992很多时候,log4j的RollingFileAppen ... -
我和java操作数据库那些事儿(5)
2011-10-26 16:56 1672引用 半自动化武器来了:Spring JdbcTemplate ... -
我和JAVA数据库操作的那些事儿(4)
2011-10-24 16:21 1502通过前面几篇的介绍,对于JDBC的使用应该基本上够上项目开发的 ... -
我和JAVA数据库操作的那些事儿(2)
2011-10-20 11:15 1581摘要 写道 上一篇提到的几个问题,在本篇有具体的代码。本篇后 ... -
图说事务隔离级别
2011-10-19 21:38 1451我们经常说的事务隔离级别,一般指的是SQL-92 ... -
我和JAVA数据库操作的那些事儿(1)
2011-10-19 15:26 2237摘要 我开始接触jdbc的 ... -
复习:观察者模式
2011-10-17 17:02 1003观察者模式(有时又被称为发布/订阅模式)是软件设计模式 ... -
复习:代理模式
2011-10-17 15:53 748代理模式是常用的Java 设计模式,它的特征是代理类与 ... -
对chainsaw中一个简单Job Scheduler的扩展
2011-10-14 23:36 1080今天在看apache chainsaw这个项目的源代码 ... -
Spring RMI 简单实现
2011-10-14 13:47 1066好久没有写java代码了,最近工作项目上需要做一个 ... -
一道关于树的面试题
2011-10-13 15:31 949记得不久以前有道面试题,要求下面的数据结构 ... -
一个简单的JAVA后台程序框架
2011-10-13 09:31 1788本项目已经通过git进行版本管理,checkout ... -
测试驱动开发:红、绿、重构
2011-10-12 23:01 901在读Ruby on Rails Tutorial: ... -
Spring JMS和ActiveMQ的应用
2011-10-12 22:43 2082笔者近期参与一个分析log的项目。主要流程是:读取Lo ...
相关推荐
在“我和JAVA数据库操作的那些事儿(2)”这篇博文中,作者分享了实际操作的经验和技巧,涵盖了数据库连接、SQL语句执行、结果集处理以及对象模型的构建。 首先,JDBC是Java平台的标准接口,它允许Java应用程序与...
Java程序员在IT行业中占据着重要的地位,他们负责...总的来说,"java程序员的那些事儿"涉及了广泛的知识领域,从编程语言本身到与之相关的框架、工具和最佳实践。不断学习和适应新技术,是Java程序员保持竞争力的关键。
"Java编程那些事儿"无疑是对这个强大语言的深入探讨,旨在帮助开发人员提升技能,拓宽视野。这份资料可能是由一系列章节或主题组成的文档,比如基础语法、面向对象编程、异常处理、集合框架、多线程、IO流、网络编程...
3. **集合框架**:Java集合框架的详解,如ArrayList、LinkedList、HashSet、HashMap等容器的使用,以及泛型和接口的概念。 4. **输入/输出流**:Java I/O系统,包括文件操作、网络通信、序列化和对象的输入输出。 ...
在这个压缩包中,我们有两份重要的文件:《Java编程那些事儿》的PDF文档以及两个与软件下载相关的文本和URL。 首先,让我们聚焦于核心的《Java编程那些事儿》PDF文档。这份资料可能包含了Java语言的基础概念,如...
本篇文章将深入探讨如何使用Java实现一个数据库连接池,并分析其中的关键技术和步骤。 首先,我们需要理解数据库连接池的基本工作原理。数据库连接池在初始化时会创建一定数量的数据库连接,这些连接在空闲时会被池...
### Java程序员上班那些事儿 #### 一、求职者与企业之间的认知差异 ##### 1.1 应聘者的自我定位问题 - **高大全式的技能列表**:许多求职者在简历上罗列了大量的技能,从传统编程语言如C++、VB,到现代技术如Java...
本压缩包文件“关于Java和Python爬虫那些事儿.zip”可能包含与使用Java和Python这两种语言进行网络爬虫相关的教程、代码示例或资源。尽管描述中只提到了“python”,但标题暗示了内容可能涵盖两种语言。 首先,让...
- **掌握数据库操作**:需了解如何使用ADO对Access或SQL Server进行操作,并能编写SQL语句。 - **图形界面开发能力**:对于OpenGL或DirectX有一定的了解。 - **Java及Java平台知识**:熟悉J2EE和J2ME等技术框架。 - ...
- **操作系统管理**:具备Linux系统操作能力,同时对Solaris等其他操作系统有一定了解。 - **网络通信**:掌握网络基础知识,能够进行基本的网络调试与维护工作。 - **图形处理与多媒体**:了解OpenGL、DirectX等...
- **企业需求特点**:企业的招聘要求相对集中,如Java编程经验、MySQL数据库使用、Linux操作系统熟练等。这反映了企业在招聘时更加注重特定领域的深度而不是广度。 **求职者的自我认知与定位** - **明确职业定位**...
12. **数据库操作**:了解SQL语言,熟悉常用的数据库如MySQL、Oracle,以及JDBC(Java数据库连接)API,能够进行数据的增删改查操作。 13. **网络编程**:理解TCP/IP协议,能够使用Socket进行网络通信,为构建...
根据给定的信息,“Java程序员上班那点事儿”这一标题与描述指向了关于Java程序员日常工作的一些常见情况、挑战或趣事等内容。尽管提供的具体内容部分没有直接给出相关知识点,但结合标题和描述,我们可以围绕Java...
这是一个很好的起点,适合那些希望深入理解Java Swing库和数据库操作的初学者。 首先,让我们详细了解Java GUI。Java Swing库提供了大量的组件,用于构建用户界面,如按钮(JButton)、文本框(JTextField)、表格...