Statement.executeUpdate() нельзя использовать для вставки нескольких записей в Oracle

С помощью приведенного ниже кода я могу вставить несколько записей в базу данных H2. Но с Oracle это не работает. Есть ли другой способ использовать обычный SQL для INSERT нескольких подобных операторов.

    //INSERT RECORDS
String    sql       = "INSERT INTO PERSON (NAME, AGE) VALUES ('"+name+"',+"+age+");" +
                      "INSERT INTO PERSON (NAME, AGE) VALUES ('Susan'   ,20      )";
Statement statement = connection.createStatement();
int       success   = statement.executeUpdate(sql);

Вам определенно не следует передавать такие параметры. Используйте PreparedStatement с setX"INSERT INTO PERSON (NAME, AGE) VALUES (?, ?)"

g00se 25.06.2024 11:46

Почему нет. Если эти значения не получены из внешнего интерфейса, опасности от SQL-инъекции нет. И ReadedStatement должен делать то же самое => генерировать правильный INSERT. Также этот код предназначен для обучения. Я пытаюсь понять пакетную вставку, и у меня сложилось впечатление, что она создаст строку, подобную этой, с большим количеством INSERT. Но это не работает с Oracle.

ivoronline 25.06.2024 11:52

Это просто хорошая практика, и нет необходимости рисковать, что цитирование пойдет не так. Кроме того, в противном случае вы не сможете получить никакой потенциальной оптимизации от использования переменных связывания. Пакетная обработка имеет другую цель — она предназначена для оптимизации записи, а не для выполнения того, что по сути является запуском сценария SQL. Я бы посмотрел, «как запустить SQL-скрипт в JDBC?»

g00se 25.06.2024 11:56

"Почему нет." Некоторые драйверы+серверы могут использовать кэшированные проанализированные операторы/планы выполнения. При конкатенации каждый оператор уникален, поэтому повторное использование не требуется. Это не проблема для небольших партий, но может существенно изменить ситуацию в крупных корпоративных системах.

DuncG 25.06.2024 12:25

Подготовленное утверждение также ускользает: Если у вас имя с апострофом 'd'Isle'. Если вы попробуете два оператора одновременно, VALUES((...), (...)) поможет. Но ReadedStatements просто полезны. Неиспользование их помечает инструменты или читателей-людей.

Joop Eggen 25.06.2024 13:27

один Statement может содержать только один оператор - даже использование ; в конце поддерживается/не всеми драйверами

user85421 25.06.2024 13:39

@user85421 Хотя это верно для операторов SQL, в Oracle вы также можете использовать один оператор PL/SQL, и этот один оператор PL/SQL может содержать несколько операторов SQL (каждый из которых должен заканчиваться знаком ;, как и оператор PL/SQL; однако терминатор оператора / для блока PL/SQL запрещен). Таким образом, ОП мог бы использовать BEGIN INSERT INTO PERSON (NAME, AGE) VALUES ('Alice',20); INSERT INTO PERSON (NAME, AGE) VALUES ('Susan', 20); END; (но, вероятно, лучше использовать параметризованный запрос и пакетные вставки).

MT0 25.06.2024 14:22

@MT0 ну, PL/SQL — это не Java; и один оператор (PL/)SQL, даже если он содержит больше операторов, все равно остается одним оператором; и вопрос заключается не в использовании оператора PL/SQL, а в двух операторах - это SQL-клиент Oracle, который интерпретирует ; как разделитель операторов - и, кстати, это был один из старых JDBC (не PL/SQL). SQL) драйверы именно Oracle, которые не допускали точку с запятой в конце (одного) оператора.

user85421 25.06.2024 14:28
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
8
90
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Используйте ReadedStatement и не включайте терминатор ; в оператор SQL (поскольку он не является частью оператора SQL и не является допустимым синтаксисом):

