Правильный способ использования StringBuilder в SQL


Я только что нашел такую сборку sql-запросов в своем проекте:

return (new StringBuilder("select id1, " + " id2 " + " from " + " table")).toString();

это StringBuilder достигните своей цели, т. е. уменьшите использование памяти?

Я сомневаюсь в этом, потому что в конструкторе используется '+' (String concat operator). Будет ли это занимать тот же объем памяти, что и при использовании строки, как показано ниже? я понял, он отличается при использовании StringBuilder.append().

return "select id1, " + " id2 " + " from " + " table";

оба оператора равны в использовании памяти или нет? Пожалуйста, уточните.

спасибо заранее!

Edit:

кстати, это не мой код. Нашел его в старом проекте. Кроме того, запрос не так мал, как в моем примере. :)

6 85

6 ответов:

цель использования StringBuilder, т. е. сокращение памяти. Достигается ли это?

нет, совсем нет. Этот код не использует StringBuilder правильно. (Я думаю, что вы неправильно процитировали его, Хотя; конечно, нет кавычек вокруг id2 и table?)

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

это займет память равна использованию строки, как показано ниже?

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

если автор этого кода хочет использовать StringBuilder (есть аргументы за, но и против; см. Примечание В конце этого ответа), лучше делать это правильно (здесь я предполагаю, что нет на самом деле кавычки id2 и table):

StringBuilder sb = new StringBuilder(some_appropriate_size);
sb.append("select id1, ");
sb.append(id2);
sb.append(" from ");
sb.append(table);
return sb.toString();

обратите внимание, что я перечислил some_appropriate_size на StringBuilder конструктор, так что он начинается с достаточной емкости для полного содержимого, которое мы собираемся добавить. Размер по умолчанию используется, если вы не укажете его 16 символов, который обычно слишком мал и приводит к StringBuilder необходимость делать перераспределения, чтобы сделать себя больше (IIRC, в Sun / Oracle JDK, он удваивает себя [или больше, если он знает, что ему нужно больше удовлетворить конкретный append] каждый раз, когда он выбегает из комнаты).

вы, возможно, слышали, что строка конкатенации будет использовать StringBuilder под обложками, если они скомпилированы с компилятором Sun/Oracle. Это правда, он будет использовать один StringBuilder для полного выражения. Но он будет использовать конструктор по умолчанию, что означает, что в большинстве случаев ему придется выполнить перераспределение. Хотя это легче читать. Обратите внимание, что это не правда в серия из конкатенации. Так, например, это использует один StringBuilder:

return "prefix " + variable1 + " middle " + variable2 + " end";

это примерно переводится как:

StringBuilder tmp = new StringBuilder(); // Using default 16 character size
tmp.append("prefix ");
tmp.append(variable1);
tmp.append(" middle ");
tmp.append(variable2);
tmp.append(" end");
return tmp.toString();

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

но это только для одного выражения. Несколько StringBuilders используются для этого:

String s;
s = "prefix ";
s += variable1;
s += " middle ";
s += variable2;
s += " end";
return s;

это заканчивается становится что-то вроде этого:

String s;
StringBuilder tmp;
s = "prefix ";
tmp = new StringBuilder();
tmp.append(s);
tmp.append(variable1);
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(" middle ");
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(variable2);
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(" end");
s = tmp.toString();
return s;

...что довольно уродливо.

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

когда у вас уже есть все "части", которые вы хотите добавить, нет смысла использовать StringBuilder на всех. Используя StringBuilderи конкатенация строк в том же вызове, что и в вашем примере кода, еще хуже.

это было бы лучше:

return "select id1, " + " id2 " + " from " + " table";

в этом случае конкатенация строк на самом деле происходит в времени компиляции в любом случае, это эквивалентно еще более простому:

return "select id1, id2 from table";

используя new StringBuilder().append("select id1, ").append(" id2 ")....toString() будет на самом деле мешают производительность в этом случае, потому что это заставляет конкатенацию выполняться в срок исполнения, вместо at compile времени. Ой.

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

I есть статью String/StringBuffer который я написал некоторое время назад - раньше StringBuilder пришли вместе. Принципы применимы к StringBuilder таким же образом, хотя.

[[ здесь есть несколько хороших ответов, но я считаю, что им все еще не хватает немного информации. ]]

return (new StringBuilder("select id1, " + " id2 " + " from " + " table"))
     .toString();

Итак, как вы указываете, пример, который вы даете, является упрощенным, но давайте проанализируем его в любом случае. Что происходит здесь-это компилятор на самом деле тут + здесь работать, потому что "select id1, " + " id2 " + " from " + " table" все константы. Так что это превращается в:

return new StringBuilder("select id1,  id2  from  table").toString();

в этом случае, очевидно, нет смысла использовать StringBuilder. Вы могли бы также делать:

// the compiler combines these constant strings
return "select id1, " + " id2 " + " from " + " table";

однако, даже если вы добавляете какие-либо поля или другие непостоянные значения, компилятор будет использовать внутренниеStringBuilder -- вам не нужно определять один:

// an internal StringBuilder is used here
return "select id1, " + fieldName + " from " + tableName;

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

StringBuilder sb = new StringBuilder("select id1, ");
sb.append(fieldName).append(" from ").append(tableName);
return sb.toString();

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

// 1 StringBuilder used in this line
String query = "select id1, " + fieldName + " from " + tableName;
if (where != null) {
   // another StringBuilder used here
   query += ' ' + where;
}

The + в первой строке используется одна StringBuilder экземпляра. Тогда += использует другой StringBuilder экземпляра. Это более эффективно сделать:

// choose a good starting size to lower chances of reallocation
StringBuilder sb = new StringBuilder(64);
sb.append("select id1, ").append(fieldName).append(" from ").append(tableName);
// conditional code
if (where != null) {
   sb.append(' ').append(where);
}
return sb.toString();

в другой раз, когда я использую StringBuilder это когда я строю строку из нескольких вызовов методов. Тогда я могу создать методы, которые принимают :

private void addWhere(StringBuilder sb) {
   if (where != null) {
      sb.append(' ').append(where);
   }
}

когда вы используете StringBuilder, вы должны смотреть на использование + в то же время:

sb.append("select " + fieldName);

это + вызовет еще один внутренний StringBuilder создать. Это, конечно, должно быть:

sb.append("select ").append(fieldName);

наконец, как указывает @T. J. rowder, вы всегда должны догадываться о размере StringBuilder. Это позволит сэкономить на количестве char[] объекты, созданные при увеличении размера внутреннего буфера.

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

однако, когда компилятор видит выражение "select id1, " + " id2 " + " from " + " table" Он выдает код, который на самом деле создает StringBuilder за кулисами и добавляет к нему, поэтому конечный результат не так уж плох в конце концов.

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

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

return new StringBuilder("select id1, ").append(" id2 ").append(" from ").append(" table").toString();

в Java строковый тип представляет собой неизменяемую последовательность символов,поэтому при добавлении двух строк виртуальная машина создает новое строковое значение с Объединенными операндами.

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

Это обеспечивает некоторые полезные функции, такие как изменение содержимого последовательности символов, передаваемой в качестве параметра внутри другого метода, что вы не можете сделать со строками.

private void addWhereClause(StringBuilder sql, String column, String value) {
   //WARNING: only as an example, never append directly a value to a SQL String, or you'll be exposed to SQL Injection
   sql.append(" where ").append(column).append(" = ").append(value);
}

дополнительная информация на http://docs.oracle.com/javase/tutorial/java/data/buffers.html

вы также можете использовать MessageFormat слишком