【问题标题】:Java create PreparedStatement with no SQL contentJava 创建没有 SQL 内容的 PreparedStatement
【发布时间】:2013-06-29 12:25:11
【问题描述】:

是否可以在不设置初始SQL查询的情况下在java中创建PreparedStatement

示例代码:

@Override
public List<AccountBean> search(AccountConstraint... c) {
    if (c.length == 0) {
        throw new IllegalArgumentException("dao.AccountDAO.search: c.length == 0");
    }
    try {
        List<AccountBean> beans = new ArrayList<>();
        for (AccountConstraint ac : c) {
            PreparedStatement ps = connection.prepareStatement(null);
            QueryBuilder queryBuilder = new QueryBuilder(ps, "SELECT * FROM accounts");
            queryBuilder.add(ac.getAccountIdConstraint());
            queryBuilder.add(ac.getUsernameConstraint());
            queryBuilder.add(ac.getPasswordConstraint());
            queryBuilder.add(ac.getEmailConstraint());
            //INSERT QUERY INTO PS
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                beans.add(new AccountBean(rs));
            }
        }
        return beans;
    } catch (SQLException ex) {
        throw new RuntimeException(ex);
    }
}

诀窍在于QueryBuilder,该类负责根据初始 SELECT 部分构建查询部分,然后添加相应的 WHERE 和 AND 子句。

然而,为了确保所有数据都是安全的,实际的参数也必须放在 PreparedStatement 中,这就是为什么要将它传递给 QueryBuilder。

每个QueryBuilder.add() 都会在 PreparedStatement 中添加一些参数,并在查询末尾附加一个特定的字符串。

我认为一些解决方法是可能的,例如,不要将PreparedStatement 提供给QueryBuilder,而是提供List&lt;Object&gt;,然后编写一个自定义函数,稍后将它们放入PreparedStatement

但是您对此有何想法和建议?

问候。

已添加解决方案

首先进行一些关键更改:

  • QueryBuilder 现在可以正确实现 Builder 模式。
  • QueryBuilder.add() 一次接受多个 Constraints。
  • AccountConstraint 可以给出一个数组,现在给出所有Constraints。

@Override
public List<AccountBean> search(AccountConstraint... c) {
    if (c.length == 0) {
        throw new IllegalArgumentException("dao.AccountDAO.search: c.length == 0");
    }
    try {
        List<AccountBean> beans = new ArrayList<>();
        for (AccountConstraint ac : c) {
            try (PreparedStatement ps = new QueryBuilder("SELECT * FROM accounts").add(ac.getConstraints()).build();ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    beans.add(new AccountBean(rs));
                }
            }
        }
        return beans;
    } catch (SQLException ex) {
        throw new RuntimeException(ex);
    }
}

ps。由于 try-with-resources,我在一个 try{ } 中得到了两个语句。

【问题讨论】:

    标签: java jdbc prepared-statement


    【解决方案1】:

    准备语句意味着编译它,以便您可以使用不同的参数有效地多次执行它。所以,不,在定义之前编译查询是没有意义的。

    据我了解,您希望使用 Java 编译器来帮助您动态定义查询。您为什么不在 compile() 方法中创建准备好的语句,因此,作为您的构建器的结果。此外,如果您使用构建器模式使得对add() 的每次调用都返回this,您的代码将变得更具可读性并且更类似于声明性查询。然后你可以这样写你的查询:

    PreparedStatement ps = new QueryBuilder()
       .select("*")
       .from("accounts")
       .where()
       .add(yourConstraint())
       ...
       .compile();
    

    但是,您必须在循环之前创建准备好的语句。否则,如果您保留对构建器的引用并在循环中调用compile(),您将在每次调用时获得一个新的准备好的语句。因此,您不会获得重用预编译查询的好处。在循环中,您只需为准备好的语句中的变量赋值。

    【讨论】:

    • 用“assist”我的意思是如果你有拼写错误,你会得到编译器错误,而不是只会在运行时导致异常的普通 SQL。
    • 但是,在您构建如此广泛的 API 之前,请快速搜索一下它是否已经存在 :-)
    【解决方案2】:

    创建后,您无法通过 API 修改准备好的语句。如果没有 SQL 语句,您也无法创建它。

    为什么不单独创建查询然后绑定参数呢?您可以使用 Map 来保存参数占位符及其值,以便将它们设置为准备好的语句。

    虽然我只是使用 Spring 的 JDBC 模板来更快地完成相同的事情。

    【讨论】:

      【解决方案3】:

      如何改进您的 SQL 查询构建器

      如果你看看 popular query builders like jOOQothers 是如何做到的,你的想法是你可以更彻底地分离你的关注点。你应该有:

      • SQL 语句的表达式树表示(理想情况下不直接对字符串进行操作)
      • 一种方便地构造表达式树的方法,例如通过使用 DSL
      • 某种执行生命周期管理,生成 SQL 字符串、准备语句、绑定变量等。

      或在代码中(jOOQ 示例,但这也适用于您自己的查询构建器):

      Result<?> result =
      
      // This constructs the expression tree through the jOOQ DSL
      ctx.selectFrom(ACCOUNTS)
         .where(ac.getAccountIdConstraint())
         .and(ac.getUsernameConstraint())
         .and(ac.getPasswordConstraint())
         .and(ac.getEmailConstraint())
      
      // This internally creates a PreparedStatement, binds variables, executes it, and maps results
         .fetch();
      

      当然,您的AccountConstraint.getXYZConstraint() 方法不会返回SQL 字符串sn-ps,而是返回表达式树元素。在 jOOQ 的情况下,这将是 Condition

      (免责声明:我为 jOOQ 的供应商工作)

      如何提高 SQL 性能

      我注意到您对 N 个AccountConstraint 值运行 N 次查询,并且您以某种方式混合结果,即哪个AccountConstraint 值产生哪个AccountBean 并不重要。我强烈建议您将该循环移到生成的 SQL 查询中,因为您将在几乎每个数据库上获得更快的结果。 I've blogged about this here.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-03
        • 2011-02-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多