Oracle的數(shù)據(jù)庫驅(qū)動(dòng)有兩種,oci和thin,沒有用過oci,這里用的是thin。 問題描述: 有一張表MESSAGE,里面有個(gè)字段是content varchar2(4000)。 如果直接用Statement來更新這個(gè)字段,是不會(huì)用任何問題的。因?yàn)镾tatement的原理就是直接將整個(gè)sql語句發(fā)到數(shù)據(jù)庫執(zhí)行。 但是,如果用PreparedStatement的setString()方法來設(shè)置這個(gè)字段,就有問題了,當(dāng)設(shè)置的字符串長度大于2000時(shí),就會(huì)報(bào)錯(cuò):”java.sql.SQLException: 數(shù)據(jù)大小超出此類型的最大值“。 問題分析: 這些天學(xué)著用iBatis,就遇到了上面的這個(gè)問題。因?yàn)槿绻趇Batis中使用內(nèi)聯(lián)參數(shù)(#做占位符),底層實(shí)現(xiàn)的時(shí)候就是用的PreparedStatement來實(shí)現(xiàn)的,所以就遇到了上面的問題。 至于為什么用PreparedStatement會(huì)出先這個(gè)問題,JavaEye上的一篇文章《當(dāng)PreparedStatement中的參數(shù)遇到大字符串》給出了很好的解釋(牛逼?。。?/p>
(1)Using the JDBC thin driver (2)Using binds (not defines) (3)Using SQL CHAR datatypes (4)Connecting to a database whose character set is neither ASCII (US7ASCII) nor ISO Latin1 (WE8ISO8859P1) When the database character set is neither US7ASCII nor WE8ISO8859P1, the JDBC thin driver converts Java UTF-16 characters to UTF-8 encoding bytes for SQL CHAR binds. The UTF-8 encoding bytes are then transferred to the database, and the database converts the UTF-8 encoding bytes to the database character set encoding.” 原來是JDBC在轉(zhuǎn)換過程中對(duì)字符串的長度做了限制。這個(gè)限制和數(shù)據(jù)庫中字段的實(shí)際長度沒有關(guān)系。 所以,setCharacterStream()方法可以逃過字符轉(zhuǎn)換限制,也就成為了解決此問題的方案之一。 而JDBC對(duì)轉(zhuǎn)換字符長度的限制是為了轉(zhuǎn)換過程中的數(shù)據(jù)擴(kuò)展。 問題解決: 根據(jù)上面的分析,可以用PreparedStatement的setCharacterStream()方法來解決問題。 在iBatis中,可以使用下面的自定義類型處理器來處理(參考文章《用ibatis向oracle數(shù)據(jù)庫的varchar2(4000)列寫值的問題》)
/**
* String implementation of TypeHandler
*/
public class StringTypeHandler extends BaseTypeHandler implements TypeHandler {
/**
* 在使用oracle時(shí),如果在一個(gè)varchar2(4000)的字段上插入的字符過長(一般只能插入666個(gè)漢字,視字符集不同會(huì)有所不同),
* 會(huì)導(dǎo)致失敗,具體報(bào)錯(cuò)會(huì)因驅(qū)動(dòng)版本不同而不同。
* 如果使用setCharacterStream則可以插入最多的字符。因此,我將這個(gè)方法改了一下。
*/
public void setParameter(PreparedStatement ps, int i, Object parameter,
String jdbcType) throws SQLException {
//原來的代碼
//ps.setString(i, ((String) parameter));
//以下是我加的代碼,為了盡量提高效率,作了一下判斷
String s = (String) parameter;
if (s.length() < 667) { //以字符中全是漢字為底線。
ps.setString(i, s);
} else {
ps.setCharacterStream(i, new StringReader(s), s.length());
}
}
public Object getResult(ResultSet rs, String columnName) throws
SQLException {
Object s = rs.getString(columnName);
if (rs.wasNull()) {
return null;
} else {
return s;
}
}
public Object getResult(ResultSet rs, int columnIndex) throws SQLException {
Object s = rs.getString(columnIndex);
if (rs.wasNull()) {
return null;
} else {
return s;
}
}
public Object getResult(CallableStatement cs, int columnIndex) throws
SQLException {
Object s = cs.getString(columnIndex);
if (cs.wasNull()) {
return null;
} else {
return s;
}
}
public Object valueOf(String s) {
return s;
}
}
其實(shí),上面的解決方案是比較麻煩的,今天發(fā)現(xiàn)了一個(gè)更好的解決方案,就是將oracle jdbc驅(qū)動(dòng)換成用于oracle10g第二版的驅(qū)動(dòng)ojdbc14.jar。就行了,唉,前面折騰了幾天的問題竟然可以這么容易地解決...
文章來源:http://localhost/wp2/?p=63