Я унаследовал проект, который имеет следующую настройку:
Объявленный класс и один из методов, используемых для выполнения запросов, выглядят следующим образом:
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 И ФОРМА НЕ НЕТ
Проблема в том, что если входная строка (представляющая геометрию) превышает предел символов Oracle 4000, мы получаем
ORA-01460: запрошенная неоплаченная или необоснованная конверсия
Другими словами, это означает, что queryForList (и все, что стоит за ним) не обрабатывает строки, превышающие лимит.
После некоторых исследований я понял, что для создания временного Clob я должен использовать Oracle3t0 cdp0. Поэтому я изменил код, чтобы проверить параметры и соответственно изменить их:
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
После другого исследования я понял, что указатель на временный Clob, который я присвоил параметру "params", пуст и, скорее всего, из-за того, что "queryForList" выполняется в другом соединении (!), Чем тот, который используется для генерируя временный список. Поэтому я получил следующее:
JdbcTemplate t = new JdbcTemplate(new SingleConnectionDataSource(conn, false));
result = t.queryForList(sql, params);
который работал, но я боюсь, что это не оптимально и будет только вызывать проблемы в какой-то момент в будущем.
Мой вопрос в том, есть ли способ повторно использовать соединение, используемое для генерации CLOB для фактического запроса?
Прямым делом было бы уклониться от сложного промежуточного ПО.
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 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. Это просто кажется мне непрозрачным. Мое решение гораздо более подробное, но я считаю логически очень простым и доступным для обслуживания всем, кто понимает JDBC.
Удачи!