前军教程网

中小站长与DIV+CSS网页布局开发技术人员的首选CSS学习平台

Java修炼终极指南:01 创建多行SQL、JSON和HTML字符串


让我们考虑以下多行SQL字符串:

UPDATE "public"."office"  
SET ("address_first", "address_second", "phone") =  
  (SELECT "public"."employee"."first_name",  
          "public"."employee"."last_name", ?  
   FROM "public"."employee"  
   WHERE "public"."employee"."job_title" = ?


众所周知,在JDK 8之前,我们可以以多种方式将这段SQL包装成Java字符串(字符串字面量)。

在JDK 8之前

可能最常见的方法是使用众所周知的"+"运算符进行直接连接。这样,我们得到如下的多行字符串表示:

String sql =   
  "UPDATE \"public\".\"office\"\n"  
+ "SET (\"address_first\", \"address_second\", \"phone\") =\n"  
+ "  (SELECT \"public\".\"employee\".\"first_name\",\n"  
+ "          \"public\".\"employee\".\"last_name\", ?\n"  
+ "   FROM \"public\".\"employee\"\n"  
+ "   WHERE \"public\".\"employee\".\"job_title\" = ?";


编译器应该(并且通常是)足够智能,将"+"操作内部转换为StringBuilder/StringBuffer实例,并使用append()方法来构建最终的字符串。但是,我们可以直接使用StringBuilder(非线程安全)或StringBuffer(线程安全),如以下示例所示:

StringBuilder sql = new StringBuilder();  
sql.append("UPDATE \"public\".\"office\"\n")  
   .append("SET ...\n")  
   .append("  (SELECT...\n")  
   // ... 省略其他部分


另一种方法(可能不如前两种流行)是使用String.concat()方法。这是一个不可变操作,基本上将给定的字符串附加到当前字符串的末尾。最后,它返回新的组合字符串。尝试附加null值将导致NullPointerException(在前面的两个示例中,我们可以附加null值而不会引发任何异常)。通过链接concat()调用,我们可以像以下示例一样表示多行字符串:

String sql = "UPDATE \"public\".\"office\"\n"  
  .concat("SET...\n")  
  .concat("  (SELECT...\n")  
  // ... 省略其他部分


进一步来说,我们有String.format()方法。只需使用%s格式说明符,我们就可以在多行字符串中连接多个字符串(包括null值),如下所示:

String sql = String.format("%s%s%s%s%s%s",   
   "UPDATE \"public\".\"office\"\n",  
   "SET ...\n",  
   "  (SELECT ...\n",  
   // ... 省略其他部分


虽然这些方法如今仍然很流行,但让我们看看JDK 8在这个话题上有什么要说的。

从JDK 8开始

从JDK 8开始,我们可以使用String.join()方法来表示多行字符串。此方法也专门用于字符串连接,它允许我们的示例具有清晰的可读性。如何实现?此方法将分隔符作为第一个参数,并在要连接的字符串之间使用此分隔符。因此,如果我们认为\n是我们的行分隔符,那么它只需要指定一次,如下所示:

String sql = String.join("\n",  
 "UPDATE \"public\".\"office\"",  
 "SET (\"address_first\", \"address_second\", \"phone\") =",  
 "  (SELECT \"public\".\"employee\".\"first_name\",",  
 "          \"public\".\"employee\".\"last_name\", ?",  
 "   FROM \"public\".\"employee\"",  
 "   WHERE \"public\".\"employee\".\"job_title\" = ?;");


除了String.join()方法外,JDK 8还提供了java.util.StringJoiner。StringJoiner支持分隔符(如String.join())但也支持前缀和后缀。表达我们的多行SQL字符串不需要前缀/后缀;因此分隔符仍然是我们最喜欢的功能:

StringJoiner sql = new StringJoiner("\n");  
sql.add("UPDATE \"public\".\"office\"")  
   .add("SET (\"address_first\", ..., \"phone\") =")  
   .add("  (SELECT \"public\".\"employee\".\"first_name\",")  
   // ... 省略其他部分


最后,谈到JDK 8就不能不提它的强大的Stream API。更具体地说,我们对Collectors.joining()收集器感兴趣。这个收集器的工作方式与String.join()相同,在我们的例子中,它看起来像这样:

String sql = Stream.of(  
 "UPDATE \"public\".\"office\"",  
 "SET (\"address_first\", \"address_second\", \"phone\") =",  
 "  (SELECT \"public\".\"employee\".\"first_name\",",  
 "          \"public\".\"employee\".\"last_name\", ?",  
 "   FROM \"public\".\"employee\"",  
 "   WHERE \"public\".\"employee\".\"job_title\" = ?;")  
 .collect(Collectors.joining(String.valueOf("\n")));


所有前面的示例都有一个共同的缺点。最重要的是,这些示例中没有一个是真正的多行字符串字面量,每行之间的转义字符和额外引号严重影响了可读性。幸运的是,从JDK 13(作为未来预览)到JDK 15(作为最终功能),新的文本块已成为表示多行字符串字面量的标准。让我们看看如何实现。

引入文本块(JDK 13/15)

JDK 13(JEP 355)引入了一个预览功能,旨在为多行字符串字面量提供支持。在JDK 15(JEP 378)的两个版本中,文本块功能已成为最终且永久可用的功能。但是,让我们快速看看文本块如何塑造我们的多行SQL字符串:

String sql="""  
           UPDATE "public"."office"  
           SET ("address_first", "address_second", "phone") =  
             (SELECT "public"."employee"."first_name",  
                     "public"."employee"."last_name", ?  
              FROM "public"."employee"  
              WHERE "public"."employee"."job_title" = ?""";


这太棒了,对吧?!我们立即注意到SQL的可读性已经恢复,我们没有混淆分隔符、行终止符和连接。文本块简洁、易于更新且易于理解。在SQL字符串中额外的代码足迹为零,Java编译器将尽最大努力以尽可能可预测的方式创建字符串。以下是嵌入JSON信息的另一个示例:

String json = """  
              {  
                "widget": {  
                  "debug": "on",  
                  "window": {  
                    "title": "Sample Widget 1",  
                    "name": "back_window"  
                  },  
                  // ... 省略其他部分  
              }""";


那么,如何用文本块表示HTML呢?

String html = """  
              <table>  
                <tr>  
                  <th>Name</th>  
                  <th>Age</th>  
                </tr>  
                <tr>  
                  <td>John</td>  
                  <td>Smith</td>  
                  <!-- 这里有一个错误,缺少年龄单元格 -->  
                 </tr>  
              </table>"""; // 注意这里修正了原始代码中的错误,应该是</table>而不是<table>


挂接文本块语法

文本块的语法相当简单。没有花哨的东西,也没有复杂的事情,只需要记住两个方面:

1. 文本块必须以"""(即三个双引号)和换行符开始。我们称此构造为开放分隔符。

2. 文本块必须以"""(即三个双引号)结束。"""可以单独放在一行(作为新行)或放在文本的最后一行末尾(如我们的示例所示)。我们称此构造为关闭分隔符。但是,这两种方法之间存在语义差异(在下一个问题中详细讨论)。

在此上下文中,以下示例在语法上是正确的:

String tb = """  
            I'm a text block""";  
  
String tb = """  
            I'm a text block  
            """;  
  
// ... 其他正确示例


另一方面,以下示例是不正确的,并会导致编译器错误:

String tb = """I'm a text block"""; // 错误:缺少换行符  
// ... 其他错误示例


但是,请考虑以下最佳实践。

通过查看前面的代码片段,我们可以为文本块塑造一个最佳实践:仅在您有多行字符串时使用文本块;如果字符串适合单行代码(如前面的代码片段所示),则使用普通字符串字面量,因为使用文本块不会增加任何显著价值。

在捆绑的代码中,您可以在SQL、JSON和HTML的片段上实践本问题中的所有示例。

对于第三方库支持,请考虑:Apache Commons, StringUtils.join(), 和 Guava Joiner.on()。

接下来,让我们专注于处理文本块分隔符。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言