在 Java 中处理丑陋的 SQL

Posted

技术标签:

【中文标题】在 Java 中处理丑陋的 SQL【英文标题】:Dealing with ugly SQL in Java 【发布时间】:2009-12-17 03:21:57 【问题描述】:

这是一个 SQL-Java 编码风格问题...

这里的其他人如何干净地处理在 Java 中创建复杂的自定义查询?

我说的是准备一个要执行的 SQL 语句的字符串这个看似简单的任务。

我了解 HQL,也了解存储过程,但老实说,我并不真正喜欢这些解决方案。也许我可以以不同的方式说服我。存储过程的部署/维护很烦人,在我的情况下,解析性能并不是一个大问题——灵活性优先。 HQL 似乎是一个很大的飞跃,对我的复杂查询有一些限制。

明确地说,我说的是这样的超级丑陋的代码:

    return 
        "(" + topTwenty + ")" +
        "UNION " +
        "(" + twentyBeforeMe + ")" +
        "UNION " +
        "(" + meDummyQuery + ")" +
        "UNION " +
        "(" + twentyAfterMe + ")";

例如变量 topTwenty 也是类似创建的子查询。

我从没想过我会这么说,但它在 php 中更简洁,它在字符串中嵌入了多行字符串和 $variable。

人们曾经使用过简单的模板库吗?你如何在程序中整齐地保留字符串?或者你把它们放在一个单独的文件中(不知何故我也觉得很烦)。

【问题讨论】:

我打算建议准备好的陈述,但已经有 2 个答案相同:/ 为什么这个被标记的社区维基? 阿萨夫,哎呀,我错了。我没有看到撤消它的选项。 【参考方案1】:

不涉及数据库抽象,您可能可以使用PreparedStatment 使您的查询更具可读性——更不用说它有助于提高安全性。

【讨论】:

杰夫,这是一个很好的提示,但即使有准备好的语句,我仍然有这些怪物,怪物字符串,比如 "SELECT " + " field1, " + " field2 " + [ " FROM" 。 .. 看一下 PreparedStatement,我发现它使用数字索引来索引参数......对于任何复杂的语句,似乎都需要很多麻烦。 不,真正的麻烦是当您的用户设法将“;从用户中删除 *”进入您的某个字段时。 ;) 我建议您在传递给 PreparedStatement() 之前格式化您的查询(例如利用 PrintfFormat() 或任何其他类或您的自定义代码)。【参考方案2】:

您可能希望查看可以使用 JDBC PreparedStatements 执行的查询参数化。如果您来自 PHP 世界,它类似于 PDO。基本上。您上面提到的 PHP 字符串中的所有动态 $variables 在参数化查询中都是 ?s。 ?s 被PreparedStatement 实现的方法以安全(阅读正确转义)的方式替换。

有关示例和进一步说明,请参阅Sun's PreparedStatement tutorial。

【讨论】:

PreparedStatement 是否支持嵌入子选择来代替问号?我熟悉的大多数库只支持嵌入文字值,不支持查询。如果是这种情况,用户仍然需要使用某种字符串格式化程序或模板来创建示例查询,尽管可以使用 PreparedStatement 对各个子选择中的参数进行转义。 @Dathan:不,PreparedStatements 仅支持文字值代替问号。您将通过连接字符串来构建嵌入式子选择。【参考方案3】:

Java 有 PrintFormat 类,它的作用与 C 中的 sprintf 非常相似,您可能想利用它来格式化您的查询。

以下代码 sn-p 取自http://java.sun.com/developer/technicalArticles/Programming/sprintf/

System.out.println(
new PrintfFormat("Pre %s Post%%").sprintf("target")
);  

【讨论】:

【参考方案4】:

我所做的是将 String.format 用于表名和无法参数化的内容,并使用this class(NamedPreparedStament),它允许为绑定变量使用好名称而不是问号。

String sql = "SELECT id FROM %s WHERE id > :lastInsertedYesterday ";
NamedParameterStatement p = new NamedParameterStatement(con, 
                                    String.format(sql, "table1"));
p.setInt("lastInsertedYesterday", lastOne);

【讨论】:

谢谢你,山姆。我会接受你的回答——NamedPreparedStatements 看起来很漂亮,而且你的 String.format 也很漂亮。 根据你的回答看了一圈后,我也发现了这个NamedParameterJdbcTemplate -- vaannila.com/spring/spring-named-parameter-jdbc-template.html。因为我们已经在这个项目中使用了 Spring,所以我会试一试。【参考方案5】:

SQL Construction Set,顺便说一句,这是我的项目。

【讨论】:

【参考方案6】:

如果你认为 StringBuilder.append() 会让你的代码看起来更干净,你可以尝试使用:

return new StringBuilder()
   .append("(").append(topTwenty).append(")")
   .toString();

【讨论】:

这是引入 SQL 注入攻击向量的 100% 肯定的方法!

以上是关于在 Java 中处理丑陋的 SQL的主要内容,如果未能解决你的问题,请参考以下文章

使用 final 成员处理构造函数中捕获的 Java 异常

java事务处理全解析

在 YAML 中使用 enter 处理空格的更漂亮的方法

util.date和sql.date的衔接处理

在它变得丑陋之前,我可以在 Sql server 表中存储多少条记录?

在 Oracle 11g 中执行 PL/SQL 块并在 Java 客户端中处理游标