嵌套准备语句的最佳实践是什么?

时间:2008-12-22 21:33:48

标签: java sql prepared-statement

我有几个实例,其中遗留sql语句的一部分基于依赖项。例如。

if (x !=null)
{
  SQL = "SELECT z WHERE x > y";
}
else
{
  SQL = "SELECT z WHERE x <= y";
} 

SQL2 = SQL + " JOIN a ON b";

我正在使用此遗留代码创建PreparedStatements。这里最好的做法是什么?我是否应该为var SQL创建一个PreparedStatement并将其嵌入SQL2中,如果有多个基于SQL2的PreparedStatements没有嵌套,或者某些东西完全不同?

代码比示例复杂得多,因为SQL var在许多冗长而复杂的SQL查询中被重用。

编辑:项目设计需要使用PreparedStatements,我现在无法选择使用库。

6 个答案:

答案 0 :(得分:4)

&gt;我应该为var SQL创建PreparedStatement并将其嵌套在SQL2

没有

&gt;或者是否应该有多个基于SQL2的PreparedStatements而不嵌套

此外:如果您可以为每个查询创建一个更好的字符串。我真的不喜欢将SQL与代码混合在一起。它使调试和理解变得更加困难,您无法复制/粘贴到SQL工具来轻松地进行测试。通过将SQL与代码分开,您可以将查询与操作(实际提取)隔离开来,并且更容易维护。此外,如果代码不是你的,那么理解起来会容易得多。

看起来你的重复字符串并不重要,重点是尽可能地简化陈述。

我会做这样的事情:

final class DatabaseQueries {
    public final static String SOME_SCENARIO       = "SELECT z WHERE x > y JOIN A, B ";
    public final static String SOME_OTHER_SCENARIO = "SELECT z WHERE x <= y JOIN A, B";
 }

然后在课堂上使用它:

 PreparedStatement pstmt = getCon().prepareStatement( getQuery() );


 private String getQuery() { 
     if( x != null ) { 
          return DatabaseQueries.SOME_SCENARIO;
     } else { 
           return DatabaseQueries.SOME_OTHER_SCENARIO;
     }
 }

在创建“DatabaseQueries”类时,你会发现你重复了很多字符串,我认为用其他常量来支持某些部分会很好。

final class DataBaseQueries { 
    // this one is private
    private final static String JOIN_A_B = " join A, B ";
    public final static String SOME_SCENARIO       = "SELECT z WHERE x > y " + JOIN_A_B ;
    public final static String SOME_OTHER_SCENARIO = "SELECT z WHERE x <= y " + JOIN_A_B ;

}

这里的要点是让事情变得简单。这是第一步。在第二步中,您可以创建一个类来创建那些非常复杂的查询,但可能是YAGNI。

如果查询过多,您可以将其替换为从ResourceBundle中加载它们,例如question

我希望这会有所帮助。

答案 1 :(得分:2)

Ibatis非常擅长这一点。

<select id="queryName" parameterClass="com.blah.X"><!<[CDATA[
  SELECT z
  FROM a
  JOIN b ON a.id = b.foreign_key
  WHERE

  <isNotNull property="value">
    x > y
  </isNotNull>

  <isNull property="value">
    x <= y
  </isNull>

]]></select>

这只是Ibatis可以做的一小部分,但它非常轻巧。优秀的技术。

答案 2 :(得分:1)

这不是正确使用预准备语句参数。只能使用参数代替SQL表达式中的文字值。不是表名,列名或其他SQL语法。

您可以使用某些库来构建SQL查询的一部分。我在PHP中使用这个库,名为Zend_Db_Select

编辑:我搜索了一个类似的Java库,我发现这个选项可能会有所帮助:

  • Squiggle是一个用于动态生成SQL SELECT语句的小型Java库。 [它]的最佳位置适用于需要使用在运行时更改的条件构建复杂查询的应用程序。通常,弄清楚如何构建这个字符串可能会非常痛苦。 Squiggle消除了很多痛苦。

它是免费的,并根据Apache许可证提供,这是一个非常灵活的开源许可证。

谷歌搜索“java查询构建器”发现了许多其他选项,但有些不是免费的。有些是可视化查询构建器,而不是程序化查询构建器。

另一种选择是使用像Hibernate这样复杂的对象关系映射框架,但这对你当前的任务来说似乎有些过分。

答案 3 :(得分:0)

Here is similar question

简短的回答 - 没有最好的方法。你可能会得到这样的结论:

String selectQuery =
  (new SelectQuery())
  .addColumns(t1Col1, t1Col2, t2Col1)
  .addJoin(SelectQuery.JoinType.INNER_JOIN, joinOfT1AndT2)
  .addOrderings(t1Col1)
  .validate().toString();

但对我来说情况更糟。

答案 4 :(得分:0)

我想一方面有一种纯粹的对象方法,如果你试图理解遗留代码,这可能不会对你有很大的帮助。我发现,在重构真正讨厌的遗留代码而不是追求“完美”时,通常更好,更容易,简化小块,模块化和文档记录,因为你可以在不重写整个应用程序的情况下制作它们。我发现重构坏代码对我来说最大的障碍是,如果我采取太大的步骤,我就不能再相信我没有破坏任何东西 - 如果它是那么糟糕,可能没有单元测试,并且可能存在未定义或未记录的行为。

我至少会将逻辑和sql作为第一遍传递出去。你不想要的东西是这样的:

String sql = "yadda yadda yadda ? yadda yadda WHERE ";
if (mystery condition 1){
   sql = sql + " page=?"
}
else if (mystery condition 2)
{
 sql = sql + " ORDER BY ? "
}

过了一段时间,你将无法分辨出正在构建的语句。不值得保存复制初始sql的位。它可能更容易理解为:

private static final String FIND_PAGE_QUERY = "...."
private static final String ORDER_BY_QUERY =" ..."

if (mystery condition 1){
   return process(FIND_PAGE_QUERY, parameters);
}
else if (mystery condition 2)
{
  return process(ORDER_BY_QUERY, parameters);
}

然后只需创建一些内容来包装您的查询和传递的参数,如Spring JDBC Row Mappers或类似的东西。这仍然是丑陋的,但它很容易做到从你所拥有的增量步骤,并将至少解决一些查询生成的混乱。

答案 5 :(得分:0)

另一种方法是将条件移动到SQL语句本身,这样您只需要一个语句。它会是这样的:

SELECT z WHERE (? IS NOT NULL AND x > y) OR (? IS NULL AND x <= y)

然后为参数绑定适当的值。

不确定这是最好的方法......人们可能会发现代码不太清楚。但这是一种可能性。