一·异常信息:

===2018-01-24 10:36:10.256 ERROR org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler Line:37  - Unexpected error occurred invoking async method 'public void com..service.SmsCallBackListenner.callback(com..pojo.SendInterfaceListEvent)'.
org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [INSERT INTO SEND (..) VALUES(?,?,?,?,?,?,?,?,?,?,?);]; SQL state [null]; error code [0]; Error; nested exception is java.sql.SQLException: Error
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:90)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:82)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:82)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:655)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:668)
	at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:956)
	at com..service.SmsCallBackListenner.insertSms(SmsCallBackListenner.java:44)
	at com..service.SmsCallBackListenner.callback(SmsCallBackListenner.java:40)
	at com..service.SmsCallBackListenner$$FastClassBySpringCGLIB$$7d92c1dc.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:115)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.sql.SQLException: Error
	at com.alibaba.druid.pool.DruidDataSource.handleConnectionException(DruidDataSource.java:1392)
	at com.alibaba.druid.pool.DruidPooledConnection.handleException(DruidPooledConnection.java:136)
	at com.alibaba.druid.pool.DruidPooledStatement.checkException(DruidPooledStatement.java:71)
	at com.alibaba.druid.pool.DruidPooledPreparedStatement.setClob(DruidPooledPreparedStatement.java:607)
	at com..dao.InsertSmsSendBatchPreparedStatementSetter.setValues(InsertSmsSendBatchPreparedStatementSetter.java:29)
	at org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:966)
	at org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:956)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:639)
	... 13 common frames omitted
Caused by: java.lang.ClassCastException: com.alibaba.druid.proxy.jdbc.ClobProxyImpl cannot be cast to oracle.sql.CLOB
	at oracle.jdbc.driver.OraclePreparedStatement.setClob(OraclePreparedStatement.java:6805)
	at oracle.jdbc.driver.OraclePreparedStatementWrapper.setClob(OraclePreparedStatementWrapper.java:162)
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_setClob(FilterChainImpl.java:3054)
	at com.alibaba.druid.filter.FilterAdapter.preparedStatement_setClob(FilterAdapter.java:1195)
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_setClob(FilterChainImpl.java:3051)
	at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.setClob(PreparedStatementProxyImpl.java:297)
	at com.alibaba.druid.pool.DruidPooledPreparedStatement.setClob(DruidPooledPreparedStatement.java:605)
	... 17 common frames omitted

二·背景描述

oracle 数据库,查询带有clob类型字段的A表,并存储到 实体类,实体类对应的字段 类型也为 oracle.sql.CLOB。
经过业务处理后 把该字段的值插入到 到 B表 的也为clob类型字段时  报出 如上异常。
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid-spring-boot-starter</artifactId>
   <version>1.1.1</version>
</dependency>
<dependency>
   <groupId>com.oracle</groupId>
   <artifactId>ojdbc6g</artifactId>
   <version>11.2.0.4.0</version>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

三·异常分析

主要是 使用的druid 连接池导致的,JdbcTemplate本身没有直接关系。
我们看到错误日志 ,有一个类型转换异常。
Caused by: java.lang.ClassCastException: com.alibaba.druid.proxy.jdbc.ClobProxyImpl cannot be cast to oracle.sql.CLOB
oracle 数据库驱动 clob 使用的是oracle.sql.CLOB 而
 Druid 我们 从  ClobProxyImpl  和 DruidPooledPreparedStatement 可以看到  使用的是 java.sql.Clob
而 这并没有关系 ,会在 驱动包里的 OraclePreparedStatement 里把clob 强转成 CLOB

继续看源码 :在FilterChainImpl 里的connection_createClob 方法:

    @Override
    public Clob connection_createClob(ConnectionProxy connection) throws SQLException {
        if (this.pos < filterSize) {
            return nextFilter().connection_createClob(this, connection);
        }

        Clob clob = connection.getRawObject().createClob();

        return wrap(connection, clob);
    }
最后 return的 是 FilterChainImpl 下的 wrap:

    public ClobProxy wrap(ConnectionProxy conn, Clob clob) {
        if (clob == null) {
            return null;
        }

        if (clob instanceof NClob) {
            return wrap(conn, (NClob) clob);
        }

        return new ClobProxyImpl(dataSource, conn, clob);
    }

导致 类型转换异常的原因就是这样。 至于为什么这么做,并不清楚,可能我们使用的姿势并不对,druid 可能并不允许我们这么做。

四·解决方案

从数据库读取的clob字段的时候 ,实体类 相应字段 使用String 类型存储,读取的时候 也 通过getSting 去 获取clob的值:
    @Override
    public Object mapRow(ResultSet resultSet, int i) throws SQLException {
        SendInterface sendInterface = new SendInterface();
        sendInterface.setSendId(resultSet.getString("SEND_ID"));
        sendInterface.setMobileTo(resultSet.getString("MOBILE_TO"));
        sendInterface.setSendMsg(resultSet.getString("SEND_MSG"));
        sendInterface.setExtendNumber(resultSet.getString("EXTEND_NUMBER"));
        sendInterface.setSysTime(resultSet.getDate("SYS_TIME"));
        sendInterface.setPreSendTime(resultSet.getDate("PRE_SEND_TIME"));
        return sendInterface;
    }
存储 字符串 在 表 clob字段的时候 也不需要你把 String 转为 clob 再存 ,因为Druid 都 替你做了,



DruidLobCreator:

    @Override
    public void setClobAsAsciiStream(PreparedStatement ps, int paramIndex, InputStream asciiStream, int contentLength)
                                                                                                                      throws SQLException {
        if (asciiStream != null) {
            Clob clob = ps.getConnection().createClob();

            OutputStream out = clob.setAsciiStream(1);

            final int BUFFER_SIZE = 4096;
            try {
                byte[] buffer = new byte[BUFFER_SIZE];
                int bytesRead = -1;
                while ((bytesRead = asciiStream.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                }
                out.flush();
            } catch (Exception e) {
                throw new SQLException("setClob error", e);
            } finally {
                JdbcUtils.close(asciiStream);
                JdbcUtils.close(out);
            }

            ps.setClob(paramIndex, clob);
        } else {
            ps.setClob(paramIndex, (Clob) null);
        }
    }
    @Override
    public void setClobAsString(PreparedStatement ps, int paramIndex, String content) throws SQLException {
        Clob clob = ps.getConnection().createClob();
        clob.setString(1, content);
        ps.setClob(paramIndex, clob);
    }



GitHub 加速计划 / druid / druid
27.83 K
8.56 K
下载
阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池
最近提交(Master分支:3 个月前 )
f060c270 - 17 天前
1613a765 * Improve gaussdb ddl parser * fix temp table 19 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