意外と知らないSalesforce Tips (6~7)

またもやこのシリーズです。
今回は6つめと7つめをお送りします。

6. VisualforceをRead-Onlyモードで実行する

Apexの実行には実はRead-Onlyモードというものがあります。
これ、何か良いことがあるのかというとガバナ制限が緩和されるというメリットがあります。

どうしても大量のデータを扱いたいといったケースなどには、特別な細工をせずに扱えるデータ件数が増えるので利用価値がありそうです。

Read-Onlyモードの設定の仕方には2通りの方法があります。

(1) Visualforceページ全体をRead-Onlyモードにする
(2) コントローラークラスのメソッドをRead-Onlyモードにする

どちらの方法でモード設定するかによって緩和される制限に差があります。

具体的な緩和されるガバナ制限の内容は下記になります。

・1トランザクションで取得可能なレコード数上限が緩和される

50,000件 → 1,000,000件

・繰り返しコンポーネントでの使用可能コレクションサイズの上限が緩和される

1,000件 → 10,000件
対象の繰り返しコンポーネントは<apex:repeat><apex:dataTable><apex:dataList>の3つ
※Visualforceページ全体をRead-Onlyモードにした時のみ適用されます。

ここからはRead-Onlyモードの設定の仕方について書いていきます。

まずは、Visualforceページ全体をRead-Onlyモードにする方法から。
これはすごく簡単で、<apex:page>タグにパラメータ「readOnly="true"」を追加するだけです。

使用例: Visualforce側

<apex:page readOnly="true" controller="ReadOnlyController" action="{!init}" >
    <apex:repeat value="{!accountList}" var="ac">
        <apex:outputField value="{!ac.Id}"/>
        <apex:outputField value="{!ac.Name}"/>
        <hr />
    </apex:repeat>
    <apex:form>
        <apex:commandButton action="{!save}" value="Save"/>
    </apex:form>
</apex:page>

使用例: コントローラー側

global class ReadOnlyController {

    public List<Account> accountList {get; set;}
    
    public PageReference init(){
        accountList = [SELECT Id, Name FROM Account];
        return null;
    }
    
    // Read-Onlyモードだとこの処理は失敗する
    public PageReference save(){
        update [SELECT Id, Name FROM Account LIMIT 1];
        return null;
    }
}

コントローラー側は特に何もする必要はありません。
ちなみに、Read-Onlyモードの時にコントローラー側でデータの更新をしようとすると「Too many DML statements」の例外が発生します。

次はコントローラーのメソッド単位でRead-Onlyモードを設定する方法です。
コントローラーのメソッドにRead-Onlyモードを設定するには「@ReadOnly」あのテーションを付与します。
ただ、どんなメソッドにもアノテーションを付けられる訳ではなく、次の条件を満たしている必要があります。

・ global もしくは public である
・ static である
・ @RemoteAction アノテーションが付いている

ちなみにVisualforceということを抜きにすれば「@ReadOnly」自体はwebserviceなどでも使用できます。
詳細はDeveloper's Guideを見てください。

Force.com Apex Code Developer's Guide - ReadOnly Annotation
https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_annotation_ReadOnly.htm

Visualforce Developer's Guide - Working with Large Sets of Data
https://www.salesforce.com/us/developer/docs/pages/Content/pages_controller_readonly_context.htm

では、実際の使用例です。

使用例: Visualforce側

<apex:page controller="ReadOnlyController2">
    <apex:includeScript value="//code.jquery.com/jquery-1.11.0.min.js" />
    <apex:includeScript value="//code.jquery.com/jquery-migrate-1.2.1.min.js" />
    <apex:includeScript value="//cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js" />

    <div data-bind="foreach: accountList">
        <span data-bind="text: Id" /> / <span data-bind="text: Name" />
        <hr/>
    </div>
    
    <button data-bind="click:save1">Save1</button><br/>
    <button data-bind="click:save2">Save2</button><br/>
    
    <apex:form>
        <apex:commandButton action="{!save3}" value="Save3"/>
    </apex:form>

    <script>
        jQuery.noConflict();    
        jQuery(document).ready(function($){
            var viewModel = new (function(){
                var self = this;
                self.accountList = ko.observableArray();
                
                self.find = function(){
                    Visualforce.remoting.Manager.invokeAction(
                        '{!$RemoteAction.ReadOnlyController2.find}',
                        function(result, e){
                            if(!e.status){
                                alert('error:' + ko.toJSON(e));
                                return;
                            }
                            self.accountList(ko.utils.arrayMap(result, function(o){
                                return {
                                    Id: ko.observable(o.Id),
                                    Name : ko.observable(o.Name)
                                };
                            }));
                        },
                        {escape :false}
                    );
                };
                
                self.save1 = function(){
                    Visualforce.remoting.Manager.invokeAction(
                        '{!$RemoteAction.ReadOnlyController2.save1}',
                        function(result, e){
                            alert((e.status?"success":("failed"+ko.toJSON(e))));
                        },
                        {escape :false}
                    );
                };
                
                self.save2 = function(){
                    Visualforce.remoting.Manager.invokeAction(
                        '{!$RemoteAction.ReadOnlyController2.save2}',
                        function(result, e){
                            alert((e.status?"success":("failed"+ko.toJSON(e))));
                        },
                        {escape :false}
                    );
                };
                
                //initialize
                self.find();
            })();
            ko.applyBindings(viewModel);
        });
    </script>
</apex:page>

使用例: コントローラー側

global class ReadOnlyController2 {
   
    @ReadOnly @RemoteAction global static List<Account> find() {
        return [SELECT Id, Name FROM Account];
    }
    
    // Read-Onlyモードなのでこの処理は失敗する
    @ReadOnly @RemoteAction global static void save1() {
        update [SELECT Id, Name FROM Account LIMIT 1];
    }
    
    @RemoteAction global static void save2() {
        update [SELECT Id, Name FROM Account LIMIT 1];
    }
    
    public PageReference save3(){
        update [SELECT Id, Name FROM Account LIMIT 1];
        return null;
    }
}

ページ全体がRead-Onlyモードになるわけではないので通常のメソッドを呼び出してデータを更新することもできます。

いかがだったでしょうか。
機会があったら活用してみてください。

7. 変更セットへ楽に項目を追加する

変更セット使ってますか。
変更セットは便利といえば便利なのですが、そのUIはお世辞にも使い易いとは言えません。
特にカスタム項目数が数千件ある時に、そのなかから必要なものを選択する場合は一苦労です。

こんなときに次の手順を踏むと少しだけ項目を選ぶのが楽になります。

(1) 一覧で「▼増やす」のリンクをクリック

(2) URLに追加されるパラメータ「rowsperpage」を1000に書き換えたURLに移動

これで一気に一覧の表示件数を上限の1000件に変更することができます。
あとはソートするなり、ページ内検索するなりすればほんの少し楽に項目追加ができるようになります。

さらに手動でURLを書き換えるのすら面倒だという方のために一覧表示を1000件にするブックマークレットを用意してみました。
(やっていることは表示しているURLにパラメータ「rowperpage」があれば値を1000に変更し、なければ値を1000で追加して遷移させているだけです。)

javascript:(function(){var l=location;var h=l.href;if(!(/entityType=/i).test(h)){h+=('&entityType='+document.getElementById('entityType').value);} l.href=((/rowsperpage=[0-9]+/i).test(h))?h.replace(/rowsperpage=[0-9]+/ig,'rowsperpage=1000'):(h+'&rowsperpage=1000');})();

一応、Chromeで動作確認してあります。
もし良かったら使ってやってください。

それでは。

コメント(0)