iBatis操作CLOB类型字段时报setString can only process strings of less than 32766 chararacters

最近生产环境上因数据库某一字段类型由VARCHAR2(4000)改为CLOB后,报如下异常:

Caused by: java.sql.SQLException: setString can only process strings of less than 32766 chararacters
	at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:125)
	at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:162)
	at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:227)
	at oracle.jdbc.driver.OraclePreparedStatement.setStringInternal(OraclePreparedStatement.java:4746)
	at oracle.jdbc.driver.OraclePreparedStatement.setString(OraclePreparedStatement.java:4717)
	at com.jolbox.bonecp.PreparedStatementHandle.setString(PreparedStatementHandle.java:1029)
	at com.ibatis.sqlmap.engine.type.StringTypeHandler.setParameter(StringTypeHandler.java:30)
	at com.ibatis.sqlmap.engine.type.UnknownTypeHandler.setParameter(UnknownTypeHandler.java:69)
	at com.ibatis.sqlmap.engine.mapping.parameter.BasicParameterMap.setParameter(BasicParameterMap.java:165)
	at com.ibatis.sqlmap.engine.mapping.parameter.BasicParameterMap.setParameters(BasicParameterMap.java:125)
	at com.ibatis.sqlmap.engine.execution.SqlExecutor.executeUpdate(SqlExecutor.java:79)
	at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.sqlExecuteUpdate(GeneralStatement.java:200)
	at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeUpdate(GeneralStatement.java:78)

从异常信息观察,应该是com.ibatis.sqlmap.engine.type.StringTypeHandler.setParameterv即iBatis报出来的,通过代码跟踪,进入com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.class类中的update方法如下:

public int update(SessionScope session, String id, Object param) throws SQLException {
    int rows = 0;

    MappedStatement ms = getMappedStatement(id);
    Transaction trans = getTransaction(session);
    boolean autoStart = trans == null;

    try {
      trans = autoStartTransaction(session, autoStart, trans);

      RequestScope request = popRequest(session, ms);
      try {
        rows = ms.executeUpdate(request, trans, param);
      } finally {
        pushRequest(request);
      }

      autoCommitTransaction(session, autoStart);
    } finally {
      autoEndTransaction(session, autoStart);
    }

    return rows;
  }

继续跟踪到com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.class类中,请看executeUpdate方法如下:

parameterObject = validateParameter(parameterObject);

      Sql sql = getSql();

      errorContext.setMoreInfo("Check the parameter map.");
      ParameterMap parameterMap = sql.getParameterMap(request, parameterObject);

      errorContext.setMoreInfo("Check the result map.");
      ResultMap resultMap = sql.getResultMap(request, parameterObject);
注意 ParameterMap parameterMap = sql.getParameterMap(request, parameterObject);这句  ,继续跟踪,在 com.ibatis.sqlmap.engine.mapping.sql.dynamic.DynamicSql.class中有如下方法,注意 dynSql = new SimpleDynamicSql(this.delegate, dynSql).getSql(request, parameterObject);这句

private void process(RequestScope request, Object parameterObject) {
  SqlTagContext ctx = new SqlTagContext();
  List localChildren = this.children;
  processBodyChildren(request, ctx, parameterObject, localChildren.iterator());

  BasicParameterMap map = new BasicParameterMap(this.delegate);
  map.setId(request.getStatement().getId() + "-InlineParameterMap");
  map.setParameterClass(((GeneralStatement)request.getStatement()).getParameterClass());
  map.setParameterMappingList(ctx.getParameterMappings());

  String dynSql = ctx.getBodyText();

  if (SimpleDynamicSql.isSimpleDynamicSql(dynSql)) {
    dynSql = new SimpleDynamicSql(this.delegate, dynSql).getSql(request, parameterObject);
  }

  request.setDynamicSql(dynSql);
  request.setDynamicParameterMap(map);
}
继续跟踪到箭头所示 getSql(request, parameterObject)就去里,找着如下代码:

iBatis操作CLOB类型字段时报setString can only process strings of less than 32766 chararacters_第1张图片

这里应该是获取对应的类型转换器,继续跟踪到com.ibatis.sqlmap.engine.type.TypeHandlerFactory.class类中,发现该类创建了所有默认的类型转换器。

register(String.class, new StringTypeHandler());
register(String.class, "CLOB", new CustomTypeHandler(new ClobTypeHandlerCallback()));
register(String.class, "LONGVARCHAR", new CustomTypeHandler(new ClobTypeHandlerCallback()));
register(BigDecimal.class, new BigDecimalTypeHandler());
register([B.class, new ByteArrayTypeHandler());
register([B.class, "BLOB", new CustomTypeHandler(new BlobTypeHandlerCallback()));
register([B.class, "LONGVARBINARY", new CustomTypeHandler(new BlobTypeHandlerCallback()));
register(Object.class, new ObjectTypeHandler());
register(Object.class, "OBJECT", new ObjectTypeHandler());
register(java.util.Date.class, new DateTypeHandler());
register(java.util.Date.class, "DATE", new DateOnlyTypeHandler());
register(java.util.Date.class, "TIME", new TimeOnlyTypeHandler());
很明显,CLOB类型调了自定义类型转换器 new CustomTypeHandler(new ClobTypeHandlerCallback())),于是继续跟踪到ClobTypeHandlerCallback.class里面,发现有如下代码:

public void setParameter(ParameterSetter setter, Object parameter) throws SQLException{
  String s = (String)parameter;
  if (s != null) {
    StringReader reader = new StringReader(s);
    setter.setCharacterStream(reader, s.length());
  } else {
    setter.setString(null);
  }
}

看来该转换器是调用setCharacterStream以流的方式实现的,这样对于CLOB的处理应该是没有问题的,但实际项目为什么会报异常呢,经过摸索发现,可以按钮如下的sqlMap.xml中写,然后测试,问题解决,注意desc属性对应的写法 DESC = #desc,handler=com.ibatis.sqlmap.engine.type.ClobTypeHandlerCallback# 

<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="iBatisTest">
	<typeAlias alias="testDO" type="com.shixing.domain.TestDO" />
	<resultMap id="TestResult" class="testDO">
		<result property="id" column="ID" />
		<result property="desc" column="DESC" />
		<result property="version" column="VERSION" />
	</resultMap>
    <update id="updateItem" parameterClass="testDO">
		<![CDATA[UPDATE TEST_TABLE]]>
		<dynamic prepend="SET">
			<isNotEmpty prepend="," property="desc">
				<![CDATA[DESC = #desc,handler=com.ibatis.sqlmap.engine.type.ClobTypeHandlerCallback#]]>
			</isNotEmpty>
			<isNotEmpty prepend="," property="version">
				<![CDATA[VERSION = #version#]]>
			</isNotEmpty>
		</dynamic>
		<![CDATA[WHERE id = #id#]]>
	</update>
</sqlMap>

对于insert同样的方法处理,我测试的项目中select语句没有报错,如报错select中比较直观,可以按如下方法

<result property="desc" column="DESC" typeHandler="com.ibatis.sqlmap.engine.type.ClobTypeHandlerCallback"/>

本人测试环境:

iBatis: ibatis-sqlmap-2.3.0

Spring:Spring-*-3.0.5.REALEASE

Oracle: oracle11g

Oracle驱动: ojdbc14






你可能感兴趣的:(session,object,String,ibatis,null,less)