connection con.setAutoCommit(false);   
PreparedStatement ps=connection.prepareStatement(
  "INSERT INTO PERSON (NAME, AGE) VALUES (?,?)"
);
ps.setString(1, "Susan");
ps.setInt(2, 20);
ps.addBatch();
ps.setString(1, "Alice");
ps.setInt(2, 21);
ps.addBatch();
int [] numInserts = ps.executeBatch();
connection.commit();

Примечание. В Oracle в команде не может быть более одного оператора, поэтому попытка передать INSERT INTO t VALUES('X'); INSERT INTO t VALUES('Y') завершится синтаксической ошибкой, даже если операторы синтаксически действительны по отдельности (это помогает предотвратить атаки SQL-инъекцией). Вам необходимо либо (1) отправить их по отдельности как две команды, либо (2) обернуть их в блок PL/SQL, чтобы они стали одной командой. В вашем случае просто используйте пакет операторов.


Есть ли другой способ использовать обычный SQL для нескольких подобных операторов?

Существует несколько способов вставить несколько строк в один оператор:

  • Используя оператор INSERT:

    INSERT ALL
      INTO person (name, age) VALUES ('Susan', 20)
      INTO person (name, age) VALUES ('Alice', 21)
    SELECT 1 FROM DUAL
    
  • Используя INSERT ALL:

    INSERT INTO person (name, age) 
      SELECT 'Susan', 20 FROM DUAL UNION ALL
      SELECT 'Alice', 21 FROM DUAL
    
  • Ответ Мурейника показывает, как использовать INSERT INTO ... SELECT ... с несколькими предложениями INSERT INTO, которое доступно в Oracle 23.

  • Использование PL/SQL для переноса нескольких отдельных VALUES:

    BEGIN
      INSERT INTO person (name, age) VALUES ('Susan', 20);
      INSERT INTO person (name, age) VALUES ('Alice', 21);
    END;
    

Вы можете параметризовать все эти запросы, заменив INSERT, 'Susan', 'Alice' и 20 на 21, а затем передать значения в качестве параметров привязки с помощью Java.

Примечание. Первые три не включают завершающий ?, но решение PL/SQL будет включать завершающий ; для завершения оператора ; (но не завершающий END в качестве терминатора для блока PL/SQL).

Ответ MTO об использовании пакетов, вероятно, лучший способ.

Сказав это, просто чтобы дать полную картину исходного вопроса:

Есть ли другой способ использовать обычный SQL для INSERT нескольких подобных операторов.

В Oracle (начиная с версии 23) вы можете иметь несколько наборов values с запятыми (,) между ними:

String SQL = "INSERT INTO PERSON (NAME, AGE) VALUES ('"+name+"',+"+age+"), ('Susan', 20)";

Это, конечно, можно сделать с помощью PreparedStatement и связать значения вместо их объединения:

String SQL = "INSERT INTO PERSON (NAME, AGE) VALUES (?, ?), ('Susan', 20)";
PreparedStatement ps = connection.prepareStatement(SQL);
ps.setString(1, name);
ps.setInt(2, age);
ps.executeUpdate();

или даже преобразовать все значения в заполнители:

String SQL = "INSERT INTO PERSON (NAME, AGE) VALUES (?, ?), (?, ?)";
PreparedStatement ps = connection.prepareStatement(SQL);
ps.setString(1, name);
ps.setInt(2, age);
ps.setString(3, "Susan");
ps.setInt(4, 20);
ps.executeUpdate();

«В Oracle вы можете иметь несколько наборов значений с запятыми (,) между ними». Только из Oracle 23, до этого вам нужно было использовать оператор INSERT ALL или INSERT ... SELECT ... FROM DUAL UNION ALL SELECT .... FROM DUAL. См. этот вопрос.

MT0 25.06.2024 13:08

@MT0 хорошее отличие, спасибо — отредактировал это.

Mureinik 25.06.2024 13:17

Другие вопросы по теме