【问题标题】:CLOB : JdbcTemplate : c3p0 - how reuse the same connection?CLOB : JdbcTemplate : c3p0 - 如何重用相同的连接?
【发布时间】:2014-09-28 22:41:10
【问题描述】:

我继承了一个具有以下设置的项目:

声明的类和用于执行查询的方法之一如下所示

public class DataSourceServiceImpl extends SimpleJdbcDaoSupport implements DataSourceService {
...
    public List<Map<String, Object>> valueList(String dataSource, Object[] params, String sql) throws DataAccessException {
        DataSourceContextHolder.setDataSource(dataSource);
        return getSimpleJdbcTemplate().getJdbcOperations().queryForList(sql, params);
    }
}

示例 SQL:

SELECT samplefield FROM sampletable WHERE SDE.ST_INTERSECTS(SHAPE, SDE.ST_GEOMETRY(?, ?)) = 1 AND SHAPE IS NOT NULL

问题是,如果输入字符串(表示几何)超过 Oracle 驱动程序的 4000 个字符限制,我们会得到 ​​p>

ORA-01460:请求的转换未实现或不合理

换句话说,这意味着 queryForList(以及它后面的任何东西)不会自动处理超出限制的字符串。

经过一番研究,我意识到我必须使用 c3p0 OracleUtils 来生成临时 Clob。所以我修改了代码以检查参数并相应地修改它们:

try {           
        Connection conn =  getConnection(); 

        for (Object obj: params){           
            if (obj instanceof String && obj.toString().length() > 4000){           
                Clob clob = OracleUtils.createTemporaryCLOB(conn, true, 10);                    
                clob.setString(1, (String)obj);
                clobs.add(clob);
                params[i] = clob; // re-assign the parameter back                           
            }
            i++;
        }

        List<Map<String, Object>> result = getSimpleJdbcTemplate().getJdbcOperations().queryForList(sql, params);           

        if (!clobs.isEmpty())
            for (Clob c: clobs) c.free();                           

    } catch (SQLException e) {
        e.printStackTrace();
    }

很遗憾,这导致了另一个 Oracle 错误:

ORA-22922:LOB 值不存在

经过另一项研究,我意识到我分配给“params”参数的临时 Clob 的指针是空的,很可能是由于“queryForList”在不同的连接中执行 (!),而不是用于生成临时 Clob 的那个。所以我最终得到了以下结果:

JdbcTemplate t = new JdbcTemplate(new SingleConnectionDataSource(conn, false));
result = t.queryForList(sql, params);

这行得通,但恐怕这不是最佳选择,只会在将来的某个时候引起问题。

我的问题是,有没有办法重用用于为实际查询生成 CLOB 的连接?

【问题讨论】:

    标签: java spring oracle c3p0 clob


    【解决方案1】:

    最简单的做法是避开过于复杂的中间件。

    public List<Map<String, Object>> valueList(String dataSource, Object[] params, String sql) throws DataAccessException {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        Clob clob = null;
    
        try {
          con = dataSource.getConnection();
          ps = con.prepareStatement( "SELECT samplefield FROM sampletable WHERE SDE.ST_INTERSECTS(SHAPE, SDE.ST_GEOMETRY(?, ?)) = 1 AND SHAPE IS NOT NULL" ); // probably externalize this SQL string somewhere
    
          // Note that Oracle's two-arg ST_GEOMETRY function takes a CLOB and an INTEGER
          // see http://resources.arcgis.com/en/help/main/10.1/index.html#//006z00000050000000
          Clob clob = OracleUtils.createTemporaryCLOB(conn, true, 10);                    
          clob.setString(1, (String) params[0]);
          ps.setClob( 1, clob );
    
          ps.setObject( 2, params[1], java.sql.Types.INTEGER );
    
          rs = ps.executeQuery();
    
          // inefficient and overdone output format, but hey.
          List<Map<String,Object>> out = new LinkedList()
          while (rs.next()) {
             Map<String,Object> oneBindingMap = new HashMap(1);
             oneBindingMap.put("samplefield", rs.getObject(1)); //again, maybe externalize the field name
             out.add( oneBindingMap );
          }
          return out;
        }
        catch ( SQLException e ) {
         throw new DataAccessException( e.getMessage(), e ); // adapt to expected Exception type
        }
        finally {
          try { if ( clob != null ) clob.free(); } catch ( Exception e ) { e.printStackTrace(); }
    
          // in java 7+ you could avoid the rest of this via the try-with-resources construct
          try { if ( rs != null ) rs.close(); } catch ( Exception e ) { e.printStackTrace(); }
          try { if ( ps != null ) ps.close(); } catch ( Exception e ) { e.printStackTrace(); }
          try { if ( con != null ) con.close(); } catch ( Exception e ) { e.printStackTrace(); }
        }
    }
    

    我只是在网页中输入这个;我没有测试它是否可以编译,我正在对你想要的语义做出一些假设(例如输出映射中的格式和键名)。但我认为这应该让你非常接近你想要的位置,而不必让你的逻辑复杂化以使其适应不打算做你想做的事情的中间件。

    另外,我认为您的 SingleConnectionDataSource hack 没有太多错误或可能会失败。它只是让我觉得不透明。我的解决方案更加冗长,但我认为逻辑上非常简单,任何了解 JDBC 的人都可以进行维护。

    祝你好运!

    【讨论】:

    • 感谢您的回复,我之前有过这个解决方案(使用preparedStatement),但并不想弄乱结果集,想要保持整洁(尽管我已经结束了混乱与)感谢绑定映射:)。不过,有没有办法在查询操作中告诉 jdbctemplate 使用我已经拥有的连接?或者任何可以为我做这一切的任何地方? .. 就像以前有 SetBigStringTryClob 或类似的东西......
    猜你喜欢
    • 2011-06-25
    • 2011-10-14
    • 1970-01-01
    • 2016-05-20
    • 2018-02-25
    • 1970-01-01
    • 2013-12-29
    • 2015-07-02
    • 1970-01-01
    相关资源
    最近更新 更多