% войдите в PreparedStatement Java

PreparedStatement ps = con.createStatement("select * from table1 where last_name like ?");
ps.setString(1, "'%"+lastName+"'");

Будет ли это работать так же, как...

Statement s = con.createStatement("select * from table1 where last_name like %"+ lastName);

Или PreparedStatement удаляет знак %?


person Zombies    schedule 14.05.2009    source источник
comment
Подготовленный оператор не удаляет знак %. вызов setString может быть простым с помощью ps.setString(1, % + lastName);   -  person Brian    schedule 18.05.2009


Ответы (6)


% является подстановочным знаком (по крайней мере, в Oracle), поэтому теоретически оба должны работать одинаково (при условии, что вы добавите отсутствующие одинарные кавычки)

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

person cagcowboy    schedule 14.05.2009

Второй не сработает, потому что вы забыли поставить строку в кавычки! рядом с этим вам нужно сбежать и быть осторожным с sql-инъекциями.

Предположим, SQL

lastName = "and a quote' or a bracket()";
Statement s = con.createStatement("select * from table1 where last_name like '%"+ lastName + "'");

результирующий SQL:

select * from table1 where last_name like '%and a quote' or a bracket()'

который потерпит неудачу

Переменные привязки всегда делают работу с ними более безопасной.

person Janco    schedule 14.05.2009

Краткий ответ: да, если вы исправите цитирование, они должны дать одинаковые результаты. Знак процента не будет "удален" из подготовленного оператора, как и любой другой символ.

Более длинный ответ: вопрос о подготовленном операторе и одноразовом операторе может быть сложным. Если вы собираетесь выполнить его только один раз, подготовленный оператор займет больше времени, потому что механизм базы данных должен выполнить всю настройку для подготовленного оператора, затем вставить значения, а затем оставить его в кэше, пока механизм не примет решение. чтобы смыть его. Кроме того, оптимизатор часто не может эффективно обрабатывать подготовленный оператор. Весь смысл подготовленного оператора заключается в том, что оптимизатор один раз анализирует запрос и разрабатывает план запроса. Предположим, вы говорите что-то вроде «выберите имя_клиента из списка клиентов, где тип_покупателя=? и имя_клиента=?». У вас есть индексы как по типу, так и по почтовому индексу. С одноразовым оператором (конечно, с заполненными реальными значениями, а не вопросительными знаками) оптимизатор запросов во многих механизмах баз данных может просмотреть статистику распределения значений для двух полей и выбрать индекс, который даст меньшее значение. набор записей, затем прочитайте их все последовательно и исключите записи, не прошедшие второй тест. С подготовленным оператором он должен выбрать индекс, прежде чем узнает, какие значения будут предоставлены, поэтому он может выбрать менее эффективный индекс.

Вы никогда не должны под страхом смерти когда-либо писать код, который просто заключает в кавычки неизвестное значение и вставляет его в оператор SQL. Либо используйте подготовленные операторы, либо напишите функцию, которая правильно экранирует любые встроенные кавычки. Такую функцию легко написать. Я не понимаю, почему JDBC не включает его, поэтому вы должны написать его самостоятельно и включать в каждое приложение. (Это особенно верно, учитывая, что в некоторых диалектах SQL есть символы, отличные от одинарных кавычек, которые следует экранировать.)

Вот пример такой функции в Java:

public static String q(String s)
{
  if (s==null)
    return "null";
  if (s.indexOf('\'')<0)
    return "'"+s+"'";
  int sl=s.length();
  char[] c2=new char[sl*2+2];
  c2[0]='\''; 
  int p2=1;
  for (int p=0;p<sl;++p)
  {
    char c=s.charAt(p);
    if (c=='\'')
      c2[p2++]=c;
    c2[p2++]=c;
  }
  c2[p2++]='\'';
  return new String(c2,0,p2);
}

