簡體   English   中英

Java創建沒有SQL內容的PreparedStatement

[英]Java create PreparedStatement with no SQL content

是否可以在Java中創建PreparedStatement而不設置初始SQL查詢?

示例代碼:

@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中添加一些參數,並在查詢末尾附加一個特定的字符串。

我認為一些解決方法是可行的,例如,可以給List<Object>而不是給QueryBuilder提供PreparedStatement ,然后編寫一個自定義函數,稍后將它們放入PreparedStatement中。

但是您對此有何想法和建議?

問候。

解決方案已添加

首先很少更改鍵:

  • QueryBuilder現在可以正確實現Builder模式。
  • QueryBuilder.add()一次接受多個Constraint
  • AccountConstraint可以提供一個數組,該數組現在提供所有Constraint

@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編譯器來幫助您動態定義查詢。 您為什么不直接在compile()方法中創建准備好的語句,這是您生成器的結果。 另外,如果您使用構建器模式,那么每次對add()調用都返回this ,代碼將變得更具可讀性,並且更類似於聲明性查詢。 然后,您可以這樣編寫查詢:

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

但是,您必須在循環之前創建准備好的語句。 否則,如果保留對構建器的引用並在循環中調用compile() ,則每次調用都會獲得一個新的准備好的語句。 因此,您將不會獲得重新使用預編譯查詢的好處。 在循環中,您僅將值分配給准備好的語句中的變量。

創建完后,您無法通過API修改准備好的語句。 您也不能沒有SQL語句來創建它。

為什么不分別創建查詢然后綁定參數? 您可以使用Map來保存參數占位符及其值,以便可以將它們設置為prepared語句。

盡管我只是使用Spring的JDBC模板來更快地完成相同的事情。

如何改善您的SQL查詢構建器

如果您查看jOOQ 流行的查詢構建器是如何做到的,則想法是您可以更徹底地分離您的關注點。 你應該有:

  • 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字符串摘要,而是返回表達式樹元素。 對於jOOQ,這將是一個Condition

(免責聲明:我為jOOQ的供應商工作)

如何提高SQL性能

我注意到您對N個AccountConstraint值運行了N個查詢,並且混合結果的方式與哪個AccountConstraint值產生哪個AccountBean無關。 強烈建議您將該循環移到生成的SQL查詢中,因為您將在幾乎每個數據庫上獲得更快的結果。 我在這里寫過博客

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM