Мой вопрос: Как обойти ошибку ORA-01704: string literal too long
при вставке (или выполнении чего-либо в запросах) с помощью CLOB
s?
Я хочу иметь такой запрос:
INSERT ALL
INTO mytable VALUES ('clob1')
INTO mytable VALUES ('clob2') --some of these clobs are more than 4000 characters...
INTO mytable VALUES ('clob3')
SELECT * FROM dual;
Когда я пытаюсь использовать его с фактическими значениями, я получаю ORA-01704: string literal too long
назад. Это довольно очевидно, но как я могу вставлять clobs (или вообще выполнять какой-либо оператор с помощью clob)?
Я пробовал смотреть на question, но я не думаю, что у меня есть то, что я ищу. Клобы, которые у меня есть, находятся в List<String>
, и я повторяю их, чтобы сделать утверждение. Мой код выглядит следующим образом:
private void insertQueries(String tempTableName) throws FileNotFoundException, DataException, SQLException, IOException {
String preQuery = " into " + tempTableName + " values ('";
String postQuery = "')" + StringHelper.newline;
StringBuilder inserts = new StringBuilder("insert all" + StringHelper.newline);
List<String> readQueries = getDomoQueries();
for (String query : readQueries) {
inserts.append(preQuery).append(query).append(postQuery);
}
inserts.append("select * from dual;");
DatabaseController.getInstance().executeQuery(databaseConnectionURL, inserts.toString());
}
public ResultSet executeQuery(String connection, String query) throws DataException, SQLException {
Connection conn = ConnectionPool.getInstance().get(connection);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);
conn.commit();
ConnectionPool.getInstance().release(conn);
return rs;
}
Вы делаете это сложно.
Используйте PreparedStatement и addBatch() для каждого clob в вашем списке:
String sql = "insert into " + tempTableName + " values (?)";
PreparedStatement stmt = connection.prepareStatement(sql);
for (String query : readQueries) {
stmt.setCharacterStream(1, new StringReader(query), query.lenght());
stmt.addBatch();
}
stmt.exececuteBatch();
Не нужно возиться с экранирующими строками, без проблем с длиной литералов, нет необходимости создавать временные складки. И, скорее всего, так же быстро, как использование одного оператора INSERT ALL.
Если вы используете текущий драйвер ( > 10.2), то я думаю, что вызов setCharacterStream() и создание Reader тоже не нужны. Простой setString(1, query)
, скорее всего, тоже будет работать.
Вы должны помнить следующее автоматическое переключение режима ввода для больших данных. Существует три режима ввода: Прямое связывание, привязка потока и привязка LOB.
Для операторов PL/SQL
В методах setBytes и setBinary stream используется прямая привязка для данных размером менее 32767 байт.
В методах setBytes и setBinaryStream используется привязка LOB для данных размером более 32766 байт.
В методах setString, setCharacterStream и setAsciiStream используется прямая привязка для данных размером менее 32767 байт в наборе символов базы данных.
В методах setString, setCharacterStream и setAsciiStream используется привязка LOB для данных размером более 32766 байт в наборе символов базы данных.
Методы setBytesForBlob и setStringForClob, присутствующие в интерфейсе oracle.jdbc.OraclePreparedStatement, используют привязку LOB для любого размера данных.
Ниже приведен пример размещения содержимого файла во входном параметре CLOB процедуры PLSQL:
public int fileToClob( FileItem uploadFileItem ) throws SQLException, IOException
{
//for using stmt.setStringForClob method, turn the file to a big String
FileItem item = uploadFileItem;
InputStream inputStream = item.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader( inputStream );
BufferedReader bufferedReader = new BufferedReader( inputStreamReader );
StringBuffer stringBuffer = new StringBuffer();
String line = null;
while((line = bufferedReader.readLine()) != null) { //Read till end
stringBuffer.append(line);
stringBuffer.append("\n");
}
String fileString = stringBuffer.toString();
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
item.delete();
OracleCallableStatement stmt;
String strFunction = "{ call p_file_to_clob( p_in_clob => ? )}";
stmt= (OracleCallableStatement)conn.prepareCall(strFunction);
try{
SasUtility servletUtility = sas.SasUtility.getInstance();
stmt.setStringForClob(1, fileString );
stmt.execute();
} finally {
stmt.close();
}
}
BLOB (двоичные большие объекты) и CLOB (большие объекты символов) являются специальными типами данных и могут содержать большие куски данных в виде объектов или текста. Объекты Blob и Clob сохраняют данные объектов в базе данных как поток.
Пример фрагмента кода:
public class TestDB {
public static void main(String[] args) {
try {
/** Loading the driver */
Class.forName("com.oracle.jdbc.Driver");
/** Getting Connection */
Connection con = DriverManager.getConnection("Driver URL","test","test");
PreparedStatement pstmt = con.prepareStatement("insert into Emp(id,name,description)values(?,?,?)");
pstmt.setInt(1,5);
pstmt.setString(2,"Das");
// Create a big CLOB value...AND inserting as a CLOB
StringBuffer sb = new StringBuffer(400000);
sb.append("This is the Example of CLOB ..");
String clobValue = sb.toString();
pstmt.setString(3, clobValue);
int i = pstmt.executeUpdate();
System.out.println("Done Inserted");
pstmt.close();
con.close();
// Retrive CLOB values
Connection con = DriverManager.getConnection("Driver URL","test","test");
PreparedStatement pstmt = con.prepareStatement("select * from Emp where id=5");
ResultSet rs = pstmt.executeQuery();
Reader instream = null;
int chunkSize;
if (rs.next()) {
String name = rs.getString("name");
java.sql.Clob clob = result.getClob("description")
StringBuffer sb1 = new StringBuffer();
chunkSize = ((oracle.sql.CLOB)clob).getChunkSize();
instream = clob.getCharacterStream();
BufferedReader in = new BufferedReader(instream);
String line = null;
while ((line = in.readLine()) != null) {
sb1.append(line);
}
if (in != null) {
in.close();
}
// this is the clob data converted into string
String clobdata = sb1.toString();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Вам нужно будет использовать переменные связывания, а не строить инструкцию SQL, используя конкатенацию строк. Это будет полезно с точки зрения безопасности, производительности и надежности, так как это уменьшит риск атаки SQL-инъекций, уменьшит время, затрачиваемое Oracle на проведение сложных анализов SQL-запроса, и устранит потенциал, который там является специальным символом в строке, который приводит к тому, что недопустимый оператор SQL генерируется (т.е. одинарная кавычка).
Я бы ожидал, что вы хотите что-то вроде
private void insertQueries(String tempTableName) throws FileNotFoundException, DataException, SQLException, IOException {
String preQuery = " into " + tempTableName + " values (?)" + StringHelper.newline;
StringBuilder inserts = new StringBuilder("insert all" + StringHelper.newline);
List<String> readQueries = getDomoQueries();
for (String query : readQueries) {
inserts.append(preQuery);
}
inserts.append("select * from dual");
Connection conn = ConnectionPool.getInstance().get(connection);
PreparedStatement pstmt = conn.prepareStatement(
inserts);
int i = 1;
for (String query : readQueries) {
Clob clob = CLOB.createTemporary(conn, false, oracle.sql.CLOB.DURATION_SESSION);
clob.setString(i, query);
pstmt.setClob(i, clob);
i = i + 1;
}
pstmt.executeUpdate();
}
setClob
метод setClob
принимает clob, а не строку. В другом вопросе показано, что создание clob выглядит следующим образом: oracle.sql.CLOB.createTemporary(connection, false, oracle.sql.CLOB.DURATION_SESSION);
это правильно?
PreparedStatement
и егоsetClob()
?