(Примечание: я только что отредактировал эту функцию из версии, которую я вытащил из своего кода, чтобы исключить некоторые особые случаи, не относящиеся к делу — извините, если я допустил некоторые незначительные ошибки при этом.)

Обычно я даю ему очень короткое имя, например «q», поэтому я могу просто написать:

String sql="select customer_name from customer where customer_type="+q(custType)
  +" and customer_zip="+q(custZip);

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

Затем я перегружаю его, чтобы он принимал даты, числа и другие специальные типы и обрабатывал их соответствующим образом.

person Community    schedule 14.05.2009
comment
@Jay: отличные очки. Чтобы уточнить, что касается вашего мнения о том, что оптимизатор не обрабатывает подготовленный оператор так эффективно, это не совсем правильно. Каждый оператор должен быть подготовлен, переменные связывания не создают дополнительной работы для оптимизатора и не приводят к тому, что подготовленный оператор зависает в общем пуле. Вы поднимаете правильное замечание об искаженных данных, когда план доступа к индексу более эффективен для одних значений, чем для других, но на самом деле это скорее исключение, чем правило. Начиная с версии 9i, оптимизатор фактически преобразует литералы в переменные связывания и просматривает значения... - person spencer7593; 16.05.2009

Использование подготовленных операторов с переменными связывания намного быстрее, поскольку это означает, что Oracle не нужно снова и снова анализировать (компилировать) операторы sql. Oracle хранит все выполненные операторы вместе с планами выполнения в общей хеш-таблице для повторного использования. Однако Oracle будет повторно использовать только план выполнения подготовленных операторов с переменными связывания. Когда вы делаете:

"выберите * из таблицы 1, где фамилия_имя, например %"+ фамилия

Oracle не повторно использует план выполнения.

(Oracle хэширует каждый оператор sql, и когда вы используете select ... где last_name, например %"+ lastName, каждый оператор sql имеет другое хэш-значение, потому что переменная lastname почти всегда имеет другое значение, поэтому Oracle не может найти оператор sql в хэш-таблица, и Oracle не может повторно использовать план выполнения.)

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

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

person tuinstoel    schedule 14.05.2009

Мы часто используем первый подход без проблем. Например:

String sql = "SELECT * FROM LETTER_BIN WHERE LTR_XML Like ' (?) ' AND LTR_BIN_BARCODE_ID = (?)";
try
{
    // Cast a prepared statement into an OralcePreparedStatement
    opstmt = (OraclePreparedStatement) conn.prepareStatement(sql);
    // Set the clob using a string
    opstmt.setString(1,fX.toString());
    // for this barcode
    opstmt.setLong(2,lbbi);
    // Execute the OraclePreparedStatement
    opstmt.execute();
} catch(java.sql.SQLException e)
{
    System.err.println(e.toString());
} finally
{
    if(opstmt != null)
    {
        try
        {
            opstmt.close();
        } catch(java.sql.SQLException ignore)
        {
            System.err.println("PREPARED STMT ERROR: "+ignore.toString());
        }
    }

}
person northpole    schedule 14.05.2009

Хорошо, я поверю вам на слово относительно Oracle. Неудивительно, что это зависит от механизма базы данных. Postgres ведет себя так, как я описал. При использовании MySQL из JDBC — по крайней мере, пару лет назад, когда я в последний раз изучал это — почти нет разницы между подготовленными операторами и одноразовыми операторами, потому что драйвер MySQL JDBC сохраняет подготовленные операторы на CLIENT. стороны, когда вы выполняете подготовленный оператор, он заполняет значения в виде текста и отправляет его в механизм базы данных. Итак, что касается движка, на самом деле не существует такой вещи, как подготовленный оператор. Я бы совсем не удивился, если бы узнал, что у других движков совершенно другое поведение.

person Community    schedule 14.05.2009
comment
sqlite ведет себя так же, как Oracle, когда речь идет о подготовленных операторах. - person tuinstoel; 15.05.2009