[英]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模板來更快地完成相同的事情。
如果您查看jOOQ 等 流行的查詢構建器是如何做到的,則想法是您可以更徹底地分離您的關注點。 你應該有:
或在代碼中(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的供應商工作)
我注意到您對N個AccountConstraint
值運行了N個查詢,並且混合結果的方式與哪個AccountConstraint
值產生哪個AccountBean
無關。 我強烈建議您將該循環移到生成的SQL查詢中,因為您將在幾乎每個數據庫上獲得更快的結果。 我在這里寫過博客 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.