Вставьте CLOB в базу данных Oracle

4

Мой вопрос: Как обойти ошибку 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;
}
  • 1
    Вместо динамического SQL и построения оператора вставки со строковыми литералами, вы пытались использовать PreparedStatement и его setClob() ?
Теги:
jdbc
insert
clob

4 ответа

7
Лучший ответ

Вы делаете это сложно.

Используйте 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), скорее всего, тоже будет работать.

2

От документ Oracle

Вы должны помнить следующее автоматическое переключение режима ввода для больших данных. Существует три режима ввода: Прямое связывание, привязка потока и привязка 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();
    }
  }
2

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(); 
        } 
    } 
} 
  • 1
    Спасибо за предоставленный ответ. В следующий раз, пожалуйста, отформатируйте ваш код с логической схемой отступа. На этот раз я отредактирую ваш пост для вас. (Подсказка: вы получите больше голосов, если ваш код в дополнение к функционалу.)
2

Вам нужно будет использовать переменные связывания, а не строить инструкцию 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();
}
  • 0
    Я думаю, что это должно работать, за исключением того, setClob метод setClob принимает clob, а не строку. В другом вопросе показано, что создание clob выглядит следующим образом: oracle.sql.CLOB.createTemporary(connection, false, oracle.sql.CLOB.DURATION_SESSION); это правильно?
  • 0
    @kentcdodds - я так считаю (в данный момент у меня нет среды разработки Java для тестирования)
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню