Разница между выпиской и подготовленным состоянием

222

Подготовленный оператор является несколько более мощной версией оператора и всегда должен быть по меньшей мере таким же быстрым и простым в обращении, как и оператор.
Подготовленное заявление может быть параметризовано

Большинство реляционных баз данных обрабатывают запрос JDBC / SQL в четыре этапа:

  1. Разбор входящего SQL-запроса
  2. Скомпилируйте запрос SQL
  3. Планирование / оптимизация пути сбора данных
  4. Выполнить оптимизированный запрос / получить и вернуть данные

Оператор всегда будет проходить через четыре шага выше для каждого запроса SQL, отправляемого в базу данных. Подготовленный оператор предварительно выполняет шаги (1) - (3) в вышеописанном процессе выполнения. Таким образом, при создании подготовленного оператора некоторая предварительная оптимизация выполняется немедленно. В результате уменьшается нагрузка на ядро ​​базы данных во время выполнения.

Теперь мой вопрос таков: «Есть ли какое-либо другое преимущество использования подготовленного заявления?»

CodeBee ..
источник
12
по моему мнению, наиболее эффективным является то, что ваш запрос может быть параметризован динамически
Хуссейн Ахтар Вахид 'Ghouri'

Ответы:

198

Преимущества PreparedStatement:

  • Предварительная компиляция и кэширование на стороне БД оператора SQL приводит к общему ускорению выполнения и возможности повторного использования одного и того же оператора SQL в пакетах .

  • Автоматическое предотвращение инъекций SQL атак на встроенном экранирование кавычек и других специальных символов. Обратите внимание, что для этого необходимо использовать любой из PreparedStatement setXxx()методов для установки значений.

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
    preparedStatement.setString(1, person.getName());
    preparedStatement.setString(2, person.getEmail());
    preparedStatement.setTimestamp(3, new Timestamp(person.getBirthdate().getTime()));
    preparedStatement.setBinaryStream(4, person.getPhoto());
    preparedStatement.executeUpdate();
    

    и, следовательно , не вставляйте значения в строку SQL путем конкатенации строк.

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email) VALUES ('" + person.getName() + "', '" + person.getEmail() + "'");
    preparedStatement.executeUpdate();
    
  • Облегчает настройка нестандартных объектов Java в строке SQL, например Date, Time, Timestamp, BigDecimal, InputStream( Blob) и Reader( Clob). На большинстве этих типов вы не можете «просто» делать то, toString()что вы делаете в простом Statement. Вы могли бы даже реорганизовать все это для использования PreparedStatement#setObject()внутри цикла, как показано в методе утилиты ниже:

    public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
        for (int i = 0; i < values.length; i++) {
            preparedStatement.setObject(i + 1, values[i]);
        }
    }
    

    Который может быть использован, как показано ниже:

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
    setValues(preparedStatement, person.getName(), person.getEmail(), new Timestamp(person.getBirthdate().getTime()), person.getPhoto());
    preparedStatement.executeUpdate();
    
BalusC
источник
4
Описательный и объясняющий текст в сочетании со ссылками и примером дает отличный ответ. +1
XenoRo
1
@RD Это может быть правдой, потому что подготовленный оператор требует 2 обращения к базе данных: первый для подготовки, второй для выполнения. Однако я бы это проверил. Я предполагаю, что план все еще будет кэшироваться на сервере базы данных для Statement, но это может стоить проверить.
Брэндон
2
Я не могу с уверенностью сказать, что с Java, но в целом подготовленный оператор не преформует "встроенное экранирование кавычек и других специальных символов"; вместо этого он выполняет разделение исполняемого SQL и данных , отправляя параметры в СУБД в виде отдельных пакетов информации после преобразования SQL в план запроса.
IMSoP
@BalusC - Спасибо за подробное объяснение.
CodeBee ..
49
  1. Они предварительно скомпилированы (один раз), поэтому быстрее для повторного выполнения динамического SQL (где параметры меняются)

  2. Кэширование операторов базы данных повышает производительность выполнения БД

    В базах данных хранятся кэши планов выполнения ранее выполненных операторов. Это позволяет ядру базы данных повторно использовать планы для операторов, которые были выполнены ранее. Поскольку PreparedStatement использует параметры, каждый раз, когда он выполняется, он выглядит как один и тот же SQL, база данных может повторно использовать предыдущий план доступа, уменьшая обработку. Операторы «встраивают» параметры в строку SQL и поэтому не отображаются в БД в качестве одного и того же SQL, предотвращая использование кэша.

  3. Протокол двоичной связи означает меньшую пропускную способность и более быстрые вызовы на сервер БД

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

  4. Они защищают от внедрения SQL путем экранирования текста для всех предоставленных значений параметров.

  5. Они обеспечивают более четкое разделение между кодом запроса и значениями параметров (по сравнению со связанными строками SQL), повышая удобочитаемость и помогая сопровождающим кода быстро понимать входные и выходные данные запроса.

  6. В Java можно вызвать getMetadata () и getParameterMetadata (), чтобы отразить поля набора результатов и поля параметров, соответственно

  7. В Java интеллектуально принимает объекты Java в качестве типов параметров через setObject, setBoolean, setByte, setDate, setDouble, setDouble, setFloat, setInt, setLong, setShort, setTime, setTimestamp - он преобразуется в формат типа JDBC, который можно понять с помощью DB (не только для toString) () формат).

  8. В Java принимает SQL ARRAYs, в качестве типа параметра через метод setArray

  9. В Java принимает CLOB, BLOB, OutputStreams и Readers в качестве параметров «подачи» через методы setClob / setNClob, setBlob, setBinaryStream, setCharacterStream / setAsciiStream / setNCharacterStream соответственно

  10. В java позволяет устанавливать специфичные для БД значения для SQL DATALINK, SQL ROWID, SQL XML и NULL с помощью методов setURL, setRowId, setSQLXML и setNull

  11. В Java наследует все методы из Statement. Он наследует метод addBatch и дополнительно позволяет добавлять набор значений параметров, чтобы соответствовать набору пакетных команд SQL через метод addBatch.

  12. В Java специальный тип PreparedStatement (подкласс CallableStatement) позволяет выполнять хранимые процедуры - поддерживая высокую производительность, инкапсуляцию, процедурное программирование и SQL, администрирование / обслуживание / настройку БД логики, а также использование собственной логики и функций БД.

Глен Бест
источник
Как все эти чудеса возможны, когда оба они являются только интерфейсами?!?!
Рафаэль
1
«Чудеса» становятся возможными благодаря стандартным фабричным методам, которые возвращают (зависящие от производителя) реализации интерфейсов: Connection.createStatement и Connection.prepareStatement. Этот дизайн заставляет вас работать с интерфейсами, поэтому вам не нужно знать конкретные классы реализации и избегать ненужной тесной связи с такими классами реализации. Все объяснено с примерами в документации Java jdbc и документации Java. :)
Глен Бест
Ваша часть «как правило» не имеет никакого смысла, не так ли 🤔
бхатия-перера
38

PreparedStatementэто очень хорошая защита (но не надежная защита) для предотвращения атак SQL-инъекций . Связывание значений параметров является хорошим способом защиты от «маленьких таблиц Бобби», которые могут сделать нежелательный визит.

duffymo
источник
6
Как можно выполнить SQL-инъекцию через подготовленный оператор?
Майкл Боргвардт
2
Майкл, Переменные, передаваемые в качестве аргументов подготовленным операторам, будут автоматически экранироваться драйвером JDBC.
CodeBee ..
3
Можете ли вы привести пример того, как атака с использованием SQL-инъекций будет работать против подготовленного оператора? Вы предполагаете ошибку в коде базы данных?
Питер Рекор
2
Да, но это далеко за пределами "довольно тупой". Это умопомрачительная глупость. Никто с унцией знаний не сделает этого.
Даффимо
2
Кроме того , многие производители баз данных не поддерживают параметризацию имен столбцов (думает ORDER BY) и / или числовые константы , в некоторых местах (думает LIMIT, OFFSETи другие постраничных решений), так что они могут быть атакованы SQL инъекцией, даже если Подготовленные заявления и параметризация используются везде , где возможно.
2012 г.
31

Некоторые из преимуществ PreparedStatement по сравнению с Statement:

  1. PreparedStatement помогает нам предотвращать атаки с использованием SQL-инъекций, поскольку он автоматически экранирует специальные символы.
  2. PreparedStatement позволяет нам выполнять динамические запросы с параметрами ввода.
  3. PreparedStatement предоставляет различные типы методов установки для установки входных параметров для запроса.
  4. PreparedStatement работает быстрее, чем Statement. Он становится более заметным, когда мы повторно используем PreparedStatement или используем его методы пакетной обработки для выполнения нескольких запросов.
  5. PreparedStatement помогает нам в написании объектно-ориентированного кода с помощью методов setter, тогда как в Statement мы должны использовать конкатенацию строк для создания запроса. Если нужно задать несколько параметров, написание Query с использованием конкатенации строк выглядит очень некрасиво и подвержено ошибкам.

Узнайте больше о проблеме внедрения SQL на http://www.journaldev.com/2489/jdbc-statement-vs-preparedstatement-sql-injection-example

Панкай
источник
Я прочитал вашу статью, действительно хорошая. Мой вопрос сейчас заключается в том, почему кто-то будет использовать заявление ?! даже для статического запроса ?!
Педрам Башири
Я всегда использую PreparedStatement, я не знаю какого-либо конкретного сценария, где Statement может иметь больше преимуществ.
Панкадж
13

нечего добавить,

1 - если вы хотите выполнить запрос в цикле (более 1 раза), подготовленный оператор может быть быстрее из-за упомянутой вами оптимизации.

2 - параметризованный запрос - хороший способ избежать SQL-инъекций. Параметризованные запросы доступны только в PreparedStatement.

mhshams
источник
10

Постановка статична, а подготовленный отчет динамичен.

Постановка подходит для DDL и подготовлена ​​отчетность для DML.

