项目从MySQL 到Oracle迁移,针对一个长字符串(中文字数大概有1000以上,不到1w)的存储,经历来太多的波折,终于搞定。
以下是我的一些经验总结
本文中的测试表设计如下:
CREATE TABLE TZB.test_table (
test_id NUMBER(20,0) NOT NULL,
short_str varchar(2000 CHAR) default NULL,
middle_string varchar(4000 CHAR) default NULL,
PRIMARY KEY ( test_id )
) ;
1:如果字符串中文长度在2000或者以下,可以直接使用varchar2(2000 char)。
(1)在使用ojdbc14.jar的情况下,
在使用Statement方式插入时,定义为2000 char的数据列,可以插入2000个中文字符,但是通过PL/SQL是看不到该行。只能插入2000个英文字符。
在使用PreparedStatement方式插入时,定义为2000 char的数据列,可以通过批处理插入字符串长度小于或等于666个中文字符。
当汉字字符串长度大于666,插入数据必须使用流式方式,且最大长度不能超过2000个中文字符。当采用流式方式插入数据时,不能使用批处理。
原因是PreparedStatement.setString(index, StringValue)方法对StringValue的char长度有限制,并且一个中文字符占3个char,故只能输入666个汉字。
插入英文字符时,至多只能插入2000个。
(2)在使用ojdbc6.jar的情况下
在使用Statement方式插入时,定义为2000 char的数据列,可以插入2000个中文字符,但是通过PL/SQL是看不到该行。只能插入2000个英文字符。
在使用PreparedStatement方式插入时,可以插入2000个中文字符或者4000个英文字符,并且可以使用批处理,对英文字符串是否采用流式处理,没有限制。
(3)批处理的方法如下:
String test_str="这是一个测试字符串......";
String sql_insert = "insert into tzb.test_table (test_id, short_str) values (?,?)";
Connection conn = null;
PreparedStatement pstmt = null;
Statement stmt=null;
try {
conn= ......; //获取链接
pstmt = conn.prepareStatement(sql_insert);
for(int i=8; i字符串......";
String sql_insert = "insert into tzb.test_table (test_id, short_str) values (?,?)";
......
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn= ......; //获取链接
pstmt = conn.prepareStatement(insert_sql);
pstmt.setLong(1, 12);
//这里的length不是字符串.length(),而是有多少个char。很多人避免计算char长度,直接写成4000。
pstmt.setCharacterStream(2, new StringReader(需要保存的字符串), length);
//pstmt.setCharacterStream(2, new StringReader(需要保存的字符串), 4000);
pstmt.execute();
pstmt.close();
} catch (Exception e) {
// 异常处理,如
e.printStackTrace();
}finally{
try{
pstmt.close();
//关闭连接。如果使用连接池,则应归还连接
conn.close();
}catch(Exception e){
// 异常处理
}
}
2、当插入的字符串长度大于2000不多余4000 char时,可以直接使用varchar2(4000 char)。
(1)使用ojdbc14.jar时,
在直接使用Statement方式插入时,test_str最大长度可达2000个中文字符,4000个英文字符。
在使用PreparedStatement方式插入时,定义为4000 char的数据列,可以通过批处理插入字符串长度小于或等于666个中文字符。
当汉字字符串长度大于666,插入数据必须使用流式方式,但不能用批处理,且最大长度不能超过2000个中文字符。
插入英文字符时,当英文字符长度超过2000,必须采用流式方式,且至多只能插入4000个。
(2)在使用ojdbc6.jar时,无论使用流式存储与否,插入的字符串汉字长度不能超过2000,但可以插入4000个英文字符。
3:如果字符串长度不是很长,一个字段又装不下,可以将一个字符串分解成几段分别存储在数据表中,读取时进行合并即可。
4:如果使用了Spring2.5+Hibernate3框架,最好使用Spring提供的透明处理Clob的方法,这样将更加容易移植程序而不用修改代码。
具体做法如下:
1).在sessionFactory中加入lobHandler的注射:
xxx.hbm.xml
.....
dataSource是数据源Bean的配置,且有一定的限制。
(1)数据库为oracle9i,驱动程序使用ojdbc14.jar,在使用dbcp连接池的情况下,会抛异常:
org.springframework.transaction.TransactionSystemE xception: Could not roll back Hibernate transaction;
Caused by: org.hibernate.TransactionException: JDBC rollback failedorg.hibernate.Transaction:JDBC rollback failed
Caused by: java.sql.SQLException: Io 异常: Connection reset by peer: socket write error
(2)数据库为oracle9i,在本段介绍的解决方案中不能使用C3P0连接池(不管使用ojdbc14.jar,或者ojdbc6.jar),否则将抛出异常:
org.springframework.dao.InvalidDataAccessApiUsageE xception: OracleLobCreator needs to work on [oracle.jdbc.OracleConnection], not on [com.mchange.v2.c3p0.impl.NewProxyConnection]
Caused by: java.lang.ClassCastException: com.mchange.v2.c3p0.impl.NewProxyConnection cannot be cast to oracle.jdbc.OracleConnection
(3)使用JNDI数据源(暂时没有用过)
2).定义这个lobHandler,值得注意的是这里有Oracle的版本区别:
因为Oracle9i处理Clob的方式和别的数据库很不一样,甚至与Oracle10g都不兼容,所以这里要用spring提供的SimpleNativeJdbcExtractor.如果使用Oracle10g的话,可以直接使用:
对应的应该使用Oracle10g对应的JDBC驱动.
3).在领域对象的hbm中对应的Clob字段应该使用这样的定义:
该字段在领域对象中直接申明成String就可以了.当对这个字段写入长数据时直接调用其set方法就可以了,Spring会自己帮你做余下的处理,让你透明的处理Clob字段.
4).DAO层或者业务逻辑层对该字段的操作必须需要在有事务管理的方法中使用
相关资源:七夕情人节表白HTML源码(两款)