Oracle的數據庫驅動有兩種,oci和thin,沒有用過oci,這里用的是thin。 問題描述: 有一張表MESSAGE,里面有個字段是content varchar2(4000)。 如果直接用Statement來更新這個字段,是不會用任何問題的。因為Statement的原理就是直接將整個sql語句發到數據庫執行。 但是,如果用PreparedStatement的setString()方法來設置這個字段,就有問題了,當設置的字符串長度大于2000時,就會報錯:”java.sql.SQLException: 數據大小超出此類型的最大值“。 問題分析: 這些天學著用iBatis,就遇到了上面的這個問題。因為如果在iBatis中使用內聯參數(#做占位符),底層實現的時候就是用的PreparedStatement來實現的,所以就遇到了上面的問題。 至于為什么用PreparedStatement會出先這個問題,JavaEye上的一篇文章《當PreparedStatement中的參數遇到大字符串》給出了很好的解釋(牛逼啊!)
(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在轉換過程中對字符串的長度做了限制。這個限制和數據庫中字段的實際長度沒有關系。 所以,setCharacterStream()方法可以逃過字符轉換限制,也就成為了解決此問題的方案之一。 而JDBC對轉換字符長度的限制是為了轉換過程中的數據擴展。 問題解決: 根據上面的分析,可以用PreparedStatement的setCharacterStream()方法來解決問題。 在iBatis中,可以使用下面的自定義類型處理器來處理(參考文章《用ibatis向oracle數據庫的varchar2(4000)列寫值的問題》)
/**
* String implementation of TypeHandler
*/
public class StringTypeHandler extends BaseTypeHandler implements TypeHandler {
/**
* 在使用oracle時,如果在一個varchar2(4000)的字段上插入的字符過長(一般只能插入666個漢字,視字符集不同會有所不同),
* 會導致失敗,具體報錯會因驅動版本不同而不同。
* 如果使用setCharacterStream則可以插入最多的字符。因此,我將這個方法改了一下。
*/
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;
}
}
其實,上面的解決方案是比較麻煩的,今天發現了一個更好的解決方案,就是將oracle jdbc驅動換成用于oracle10g第二版的驅動ojdbc14.jar。就行了,唉,前面折騰了幾天的問題竟然可以這么容易地解決...
文章來源:http://localhost/wp2/?p=63