Оператор медленнее, а подготовленный оператор - быстрее.

больше различий (в архиве)

Сандип Ванама
источник
7

Не могу сделать CLOB в заявлении.

И: (OraclePreparedStatement) ps

orbfish
источник
7

По словам Mattjames

Использование Statement в JDBC должно быть на 100% локализовано для использования для DDL (ALTER, CREATE, GRANT и т. Д.), Так как это единственные типы операторов, которые не могут принимать BIND VARIABLES. PreparedStatements или CallableStatements следует использовать для КАЖДОГО ДРУГОГО типа операторов (DML, Queries). Так как это типы операторов, которые принимают переменные связывания.

Это факт, правило, закон - используйте готовые высказывания ВЕЗДЕ. Используйте ЗАЯВЛЕНИЯ почти не где.

корень
источник
5

подготовленный оператор игнорирует SQL-инъекцию

ашиш геол
источник
4
  • Легче читать
  • Вы можете легко сделать строку запроса постоянной
нанда
источник
4

Оператор будет использоваться для выполнения статических операторов SQL, и он не может принимать входные параметры.

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

МАРА МП
источник
4

Еще одна характеристика подготовленного или параметризованного запроса: ссылка взята из этой статьи.

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

Шаблон выписки подготавливается и отправляется в систему базы данных, а система баз данных выполняет синтаксический анализ, компиляцию и оптимизацию этого шаблона и сохраняет его без выполнения.

Некоторые из параметров, например, где предложение не передается во время создания шаблона в более позднем приложении, отправляют эти параметры в систему базы данных, а система базы данных использует шаблон оператора SQL и выполняется в соответствии с запросом.

Подготовленные операторы очень полезны для SQL-инъекций, потому что приложение может подготовить параметр, используя различные методы и протоколы.

Когда количество данных увеличивается, а индексы часто меняются в это время, подготовленные операторы могут потерпеть неудачу, поскольку в этой ситуации требуется новый план запросов.

Anvesh
источник
3

Statement интерфейс выполняет статические операторы SQL без параметров

PreparedStatement Интерфейс (расширяющий оператор) выполняет предварительно скомпилированный оператор SQL с / без параметров

  1. Эффективен для повторных казней

  2. Это предварительно скомпилировано, так что это быстрее

Бернард
источник
2

Не путайся: просто помни

  1. Оператор используется для статических запросов, таких как DDL, т. Е. Create, drop, alter и prepareStatement используется для динамических запросов, т. Е. DML-запросов.
  2. В операторе запрос предварительно не скомпилирован, а в режиме prepareStatement запрос предварительно скомпилирован, поскольку этот метод prepareStatement является эффективным по времени.
  3. prepareStatement принимает аргумент во время создания, в то время как Statement не принимает аргументы. Например, если вы хотите создать таблицу и вставить элемент, то :: Создать таблицу (статическую) с помощью Statement и Вставить элемент (динамический) с использованием prepareStatement.
Рупам
источник
1
prepareStatement принимает аргумент во время создания, в то время как Statement не принимает аргументы.
1

Я следовал всем ответам на этот вопрос, чтобы изменить работающий унаследованный код, используя - Statement(но с SQL-инъекциями) решение, использующее PreparedStatementгораздо более медленный код из-за плохого понимания семантики вокруг Statement.addBatch(String sql)& PreparedStatement.addBatch().

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

Мой сценарий был

Statement statement = connection.createStatement();

for (Object object : objectList) {
    //Create a query which would be different for each object 
    // Add this query to statement for batch using - statement.addBatch(query);
}
statement.executeBatch();

Итак, в приведенном выше коде у меня были тысячи различных запросов, все они были добавлены к одному и тому же оператору, и этот код работал быстрее, потому что операторы, которые не были кэшированы, были хорошими, и этот код редко выполнялся в приложении.

Теперь, чтобы исправить SQL-инъекции, я изменил этот код на

List<PreparedStatement> pStatements = new ArrayList<>();    
for (Object object : objectList) {
    //Create a query which would be different for each object 
    PreparedStatement pStatement =connection.prepareStatement(query);
    // This query can't be added to batch because its a different query so I used list. 
    //Set parameter to pStatement using object 
    pStatements.add(pStatement);
}// Object loop
// In place of statement.executeBatch(); , I had to loop around the list & execute each update separately          
for (PreparedStatement ps : pStatements) {
    ps.executeUpdate();
}

Итак, вы видите, я начал создавать тысячи PreparedStatementобъектов и затем в конечном итоге не смог использовать пакетирование, потому что мой сценарий требовал, чтобы - были тысячи запросов UPDATE или INSERT, и все эти запросы оказались разными.

Исправление внедрения SQL было обязательным без каких-либо затрат на снижение производительности, и я не думаю, что это возможно PreparedStatementв этом сценарии.

Кроме того, когда вы используете встроенную функцию пакетирования, вам нужно беспокоиться о закрытии только одного оператора, но при этом подходе List необходимо закрыть оператор перед повторным использованием, повторное использование PreparedStatement

Сабир Хан
источник