在 SQL 中使用 StringBuilder 的正确方法

2022-08-31 11:35:00

我刚刚在我的项目中发现了一些这样的sql查询构建:

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

这是否达到了它的目的,即减少内存使用量?StringBuilder

我对此表示怀疑,因为在构造函数中使用了“+”(字符串concat运算符)。这会占用与使用 String(如下面的代码)相同的内存量吗?我明白了,使用时会有所不同。StringBuilder.append()

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

两个语句在内存使用量上是否相等?请澄清。

提前致谢!

编辑:

顺便说一句,这不是我的代码。在一个旧项目中找到它。此外,查询并不像我示例中的查询那么小。:)


答案 1

使用StringBuilder的目的,即减少内存。它实现了吗?

不,一点也不。该代码未正确使用。(不过,我认为你错误地引用了它;当然,周围没有引号,而且?StringBuilderid2table

请注意,目标(通常)是减少内存改动而不是使用的总内存,以使垃圾回收器的生活更轻松一些。

这会占用足够的内存来等于使用如下所示的字符串吗?

不,它会导致更多的内存流失,而不仅仅是您引用的直线连接。(直到/除非 JVM 优化器看到代码中的显式是不必要的,如果可以的话,请将其优化掉。StringBuilder

如果该代码的作者想要使用(有论据支持,但也有反对;请参阅本答案末尾的注释),最好正确执行(这里我假设周围实际上没有引号和):StringBuilderid2table

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

请注意,我已经在构造函数中列出,因此它开始时有足够的容量来容纳我们要追加的完整内容。如果您不指定一个字符,则使用的默认大小为16个字符,这通常太小,并且导致每次用完空间时都必须进行重新分配以使自身变大(IIRC,在Sun / Oracle JDK中,它会使自身加倍[或更多,如果它知道它需要更多来满足特定])。some_appropriate_sizeStringBuilderStringBuilderappend

您可能听说过,如果使用 Sun/Oracle 编译器编译,字符串串联将使用掩护。这是真的,它将使用一个作为整体表达式。但它将使用默认构造函数,这意味着在大多数情况下,它将不得不进行重新分配。不过,它更容易阅读。请注意,对于一系列串联,情况并非如此。例如,这使用一个:StringBuilderStringBuilderStringBuilder

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();

所以没关系,尽管默认构造函数和随后的重新分配并不理想,但可能性是它足够好 - 并且串联更具可读性。

但这仅适用于单个表达式。多个 s 用于此目的:StringBuilder

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;

...这很丑陋。

但是,重要的是要记住,除了极少数情况外,在所有情况下都无关紧要,除非存在特定的性能问题,否则最好使用可读性(增强可维护性)。


答案 2

当您已经拥有要附加的所有“片段”时,使用根本没有意义。在根据示例代码在同一调用中使用字符串串联甚至更糟。StringBuilderStringBuilder

这样会更好:

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

在这种情况下,字符串串联实际上是在编译时发生的,所以它相当于更简单的:

return "select id1, id2 from table";

在这种情况下,使用实际上会降低性能,因为它强制在执行执行串联,而不是在编译时执行。哎呀。new StringBuilder().append("select id1, ").append(" id2 ")....toString()

如果实际代码通过在查询中包含来构建 SQL 查询,则这是另一个单独的问题,即您应该使用参数化查询,在参数中指定值,而不是在 SQL 中指定值。

我有一篇关于String / StringBuffer的文章,我不久前写过 - 在出现之前。不过,这些原则也以同样的方式适用。StringBuilderStringBuilder