一段 SQL 在 Java 里换行,我存成了诡异的格式
前几天 Code Review,看到有人拼一条多行 SQL 用了老套路:
String sql = "SELECT id, name, price FROM product "
+ "WHERE category = ? AND status = ? "
+ "ORDER BY create_time DESC";
这写法本身能跑,但十几个 + 连起来又丑又容易在行尾漏空格。JDK 15 的文本块(Text Block)早就能优雅解决,可团队里还有人不敢用——因为缩进和转义有几个坑。我把正确姿势写清楚。
文本块长什么样
文本块用三个双引号 """ 包裹,开始定界符之后要换行,结束定界符决定左边界:
String sql = """
SELECT id, name, price
FROM product
WHERE category = ? AND status = ?
ORDER BY create_time DESC
""";
这段生成的字符串,每行内容是 SELECT id, name, price 等,前面的缩进被自动去掉了——但去掉多少,是有规则的。
缩进规则: incidental 与 common 之分
文本块里每行可能有两种缩进:
- incidental whitespace(附带空白):为了和 Java 代码对齐而写的缩进,会被剔除。
- common whitespace(公共空白):所有行共有的最小缩进量,会被剥掉。
编译器取所有内容行的最小缩进作为 common,整体左移。上面那段,结束符 """ 在最左(0 缩进),所以每行的公共缩进被剥到 0,得到干净的 SQL。但如果结束符多缩进呢:
String sql = """
SELECT a, b
FROM t
"""; // 结束符缩进 8 空格
此时 common 是 12 空格("SELECT"那行缩进最多),所有行左移 12,结果每行以 0 开头——结束符的位置只定义边界,真正剥离的是"最小公共缩进"。想故意保留左侧空格,用 \s 转义空白避免被剥。
转义:大部分不用转义了
文本块里 " 和 \ 大多不用转义。下面这条 JSON 里双引号直接写:
String json = """
{
"userId": 88123,
"action": "PAID",
"items": [1, 2, 3]
}
""";
需要强制换行时用 \n,需要抑制行尾换行(把结束符接到最后一行)用行尾加 \:
String oneline = """
SELECT * FROM t \
WHERE id = 1"""; // 结果是 "SELECT * FROM t WHERE id = 1"
和字符串拼接对比:可读性完胜
| 写法 | 可读性 | 易错点 |
|---|---|---|
| 加号拼接 | 差,满屏 + 和空格 | 行尾漏空格导致 SQL 粘连 |
| String.format | 中,占位符分散 | 参数顺序错难发现 |
| 文本块 | 好,所见即所得 | 缩进规则要理解 |
文本块还能配合 String::formatted 做占位,比 + 清爽:
String sql = """
SELECT * FROM product
WHERE category = %s AND price > %s
""".formatted(category, minPrice);
一个要避的坑
文本块里末尾会带一个换行符(结束符前的换行)。如果拼出的值要精确无尾随换行(比如某些协议头),用 .strip() 或把内容贴着结束符写。我们曾因此多出一行导致签名校验失败。
小结
- 文本块用
""",开始定界符后必须换行。 - 缩进剥离按"最小公共缩进"算,结束符只定边界。
- 双引号、反斜杠在文本块里基本不用转义,
\n强制换行、\抑制换行。 - 写 SQL/JSON 时可读性远胜加号拼接,但要小心尾随换行。
文本块不是什么大特性,但它每天都能让我少犯"少打个空格 SQL 粘连"的低级错误。代码里那种满屏加号的拼接,现在在我这过不了 Review。