Рубрики
Без рубрики

Разница между Statement и PreparedStatement

Изучите различия между интерфейсами JDBC Statement и PreparedStatement.

Автор оригинала: baeldung.

1. Обзор

В этом уроке мы рассмотрим различия между интерфейсами JDBC ‘s Statement и PreparedStatement . Мы не будем рассматривать CallableStatement , интерфейс API JDBC , который используется для выполнения хранимых процедур.

2. Интерфейс API JDBC

Для выполнения SQL-запросов можно использовать как Statement , так и PreparedStatement . Эти интерфейсы выглядят очень похожими. Однако они существенно отличаются друг от друга по своим характеристикам и производительности:

  • Оператор Используется для выполнения строковых SQL запросов
  • PreparedStatement Используется для выполнения параметризованных SQL-запросов

Чтобы иметь возможность использовать Statement и PreparedStatement в наших примерах, мы объявим соединитель h2 JDBC как зависимость в нашем pom.xml файл:


  com.h2database
  h2
  1.4.200

Давайте определим сущность, которую мы будем использовать на протяжении всей этой статьи:

public class PersonEntity {
    private int id;
    private String name;

    // standard setters and getters
}

3. Заявление

Во-первых, оператор | interface принимает строки в качестве SQL-запросов. Таким образом, код становится менее читаемым когда мы объединяем SQL строки:

public void insert(PersonEntity personEntity) {
    String query = "INSERT INTO persons(id, name) VALUES(" + personEntity.getId() + ", '"
      + personEntity.getName() + "')";

    Statement statement = connection.createStatement();
    statement.executeUpdate(query);
}

Во-вторых, он уязвим для SQL-инъекции . Следующие примеры иллюстрируют эту слабость.

В первой строке обновления будет установлен столбец ” имя “на всех рядах до ” хакер “, как и все, что после “—” интерпретируется как комментарий в SQL, и условия оператора update будут проигнорированы. Во второй строке вставка завершится неудачей, так как цитата на ” имя “колонна не сбежала:

dao.update(new PersonEntity(1, "hacker' --"));
dao.insert(new PersonEntity(1, "O'Brien"))

В-третьих, JDBC передает запрос со встроенными значениями в базу данных Поэтому никакой оптимизации запросов нет, а главное, компонент database engine должен обеспечить все проверки Кроме того, запрос не будет отображаться как один и тот же для базы данных и это предотвратит использование кэша Аналогично, пакетные обновления должны выполняться отдельно:

public void insert(List personEntities) {
    for (PersonEntity personEntity: personEntities) {
        insert(personEntity);
    }
}

В-четвертых, оператор интерфейс подходит для DDL запросов типа CREATE , ALTER и DROP :

public void createTables() {
    String query = "create table if not exists PERSONS (ID INT, NAME VARCHAR(45))";
    connection.createStatement().executeUpdate(query);
}

Наконец, интерфейс оператора не может использоваться для хранения и извлечения файлов и массивов .

4. Подготовленное Заявление

Во-первых, PreparedStatement расширяет интерфейс Statement . Он имеет методы для привязки различных типов объектов , включая файлы и массивы. Следовательно, код становится | легким для понимания :

public void insert(PersonEntity personEntity) {
    String query = "INSERT INTO persons(id, name) VALUES( ?, ?)";

    PreparedStatement preparedStatement = connection.prepareStatement(query);
    preparedStatement.setInt(1, personEntity.getId());
    preparedStatement.setString(2, personEntity.getName());
    preparedStatement.executeUpdate();
}

Во-вторых, он защищает от SQL-инъекций , экранируя текст для всех предоставленных значений параметров:

@Test 
void whenInsertAPersonWithQuoteInText_thenItNeverThrowsAnException() {
    assertDoesNotThrow(() -> dao.insert(new PersonEntity(1, "O'Brien")));
}

@Test 
void whenAHackerUpdateAPerson_thenItUpdatesTheTargetedPerson() throws SQLException {

    dao.insert(Arrays.asList(new PersonEntity(1, "john"), new PersonEntity(2, "skeet")));
    dao.update(new PersonEntity(1, "hacker' --"));

    List result = dao.getAll();
    assertEquals(Arrays.asList(
      new PersonEntity(1, "hacker' --"), 
      new PersonEntity(2, "skeet")), result);
}

В-третьих, Подготовленное заявление использует предварительную компиляцию . Как только база данных получит запрос, она проверит кэш перед предварительной компиляцией запроса. Следовательно, если он не кэшируется, компонент database engine сохранит его для следующего использования.

Более того, эта функция ускоряет связь между базой данных и JVM через двоичный протокол, отличный от SQL. То есть в пакетах меньше данных, поэтому связь между серверами идет быстрее.

В-четвертых, Подготовленный оператор обеспечивает пакетное выполнение во время одного подключения к базе данных . Давайте посмотрим на это в действии:

public void insert(List personEntities) throws SQLException {
    String query = "INSERT INTO persons(id, name) VALUES( ?, ?)";
    PreparedStatement preparedStatement = connection.prepareStatement(query);
    for (PersonEntity personEntity: personEntities) {
        preparedStatement.setInt(1, personEntity.getId());
        preparedStatement.setString(2, personEntity.getName());
        preparedStatement.addBatch();
    }
    preparedStatement.executeBatch();
}

Затем оператор Prepared предоставляет простой способ хранения и извлечения файлов с помощью типов данных BLOB и CLOB . Точно так же он помогает хранить списки, преобразуя java.sql.Array в массив SQL.

Наконец, PreparedStatement реализует такие методы, как getMetadata () , которые содержат информацию о возвращаемом результате.

5. Заключение

В этом уроке мы представили основные различия между PreparedStatement и Statement . Оба интерфейса предлагают методы для выполнения SQL-запросов, но более подходящим является использование Statement для DDL-запросов и PreparedStatement для DML-запросов.

Как обычно, все примеры кода доступны на GitHub .