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

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

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

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

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

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

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

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

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

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

【问题讨论】:

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

标签: java sql mysql coding-style


【解决方案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% 肯定的方法!
      猜你喜欢
      • 2011-08-11
      • 2011-02-11
      • 1970-01-01
      • 1970-01-01
      • 2011-03-14
      • 2012-09-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多