设为首页 收藏本站
查看: 865|回复: 0

[经验分享] oracle preparedStatement ResultSet.Type_Scroll_Insensitive 乱码

[复制链接]

尚未签到

发表于 2016-7-28 07:11:07 | 显示全部楼层 |阅读模式
  问题:
  我用的数据库是 oracle 字符集 us7ascii
用preparedStatement(sqlString, ResultSet.Type_Scroll_Insensitive, ResultSet.Concur_read_only)
从数据库中 读取数据后 中文字段乱码,不管是转不转码都是乱码
但是如果preparedStatement(sqlString) 转码成GBK后就能正常显示中,服了。
  

  查询百度,有说是oracle jdbc驱动的问题,这个还没有验证,有时间验证一下,作参考。
  

  深入理解(原文http://www.iyunv.com/topic/123557
  JDBC2.0后提出了三种不同的cursor类型,用户代码可以在创建Statement指定cursor类型,如下:
Statement createStatement( int resultSetType, int resultSetConcurrency)
cursor类型
ResultSet.TYPE_FORWARD_ONLY
  默认的cursor类型,仅仅支持向前forward,不支持backforward,random,last,first操作,类似单向链表。
   TYPE_FORWARD_ONLY类型通常是效率最高最快的cursor类型
ResultSet.TYPE_SCROLL_INSENSITIVE
  支持backforward,random,last,first操作,对其它数据session对选择数据做出的更改是不敏感,不可见的。
ResultSet.TYPE_SCROLL_SENSITIVE
  支持backforward,random,last,first操作,对其它数据session对选择数据做出的更改是敏感,可见的。
分析
  众所周知,JDBC对数据库进行数据查询时,数据库会创建查询结果的cache和cursor。而数据库的cursor是不支持backforward,random,last,first操作,仅仅只支持向前forward。那么TYPE_SCROLL_INSENSITIVE是如何实现支持backforward,random,last,first的呢?很简单,TYPE_SCROLL_INSENSITIVE的Statement查询把所有fetch的记录都缓存到JVM的Resultset对象内,如果有10个记录,直接跳到最后记录,TYPE_SCROLL_INSENSITIVE方式下把fetch所有记录到jvm端,并缓存下来,再进行random就是在数据库数组里面进行的。这也是TYPE_FORWARD_ONLY类型通常是效率最高最快的cursor类型原因,如果要做一些复杂的功能,必然是要牺牲一些效率的。

    那么为什么TYPE_SCROLL_INSENSITIVE对选择数据做出的更改是不敏感,不可见的呢?前面提到,JDBC对数据库进行数据查询时,数据库会创建查询结果的cache和cursor,如下面sql:
    select name,id from foo
    用jdbc执行上面的sql语句时,数据库会把foo表所有记录的name和id字段缓存到cache中,之后cache和真正的数据库数据文件没有任何联系了,foo表发生的改变在查询完成后不会自动同步到cache上去,因此TYPE_SCROLL_INSENSITIVE对选择数据做出的更改是不敏感,不可见。
    那么TYPE_SCROLL_SENSITIVE是怎么做到其它数据session对选择数据做出的更改是敏感,可见的。上面的sql语句用TYPE_SCROLL_SENSITIVE的Statement来执行,会转化成以下的sql语句:
    select rowid from foo
    数据库这时候是把foo表所有记录的rowid缓存到cache中,用户代码在fetch记录时,再继续做以下查询:
    select name,id from foo where rowid=?
    因此这时候发生的查询是实时从真正的数据库数据文件中取,因此对期间发生的数据更改是可见的,敏感的。但是这种可见性仅限于update操作,而insert和delete同样是不可见的。因为如果查询发生在insert之前,insert生成的rowid并不会反应在cache中的rowid结果集上。在一个记录的rowid已经缓存到cache中,这时候被删除了,但一般数据库的删除是标记删除,也就是说rowid对应那行记录并没有真正从数据库文件中抹去,一般是可以再次取到记录的。
总结
  TYPE_FORWARD_ONLY类型通常是效率最高最快的cursor类型,也是最常用的选择。
    TYPE_SCROLL_INSENSITIVE需要在jvm中cache所有fetch到的记录实体,在大量记录集返回时慎用。
    TYPE_SCROLL_SENSITIVE在jvm中cache所有fetch到的记录rowid,需要进行二次查询,效率最低,开销最大
  
  
  JDBC ResultSet分析(原文http://www.iyunv.com/topic/560109
JDBC1.0、JDBC2.0、JDBC3.0中分别用以下方法创建Statement。
JDBC1.0: createStatement()
JDBC2.0:createStatement(resultSetType,resultSetConcurrency)
JDBC3.0: createStatement(resultSetType, resultSetConcurrency,resultSetHoldability)
 
下面依次分析resultSetType、resultSetConcurrency、resultSetHoldability这几个参数的含义。


一 ResultSetType


      resultSetType的可选值有:ResultSet.TYPE_FORWARD_ONLY、ResultSet.TYPE_SCROLL_INSENSITIVE、ResultSet.TYPE_SCROLL_SENSITIVE。



1:ResultSet.TYPE_FORWARD_ONLY

默认的cursor类型,仅仅支持结果集forward,不支持backforward,random,last,first等操作。  



2:ResultSet.TYPE_SCROLL_INSENSITIVE

支持结果集backforward,random,last,first等操作,对其它session对数据库中数据做出的更改是不敏感的。

实现方法:从数据库取出数据后,会把全部数据缓存到cache中,对结果集的后续操作,是操作的cache中的数据,数据库中记录发生变化后,不影响cache中的数据,所以ResultSet对结果集中的数据是INSENSITIVE的。



3:ResultSet.TYPE_SCROLL_SENSITIVE

支持结果集backforward,random,last,first等操作,对其它session对数据库中数据做出的更改是敏感的,即其他session修改了数据库中的数据,会反应到本结果集中。

 

实现方法:从数据库取出数据后,不是把全部数据缓存到cache中,而是把每条数据的rowid缓存到cache中,对结果集后续操作时,是根据rowid再去数据库中取数据。所以数据库中记录发生变化后,通过ResultSet取出的记录是最新的,即ResultSet是SENSITIVE的。但insert和delete操作不会影响到ResultSet,因为insert数据的rowid不在ResultSet取出的rowid中,所以insert的数据对ResultSet是不可见的,而delete数据的rowid依旧在ResultSet中,所以ResultSet仍可以取出被删除的记录(因为一般数据库的删除是标记删除,不是真正在数据库文件中删除)。



做个试验,验证一下SENSITIVE特性。数据库为oracle10g,驱动为ojdbc14.jar。

test表中数据如下:

  

  

c1c2c3
1c11c21c3
2c12c22c3
3c13c23c3
  

程序如下:
Java代码   DSC0000.png


  • public static void testResultSetSensitive(Connection conn) throws Exception{  
  •   
  •         String sql = "SELECT c1,c2,c3 FROM test";  
  •         try {  
  •             Statement stmt = conn  
  •                     .createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);  
  •               
  •             ResultSet rs = stmt.executeQuery(sql);  
  •               
  •             while (rs.next()) {  
  •                 System.out.println("[行号:" + rs.getRow() + "]\t" + rs.getString(1) + "\t" + rs.getString(2)  
  •                         + "\t" + rs.getString(3));  
  •                 Thread.sleep(20000);  
  •             }  
  •               
  •             rs.close();  
  •             stmt.close();  
  •         } catch (SQLException e) {  
  •             e.printStackTrace();  
  •         } finally {  
  •             try {  
  •                 conn.close();  
  •             } catch (Exception e) {  
  •             }  
  •         }  
  •     }  

  

定义ResultSet为ResultSet.TYPE_SCROLL_SENSITIVE类型,首先执行sql访问数据库,然后执行rs.next()移动游标取数据。在循环里面加上Thread.sleep(20000)的目的是为了我们有时间在后台把数据库里的数据改了。比如当在循环里打印出第一行的数据后,我们在后台,把第三行数据的c3列改成”3uuu”。如果ResultSet真的是敏感的话,那应该取出”3uuu”,而不是原始的“3c3”。但最终的结果却是如下:

 

[行号:1] 1c1   1c2   1c3

[行号:2] 2c1   2c2   2c3

[行号:3] 3c1   3c2   3c3
 
数据没变呀,ResultSet不敏感啊!于是去查阅资料,找了n久,还是在英文文档上找到了答案。原来是fetchsize的问题。调用ResultSet的next方法取数据时,并不是每调用一次方法就去数据库里查一次,而是有个fetchSize,一次取fetchSize条数据。Oracle默认的fetchsize等于10,所以上面的代码在第一次调用rs.next()时,就已经把3条数据都取出来了,所以才会有上面的结果。
 

      第二次实验,在ResultSet rs =stmt.executeQuery(sql);前面加上stmt.setFetchSize(1);将fetchSize设置为1。然后重新第一次实验的步骤,发现最终结果为:

[行号:1] 1c1   1c2   1c3

[行号:2] 2c1   2c2   2c3

[行号:3] 3c1   3c2   3uuu
    原因就是fetchsize设置为1时,每次next取数时都会重新用rowid取数据库里取数据,当然取到的是最新的数据了。
  
     二 ResultSetConcurrency
 
      ResultSetConcurrency的可选值有2个:
      ResultSet.CONCUR_READ_ONLY 在ResultSet中的数据记录是只读的,不可以修改
      ResultSet.CONCUR_UPDATABLE 在ResultSet中的数据记录可以任意修改,然后更新到数据库,可以插入,删除,修改。
 
     三 ResultSetHoldability
   ResultSetHoldability的可选值有2个:
     HOLD_CURSORS_OVER_COMMIT: 在事务commit或rollback后,ResultSet仍然可用。
    CLOSE_CURSORS_AT_COMMIT: 在事务commit或rollback后,ResultSet被关闭。
  
     需要注意的地方:
   1:Oracle只支持HOLD_CURSORS_OVER_COMMIT。
   2:当Statement执行下一个查询,生成第二个ResultSet时,第一个ResultSet会被关闭,这和是否支持支持HOLD_CURSORS_OVER_COMMIT无关。
 
四 验证数据库是否支持ResultSet的各种特性
  
不同的数据库版本及JDBC驱动版本,对ResultSet的各种高级特性的支持是不一样的,我们可以通过以下方法,来验证具体的数据库及JDBC驱动,是否支持ResultSet的各种特性。
    
     DatabaseMetaData dbMeta = conn.getMetaData();
     然后调用DatabaseMetaData对象的以下方法:
      boolean supportsResultSetType(intresultSetType);
      boolean supportsResultSetConcurrency(int type,int concurrency);
       boolean supportsResultSetHoldability(intholdability);
 
    参考的2篇英文文档:
 http://cs.felk.cvut.cz/10gr2/java.102/b14355/jdbcvers.htm(JDBC Standards Support)
 http://download.oracle.com/docs/cd/B10501_01/java.920/a96654/resltset.htm#1023642(Oracle9i JDBC Developer's Guide and Reference Release 2 (9.2))

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-250323-1-1.html 上篇帖子: Oracle重做日志文件管理技巧 下篇帖子: Oracle 10G windows 平台 DataGuard 实例(三)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表