Вопрос по java – Вставьте CLOB в базу данных Oracle

4

My question is: Как вы обходитеORA-01704: string literal too long ошибка при вставке (или выполнении чего-либо в запросах) сCLOBs?

Я хочу иметь такой запрос:

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 назад. Это довольно очевидно, но как мне вставить clob (или выполнить какой-либо оператор вообще с clob)?

Я пытался посмотреть на этовопрос, но я не думаю, что он имеет то, что я ищу. Сгустки у меня в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;
}
Вместо динамического SQL и построения оператора вставки со строковыми литералами, вы пытались использоватьPreparedStatement и этоsetClob() метод? QuantumMechanic

Ваш Ответ

6   ответов
7

Используйте PreparedStatement и addBatch () для каждого сгустка в вашем списке:

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

а не создавать оператор 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етод @ принимает clob, а не строку. В другом вопросе показано, что создание clob выглядит следующим образом:oracle.sql.CLOB.createTemporary(connection, false, oracle.sql.CLOB.DURATION_SESSION); это верно kentcdodds
@ kentcdodds - я так считаю (в данный момент у меня нет среды разработки Java для тестирования) Justin Cave
Вы также можете сделать setString () для столбца Oracle CLOB. Драйвер Oracle JDBC достаточно умен, чтобы выполнить преобразование. GriffeyDog
Это прекрасно работает (я думаю). Я получаюORA-00911: invalid character что не совсем сюрприз. Я посмотрел вокруг, есть ли простой способ получить запрос изPreparedStatement чтобы я понял, что не так? kentcdodds
@ JustinCave, я только что отредактировал твой ответ для Clob, но я не был уверен, стоит ли делатьclob.setString(i, query); илиclob.setString(1, query); ... kentcdodds
2

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

Оракул документ

Вы должны иметь в виду следующее автоматическое переключение режима ввода для больших данных. Существует три режима ввода: прямая привязка, привязка к потоку и привязка больших объектов.

Для операторов PL / SQL

Потоковые методы setBytes и setBinary используют прямое связывание для данных размером менее 32767 байт.

Методы setBytes и setBinaryStream используют привязку больших объектов для данных, размер которых превышает 32766 байт.

Методы setString, setCharacterStream и setAsciiStream используют прямое связывание для данных размером менее 32767 байт в наборе символов базы данных.

Методы setString, setCharacterStream и setAsciiStream используют привязку больших объектов для данных, размер которых превышает 32766 байт в наборе символов базы данных.

Методы setBytesForBlob и setStringForClob, представленные в интерфейсе oracle.jdbc.OraclePreparedStatement, используют привязку больших объектов для любого размера данных.

Follow - это пример помещения содержимого файла во входной параметр 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();
    }
  }
0

А не oracle. * Вещи. Для меня простой подход

Connection con = ...;
try (PreparedStatement pst = con.prepareStatement(
     "insert into tbl (other_fld, clob_fld) values (?,?)", new String[]{"tbl_id"});
     ) {
        Clob clob = con.createClob();
        readIntoClob(clob, inputStream);
        pst.setString(1, "other");
        pst.setClob(2, clob);
        pst.executeUpdate();
        try (ResultSet rst = pst.getGeneratedKeys()) {
            if (rst == null || !rst.next()) {
                throw new Exception("error with getting auto-generated key");
            }
            id = rst.getBigDecimal(1);
        }  

перестала работать, когда тестирование (текущий tomcat, jdbc) перешло в производство (застряло в Tomcat6 по глупым причинам). con.createClob () возвращает значение null по неизвестным в этой версии причинам, поэтому мне пришлось сделать этот двойной дубль (мне понадобилось много времени, чтобы выяснить это, поэтому я поделился здесь ...)

try (PreparedStatement pst = con.prepareStatement(
         "insert into tbl (other_fld) values (?)", new String[]{"tbl_id"});
     PreparedStatement getClob= con.prepareStatement(
         "select clob_fld from tbl where tbl_id = ? for update");
     ) {
        Clob clob = con.createClob();
        readIntoClob(clob, inputStream);
        pst.setString(1, "other");
        pst.executeUpdate();
        try (ResultSet rst = pst.getGeneratedKeys()) {
            if (rst == null || !rst.next()) {
                throw new Exception("error with getting auto-generated key");
            }
            id = rst.getBigDecimal(1);
        }  

        //  fetch back fresh record, with the Clob
        getClob.setBigDecimal(1, id);
        getClob.execute();
        try (ResultSet rst = getClob.getResultSet()) {
            if (rst == null || !rst.next()) {
                throw new Exception("error with fetching back clob");
            }
            Clob c = rst.getClob(1);
            // Fill in data
            readIntoClob(c, stream);
            // that's all 
        }

    } catch (SQLException) {
       ...
    }

для полноты вот

// Read data from an input stream and insert it in to the clob column
private static void readIntoClob(Clob clob, InputStream stream) {
    try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream))) {
        char[] buffer = new char[CHUNK_BUFFER_SIZE];
        int charsRead;
        try (Writer wr = clob.setCharacterStream(1L)) {
            // Loop for reading of chunk of data and then write into the clob.
            while ((charsRead = bufferedReader.read(buffer)) != -1) {
                wr.write(buffer, 0, charsRead);
            }
        } catch (SQLException | IOException ex) {
            ...
        }

    }
}

который откуда-то на SO, спасибо.

0

Похожие вопросы