优化JDBC编程-多提提意见
优化JDBC编程
这是我根据MS SQL SERVER 2000 JDBC DRIVER HELP,并参考其它资料整理而成。ms的这个帮助文件实在有失大家风范,示例代码很.....有兴趣者可以去下载http://download.microsoft.com/download/SQLSVR2000/jdbc/2000/NT45XP/EN-US/setup.exe。由于本人水平有限,文中不当之处请大家批评指正。
1.尽量减少对数据库元数据方法的使用
同样是产生一个ResultSet对象,DatabaseMetaData 对象的方法比其它JDBC方法相对要慢,因此平繁使用该方法会降低系统的性能。在程序中应当对产生的结果集信息进行高速缓存,比如将getTypeInfo()返回的结果集存入Vector或Hashtable中,这样可大大提高程序的效率。
2.应避免的方法调用模式
在方法调用时应当尽量避免传入null做为参数,虽然有时能执行成功,但这对DB Server负担很重。其实在很多情况下所需的参数是已知的。比如:
//这里略去了捕获违例代码(下同)。
DatabaseMetaData md=...;
ResultSet rs=md.getTables(null,null,"authors",null);//取得MS SQL SERVER pubs数据库中authors表的信息.
应当写成:
ResultSet rs=md.getTables("northwind","dbo","authors",new String[]{"TABLE"});
这样使程序更有效可靠。
3.使用哑查询语句来取得表的相关特征信息
一个哑查询语句(Dummy Query,译为哑查询不知是否恰当,愿与大家探讨)不会产生有记录的结果集,比如:select * from tableName where 1=0,因为条件永不成立,DB Server 不会执行这条语句。因此,在不需产生记录行的情况下,哑查询能极大地提高程序的执行效率。比如我们要了解一个表的有关列信息时,上面的语句比select * from tableName这个语句要高效得多,后者数据库服务器要检索所有的行并返回一个记录集,而前者不需要。针对这一问题,JDBC可以有以下两种方法:
case 1:使用getColumns()方法
//getColumns()是DatabaseMetaData的一个方法,其有关信息请查阅JDK1.3文档
ResultSet rs=md.getColumns("pubs","dbo","authors",...);//返回一个有记录的结果集
while(rs.next())//通过滚动结果集取得列名
System.out.println(rs.getString(4));
case 2:使用getMetaData()方法
Statement stmt=conn.createStatement();
//数据库服务器永远不会执行这条查询语句
ResultSet rs=stmt.executeQuery("select * from authors where 1=0");
ResultSetMetaData rsmd=rs.getMetaData();
int colCount=rsmd.getColumnCount();//取得列数
for(int col=1;col<=colCount;col++)
System.out.println(rsmd.getColumnName(col));
//!这里列的顺序是select后列出现的顺序,并不一定与表中列顺序对应
通过以上的分析,第二种方法应是我们的选择。
4.关于存储过程的调用
由于所有的JDBC驱动总是将SQL语句作为字符串发送到数据库服务器,数据库服务器经过语法分析、参数类型验证,然后将参数转换成正确的数据类型再去执行。比如有这么一个存储过程:
CallableStatement cstmt=conn.prepareCall("{call getCustomerName(123)}");
//获得指定id的客户的名字,输入参数,id是个正整数
ResultSet rs=cstmt.executeQuery();
在这里我们认为123是一个正整数,但实际"call getCustomerName(123)"作为字符串整个被发送到数据库服务器端,数据库服务器经过分析,离析出"123"将其转换为整数型值再做为参数送给存储过程执行。很明显,这样效率极低,因为我们把已知的东西仍要服务器去判断,这无疑额外加重了服务器的负担。做为优化也是我们常见的存储过程的调用方法应是:
CallableStatement cstmt=conn.prepareCall("call getCustomerName(?)");
cstmt.setLong(1,123);//将值和类型信息编码后发送
ResultSet rs=cstmt.executeQuery();
//do something
5.正确使用Statement和PreparedStatement对象及其execute方法
Statement 对象是为仅执行一次的查询语句优化而设计的,PreparedStatement 对象是为两次或更多次执行同一查询语句而设计的。PreparedStatement 对象第一次执行一个准备好的查询要花一定的代价,然而它带来的好处是为以后的查询加快了速度;因为SQL语句已经进行编译并放入高速缓存,你可以一直重复使用;想要改变查询条件获得不同的结果集只需用setXXX方法改变主机变量(?)的值就行了。
由于PreparedStatement 及 CallableStatement都是Statement的子类,所以它们都有execute(String sql),executeQuery(String sql),executeUpdate(String sql),executeBatch()方法。
execute(String sql)方法返回一个boolean值,它执行任意复杂的sql语句,可以产生多个结果集。如果有结果产生返回 true,如果没有结果集产生或仅是一个更新记数则返回 false。它产生的结果集可以通过getResultSet()和getMoreResults()获得,更新记数可通过getUpdateCount()获得。显然execute(String sql)方法的使用要复杂一些,因此如果只是简单的查询或更新操作请使用executeQuery(String sql)和executeUpdate(String sql)方法。executeUpdate(String sql)能执行INSERT,UPDATE,DELETE语句,及DDL和DML命令(此时返回值为0)。
如果需要进行更多的更新操作,只需将这些更新命令打包后一起提交给数据库,数据库一次处理所有的请求,这比逐条提交要高效得多。例如:
//保存当前提交模式
boolean commitState=conn.getAutoCommit();
// 关闭自动提交模式
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
//逐条加入
stmt.addBatch("INSERT INTO employees VALUES (1000, 'Joe Jones')");
stmt.addBatch("INSERT INTO departments VALUES (260, 'Shoe')");
stmt.addBatch("INSERT INTO emp_dept VALUES (1000, 260)");
// 一次提交
int[] updateCounts = stmt.executeBatch();
conn.commit();//使更新生效
conn.setAutoCommit(commitState);//恢复原来的提交模式
PreparedStatement和CallalbeStatement对象的使用基本与Statement一样,请参阅JDBC2.1API。
6.正确的使用游标
JDBC2.1核心API提供三种结果集类型:forward-only, scroll-insensitive和scroll-sensitive。
forward-only:仅向前型。如果你仅需要前向顺序滚动结果集中的所有行,仅向前游标能提供极高的性能;然而它不能从第一行上直接滚动到最后一行,也不能从最后一行滚到第一行。
scroll-insensitive:滚动不敏感型。对于要求较高处理级别的应用来说,滚动不敏感型结果集是一个理想的选择,它支持向前和向后的记录集滚动。滚动不敏感型结果集的第一次请求是从数据库服务端取得所有满足条件的行,然后将它存储在客户端,也就是说是一个包含数据的客户端静态视图;虽然以后的操作比较快,但数据库服务器处理第一次的请求非常慢,尤其是当返回的数据量比较大时。因此,如果返回的只是一行记录我们就不应使用这种游标,使用仅向前就满足要求了;相反,如果返回的记录非常多,也不推荐使用这种游标,因为这些数据都存放在内存里,大量的数据将很快使内存耗尽。有些滚动不敏感游标的实现是将数据缓存到数据库服务器的一个临时表中,以免占用过多的内存资源。
scroll-sensitive:滚动敏感型,有时也叫键集驱动游标。它是在你的数据库上对满足条件的记录行做了一个标识,好像行的主键,当你滚动结果集的时候,只有有标识的数据才会返回。由于每次的请求都要产生一次网络连接,因此速度是很慢的。
7.只返回需要的行或列
听了上周六范生对Oracle核心的剖析,我算是搞清楚了对表的查询或更新,数据库低层操作其实是对磁盘文件的read or write,而I/O操作数据量越大耗时越多,软盘的读写速度大家是有目共睹的。为了避免不必要的数据传输,请小心使用select * from ...这样的语句,如果只需要一列就没必要返回所有的列,特别是当你不需要的列中含有大数据类型(如BINARY,BLOB,CLOB)或者说数据量较大时,会影响系统性能。
8.使用连接池
连接池对数据库访问性能的提高是非常显著地,因为创建和销毁一个连接的代价都非常昂贵。连接池实现了数据库连接的共享,一个连接对象可以被多个用户多次重复使用。由容器管理的连接池就像是一个租赁公司,谁要使用就租给他一个,用完后还给我,下次要用接着出租,这样就免去了每次请求都要造个新的,而用完后又把它扔了。
关于连接池技术较为复杂,不过你也完全可以写自己的连接池对象,如果你看了《JAVA2高级编程》。
[参考资料]
1.MS SQL SERVER 2000 JDBC DRIVER HELP
2.《JAVA2高级编程》
3.《JAVA2核心技术II》
4.《J2EE构建企业系统专家级解决方案》
5.《JSP高级编程》
6.SUN JDBC2.1 API
优化JDBC编程-多提提意见的更多相关文章
- JDBC编程之程序优化
-----------------siwuxie095 首先下载 MySQL 的 JDBC 驱动,下载链接: https://dev.mysql.com/downloads/connector/j/ ...
- Spring+SpringMVC+MyBatis+easyUI整合优化篇(九)数据层优化-jdbc连接池简述、druid简介
日常啰嗦 终于回到既定轨道上了,这一篇讲讲数据库连接池的相关知识,线程池以后有机会再结合项目单独写篇文章(自己给自己挖坑,不知道什么时候能填上),从这一篇文章开始到本阶段结束的文章都会围绕数据库和da ...
- JavaEE之JDBC编程[详解]
1.数据库简介 数据库(DB,Data Base ) 数据库管理系统(DBMS,Data Base Management System) 关系型数据库(RDB) 关系型数据库管理系统(RDBMS) S ...
- JDBC编程之预编译SQL与防注入式攻击以及PreparedStatement的使用教程
转载请注明原文地址: http://www.cnblogs.com/ygj0930/p/5876951.html 在JDBC编程中,常用Statement.PreparedStatement 和 ...
- 数据层优化-jdbc连接池简述、druid简介
终于回到既定轨道上了,这一篇讲讲数据库连接池的相关知识,线程池以后有机会再结合项目单独写篇文章(自己给自己挖坑,不知道什么时候能填上),从这一篇文章开始到本阶段结束的文章都会围绕数据库和dao层的优化 ...
- JDBC编程之预编译SQL与防注入
在JDBC编程中,常用Statement.PreparedStatement 和 CallableStatement三种方式来执行查询语句,其中 Statement 用于通用查询, PreparedS ...
- MySQL之JDBC编程增删改查
MySQL之JDBC 一.JDBC是什么 Java DatabaseConnectivity (java语言连接数据库) 二.JDBC的本质 JDBC是SUN公司制定的一套接口(interface). ...
- 单独使用jdbc编程问题总结(一)
在学习Mybatis之前,我们先来回顾JDBC编程的相关知识.在此基础上深入的学习Mybatis框架.如有错误,敬请指正. (一)首先我们既然要使用jdbc,当然是要操作数据库了.创建一个名为:myb ...
- 浅谈JDBC编程
一.概述 1.为什么要用JDBC 数据库是程序不可或缺的一部分,每一个网站和服务器的建设都需要数据库.对于大多数应用程序员(此处不包含数据库开发人员)来说,我们更多的不是在DBMS中对数据库进行操纵, ...
随机推荐
- pygame模块参数汇总(python游戏编程)
一.HelloWorld pygame.init() #初始函数,使用pygame的第一步: pygame.display.set_mod((600,500),0,32) #生成主屏幕screen:第 ...
- 模拟界面请求到web服务器
客户端 package com.lsw.client; import java.io.*; import java.net.*; import java.util.*; public class HT ...
- jupyter notebook变量高亮
首先声明,anaconda安装的时候,一定要勾选“Add Anaconda to my PATH environment variable”! 否则会有一堆麻烦的问题,做了这一步就能自动添加好路径!不 ...
- 使用Swing的JSpinner组件设置日期时间选择器
代码: //获得时间日期模型 SpinnerDateModel model = new SpinnerDateModel(); //获得JSPinner对象 JSpinner year = new J ...
- [leetcode trie]212. Word Search II
Given a 2D board and a list of words from the dictionary, find all words in the board. Each word mus ...
- 数据预处理:规范化(Normalize)和二值化(Binarize)
注:本文是人工智能研究网的学习笔记 规范化(Normalization) Normalization: scaling individual to have unit norm 规范化是指,将单个的样 ...
- 02-c#基础之01-基础语法(一)
1.注释符 1)注销 2) 解释 2.C#中的3种注释符 1)单行注释// 2)多行注释/*要注释的内容*/ 3)文档注释///多用来解释类或者方法 2.VS中的快捷键
- 模型构建<3>:交叉验证
交叉验证是模型比较选择的一种常用方法,本文对此进行总结梳理. 1.交叉验证的基本思想 交叉验证(cross validation)的基本思想就是重复地利用同一份数据. 2.交叉验证的作用 1)通过划分 ...
- AtCoder Grand Contest 019 F-yes or no
AtCoder Grand Contest 019 F-yes or no 解题思路: 考虑一个贪心策略,假设当前还有 \(x\) 道 \(\text{yes}\) 和 \(y\) 道 \(\text ...
- BZOJ 3956: Count 主席树 可持久化线段树 单调栈
https://www.lydsy.com/JudgeOnline/problem.php?id=3956 从描述可以得到性质: 每个好点对 ( 除了差值为1的好点对 ) 中间的数 ( i , j ) ...