はじめに
APIバージョン57.0にて動的SOQLのバインド対応メソッドが追加されました。
追加されたのは下記メソッドになります。
・Database.queryWithBinds
・Database.countQueryWithBinds
・Database.getQueryLocatorWithBinds
参考:Apex 開発者ガイド(動的 SOQL)
有用な機能なのですが、あまり知られていない気がしますので、上記から代表してDatabase.queryWithBindsを紹介したいと思います。
追加されたのは下記メソッドになります。
・Database.queryWithBinds
・Database.countQueryWithBinds
・Database.getQueryLocatorWithBinds
参考:Apex 開発者ガイド(動的 SOQL)
有用な機能なのですが、あまり知られていない気がしますので、上記から代表してDatabase.queryWithBindsを紹介したいと思います。
機能紹介の前に
この機能が追加されたことによって何が変わったのかを紹介する前に、まずは、「バインド変数」「静的SOQL」「動的SOQL」について説明します。
・バインド変数
SOQLクエリに対して使用するApexの変数です。
バインド変数の特徴は下記2つになります。
①クエリに変数を動的に組み込むことが出来る
②SOQLインジェクション対策(文字エスケープ)が行われる
バインド変数の特徴は下記2つになります。
①クエリに変数を動的に組み込むことが出来る
②SOQLインジェクション対策(文字エスケープ)が行われる
・静的SOQL
静的SOQLはコード内でリテラルで記載するクエリになります。
クエリにバインド変数を設定することで、条件を動的にすることが可能になります。
クエリにバインド変数を設定することで、条件を動的にすることが可能になります。
List<String> nameList = new List<String>{'株式会社テラスカイ','株式会社キットアライブ','株式会社BeeX', '架空会社Terra\'s Sky'}; List<Account> accountList = [SELECT Id,Name FROM Account WHERE Name = :nameList];
例1 静的SOQL
・動的SOQL
動的SOQLはクエリを文字列として、パラメータなどをもとにクエリを動的に組み立てます。
これにはバインド変数が設定できないため※、動的に値を設定したい箇所は変数で組み立てを行うことになります。
※APIバージョン56.0まで
これにはバインド変数が設定できないため※、動的に値を設定したい箇所は変数で組み立てを行うことになります。
※APIバージョン56.0まで
List<String> nameList = new List<String>{'株式会社テラスカイ','株式会社キットアライブ','株式会社BeeX', '架空会社Terra\'s Sky'}; String condIn = ''; for (String condName : nameList) { condIn += '\'' + String.escapeSingleQuotes(condName) + '\','; } condIn = condIn.removeEnd(','); String soqlQuery = 'SELECT Name FROM Account'; soqlQuery += ' WHERE Name IN (' + condIn + ')'; List<Account> accountList = Database.query(soqlQuery);
例2 動的SOQL
動的SOQLはクエリの自由な組み立てができるためによく実装されますが、バインド変数が使用出来ないため、コードが複雑になりがちです。
エスケープ処理も都度適用する必要があることで、さらに可読性が落ちてしまい、コードのメンテナンスなど負荷が上がってしまいます。
エスケープ処理も都度適用する必要があることで、さらに可読性が落ちてしまい、コードのメンテナンスなど負荷が上がってしまいます。
バインド対応動的SOQLメソッド(Database.queryWithBinds)の使用
今回紹介する「Database.queryWithBinds」の利用例を説明する前に、比較するためにも複雑な動的SOQLを組む場合にバインド変数を使用しない場合の実装例を紹介します。
public static List<Account> getAccountList(List<String> nameList, String country, String postalCode, Boolean isParent) { // 引数をもとにクエリを動的に組み立てる String soqlQuery = 'SELECT Id,Name FROM Account'; soqlQuery += ' WHERE BillingCountry = \'' + String.escapeSingleQuotes(country) + '\''; soqlQuery += ' AND BillingPostalCode like \'' + String.escapeSingleQuotes(postalCode) + '%\''; String condIn = ''; for (String condName : nameList) { condIn += '\'' + String.escapeSingleQuotes(condName) + '\','; } condIn = condIn.removeEnd(','); if (nameList != null && 0 < nameList.size()) { if (isParent) { soqlQuery += ' AND Parent.name IN (' + condIn + ')'; } else { soqlQuery += ' AND Name IN (' + condIn + ')'; } } List<Account> accountList = Database.query(soqlQuery); return accountList; }
例3 動的SOQL(Database.query)
入力された値をもとに検索条件を組み立てますが、各変数にString.escapeSingleQuotesを設定する必要があります。
また、In句の作成箇所などは面倒な手順が必要になっています。
上記の動的SOQLを「Database.query」ではなく、「Database.queryWithBinds」を使用するとバインド変数が使用可能になるため、下記のようになります。
また、In句の作成箇所などは面倒な手順が必要になっています。
上記の動的SOQLを「Database.query」ではなく、「Database.queryWithBinds」を使用するとバインド変数が使用可能になるため、下記のようになります。
public static List<Account> getAccountList(List<String> nameList, String country, String postalCode, Boolean isParent) { // 引数をもとにクエリを動的に組み立てる String soqlQuery = 'SELECT Id,Name FROM Account'; soqlQuery += ' WHERE BillingCountry = :bind1'; soqlQuery += ' AND BillingPostalCode like :bind2'; if (nameList != null && 0 < nameList.size()) { if (isParent) { soqlQuery += ' AND Parent.name = :bind3'; } else { soqlQuery += ' AND Name = :bind3'; } } // クエリにバインド変数を適用する Map<String, Object> bindValiables = new Map<String, Object>{'bind1' => country, 'bind2' => (postalCode+'%'), 'bind3' => nameList}; List<Account> accountList = Database.queryWithBinds(soqlQuery, bindValiables, AccessLevel.USER_MODE); return accountList; }
例4 動的SOQL(Database.queryWithBinds)
バインド変数が使用できることでコードがかなりすっきりしました。
nameListがnullまたは空の場合は検索条件から外すという条件を実装していますが、bind3がクエリ文字列に含まれていない場合でも、bind3を含めるバインド変数Mapを適用してもエラーにはなりません。
また、第三引数で指定しているAccessLevelですが、これはユーザモードデータベース操作になります。
ユーザモードまたはシステムモードを指定できますが、SOQLのモードの指定については弊社のTech Blogにも記載がありますので参照ください。
Dreamforce2023 Day1:Apexロードマップのセッションレポート
nameListがnullまたは空の場合は検索条件から外すという条件を実装していますが、bind3がクエリ文字列に含まれていない場合でも、bind3を含めるバインド変数Mapを適用してもエラーにはなりません。
また、第三引数で指定しているAccessLevelですが、これはユーザモードデータベース操作になります。
ユーザモードまたはシステムモードを指定できますが、SOQLのモードの指定については弊社のTech Blogにも記載がありますので参照ください。
Dreamforce2023 Day1:Apexロードマップのセッションレポート
おわりに
今回追加された機能で動的SOQLにもバインド変数が使用できることになったため、動的SOQLの組み立てが簡潔になり、SOQLインジェクション対策も実施されることで可読性も良くなったかと思います。
今回は追加された3メソッドの内から「Database.queryWithBinds」を例に紹介しましたが、他の2メソッドも同様に、既存のDatabase.countQuery、Database.getQueryLocatorにバインド変数が適用できるようになっています。
これらのメソッドで新しく何かが出来るようになった訳ではありませんが、有用な機能だと思われます。
今後のApex開発ではぜひ採用してみてください。
今回は追加された3メソッドの内から「Database.queryWithBinds」を例に紹介しましたが、他の2メソッドも同様に、既存のDatabase.countQuery、Database.getQueryLocatorにバインド変数が適用できるようになっています。
これらのメソッドで新しく何かが出来るようになった訳ではありませんが、有用な機能だと思われます。
今後のApex開発ではぜひ採用してみてください。
22 件