SalesforceのApex入門：データ操作からバルク処理まで

## はじめに
Salesforceの開発を始めたばかりの方にとって、Apexコンソールは非常に便利なツールです。このツールを使うことで、簡単なデータ操作からデバッグまでをブラウザ上で実行できます。この記事では、Apexコンソールの基本的な使い方から、実践的なデータ操作手法までを解説します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/0e4f193b-24c8-3d66-b693-338b1cb3b5d6.png)

## 目次
1. System.debug()の使い方
2. SOQL基礎からデータ取得
3. コンソールでのデバッグログの見方
4. よくあるエラーと対処法
5. バルク処理とガバナンス制限
6. 安全なデータ操作の実践

## 1. System.debug()の使い方

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/bb8bf0f5-5137-a4c4-75de-45be67621d1d.png)

### 基本的な使い方
System.debug()は、Apexでのデバッグ出力の基本となるメソッドです。

```apex
// 基本的な出力
System.debug('Hello World');

// 変数の値を出力
String name = 'Salesforce';
System.debug('name: ' + name);

// オブジェクトの値を出力
Account acc = new Account(Name='Test Account');
System.debug('Account: ' + acc);
```

### デバッグレベルの指定
System.debug()には、ログレベルを指定することができます。

```apex
// ログレベルの指定
System.debug(LoggingLevel.ERROR, 'エラーレベルのメッセージ');
System.debug(LoggingLevel.WARN, '警告レベルのメッセージ');
System.debug(LoggingLevel.INFO, '情報レベルのメッセージ');
```

## 2. SOQL基礎からデータ取得

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/e5087658-3aa7-0b6a-ddf4-a0ecda23e495.png)

### 基本的なクエリ
SOQLを使用して、Salesforceのデータを取得する方法を見ていきましょう。

```apex
// 単純なクエリ
List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 10];
System.debug('Accounts: ' + accounts);

// WHERE句を使用したフィルタリング
List<Contact> contacts = [
    SELECT Id, FirstName, LastName, Email 
    FROM Contact 
    WHERE Email != null 
    LIMIT 5
];
System.debug('Contacts with email: ' + contacts);

// 関連オブジェクトの取得
List<Account> accountsWithContacts = [
    SELECT Id, Name, (SELECT FirstName, LastName FROM Contacts)
    FROM Account
    WHERE Id IN (SELECT AccountId FROM Contact)
    LIMIT 5
];
System.debug('Accounts with contacts: ' + accountsWithContacts);
```

### データ操作の実践例

```apex
// データの挿入
Account newAccount = new Account(Name='Test Account');
insert newAccount;
System.debug('Inserted Account: ' + newAccount);

// データの更新
newAccount.Name = 'Updated Account';
update newAccount;
System.debug('Updated Account: ' + newAccount);

// データの削除
delete newAccount;
System.debug('Account deleted');
```

## 3. コンソールでのデバッグログの見方

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/af360af1-2741-d6e6-e4ce-a912727e733e.png)


### デバッグログの設定方法
1. 設定から「デバッグログ」を開く
2. 「新規」をクリックして新しいトレースフラグを作成
3. 対象のユーザを選択
4. デバッグレベルを設定（初めは「FINEST」がおすすめ）

### ログの重要な項目
- EXECUTION_STARTED：実行開始
- USER_DEBUG：System.debug()の出力
- SOQL_EXECUTE_BEGIN：SOQLクエリの実行開始
- DML_BEGIN：DML操作の開始
- EXCEPTION_THROWN：例外発生

### ログの読み方の例
```plaintext
43.0 (29810000)|USER_DEBUG|[2]|DEBUG|Hello World
43.0 (29960000)|SOQL_EXECUTE_BEGIN|[5]|SELECT Id, Name FROM Account LIMIT 1
43.0 (30960000)|SOQL_EXECUTE_END|[5]
43.0 (31010000)|USER_DEBUG|[6]|DEBUG|Account: Account:{Id=001xx000003Gyl2AAC, Name=Test Account}
```

## 4. よくあるエラーと対処法

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/65685f60-2a4a-a831-8300-49df92bbaad8.png)

### DMLエラー

```apex
try {
    // 必須項目が未入力の場合
    Account invalidAccount = new Account();
    insert invalidAccount;
} catch (DmlException e) {
    System.debug('エラー: ' + e.getMessage());
    // エラーメッセージ: 必須項目（名前）が入力されていません。
}
```

DML（Data Manipulation Language）は、Salesforceのデータベースを操作するための言語や操作のことを指します。

### SOQLエラー

```apex
try {
    // 存在しないフィールドを参照
    List<Account> accounts = [SELECT InvalidField FROM Account];
} catch (QueryException e) {
    System.debug('エラー: ' + e.getMessage());
    // エラーメッセージ: No such column 'InvalidField' on entity 'Account'
}
```

### リミットエラーの防止
```apex
// クエリの件数制限チェック
if (Limits.getQueries() < Limits.getLimitQueries()) {
    List<Account> accounts = [SELECT Id FROM Account LIMIT 1];
}

// DML操作の制限チェック
if (Limits.getDmlStatements() < Limits.getLimitDmlStatements()) {
    Account acc = new Account(Name='Test');
    insert acc;
}
```

## 5. バルク処理とガバナンス制限

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/1fcf9e0f-e8ee-9d43-04e8-4bbf5e01a942.png)

### Salesforceのガバナンス制限を理解する
Salesforceには様々な制限があり、これらを意識したコーディングが重要です：

```apex
// 主な制限値の確認方法
System.debug('SOQLクエリ制限: ' + Limits.getLimitQueries() + '/' + Limits.getQueries());
System.debug('DML制限: ' + Limits.getLimitDmlStatements() + '/' + Limits.getDmlStatements());
System.debug('ヒープサイズ制限: ' + Limits.getLimitHeapSize() + '/' + Limits.getHeapSize());
```

### バルク処理の実装例

```apex
// 悪い例：ループ内でのDML
for (Account acc : accounts) {
    update acc; // ガバナンス制限に早く到達してしまう
}

// 良い例：リストでまとめて処理
List<Account> accountsToUpdate = new List<Account>();
for (Account acc : accounts) {
    acc.Description = 'Updated';
    accountsToUpdate.add(acc);
}
update accountsToUpdate; // 1回のDMLで処理

// Database.insertの使用例（部分的な成功を許容）
Database.SaveResult[] results = Database.insert(accountsToUpdate, false);
for (Database.SaveResult sr : results) {
    if (!sr.isSuccess()) {
        System.debug('Error: ' + sr.getErrors()[0].getMessage());
    }
}
```

### トリガーでのバルク処理のベストプラクティス
```apex
// トリガーでの効率的なデータ処理例
public class AccountTriggerHandler {
    public static void handleBeforeInsert(List<Account> newAccounts) {
        // 関連データを1回のクエリで取得
        Set<String> accountNames = new Set<String>();
        for (Account acc : newAccounts) {
            accountNames.add(acc.Name);
        }
        
        // 重複チェックの例
        Map<String, Account> existingAccounts = new Map<String, Account>();
        for (Account acc : [SELECT Name FROM Account WHERE Name IN :accountNames]) {
            existingAccounts.put(acc.Name, acc);
        }
        
        // バルク処理での検証
        for (Account acc : newAccounts) {
            if (existingAccounts.containsKey(acc.Name)) {
                acc.Name.addError('同じ名前のアカウントが既に存在します');
            }
        }
    }
}
```

## 6. 安全なデータ操作の実践

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/69d46ec0-3f6b-267d-92ae-02bce64cb2c9.png)

### テスト環境と本番環境の違い
```apex
@isTest
private class AccountOperationsTest {
    @isTest static void testAccountInsert() {
        // テストデータの作成
        Account testAcc = new Account(Name = 'Test Account');
        
        Test.startTest();
        insert testAcc;
        Test.stopTest();
        
        // 検証
        Account insertedAcc = [SELECT Id, Name FROM Account WHERE Id = :testAcc.Id];
        System.assertEquals('Test Account', insertedAcc.Name);
    }
}
```

### データ操作の安全性を高める
```apex
// トランザクション制御
Savepoint sp = Database.setSavepoint();
try {
    Account acc = new Account(Name = 'Test');
    insert acc;
    Contact con = new Contact(); // 意図的にエラーを発生
    insert con;
} catch (Exception e) {
    Database.rollback(sp);
    System.debug('エラーが発生したため、ロールバックしました: ' + e.getMessage());
}

// エラーハンドリングの改善例
public class CustomException extends Exception {}

public void createAccount(String accountName) {
    if (String.isBlank(accountName)) {
        throw new CustomException('アカウント名は必須です');
    }
    
    try {
        insert new Account(Name = accountName);
    } catch (DmlException e) {
        throw new CustomException('アカウントの作成に失敗しました: ' + e.getMessage());
    }
}
```

### デバッグログの詳細な解析例

#### エラーシナリオの再現と解析
```plaintext
// DMLエラーの例
43.0 (29810000)|EXCEPTION_THROWN|[5]|System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Name]: [Name]

// ガバナンス制限エラーの例
43.0 (29810000)|EXCEPTION_THROWN|[10]|System.LimitException: Too many SOQL queries: 101

// 実行時エラーの例
43.0 (29810000)|EXCEPTION_THROWN|[15]|System.NullPointerException: Attempt to de-reference a null object
```

#### ログの解析ポイント
1. タイムスタンプ: `43.0 (29810000)`
2. イベントタイプ: `EXCEPTION_THROWN`, `USER_DEBUG` など
3. 行番号: `[5]`, `[10]`, `[15]`
4. エラーの種類と詳細情報

#### デバッグログの設定最適化
```apex
// デバッグレベルの詳細設定例
LoggingLevel debugLevel = LoggingLevel.FINEST;
System.debug(debugLevel, '詳細なデバッグ情報');

// パフォーマンス分析用のログ
System.debug(LoggingLevel.INFO, 'Process Start: ' + DateTime.now());
// 処理
System.debug(LoggingLevel.INFO, 'Process End: ' + DateTime.now());
```
## まとめ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/cb4b9334-21a1-f2bb-074f-e9a161ad33c9.png)

Apexコンソールは、Salesforceの開発において非常に強力なツールです。System.debug()を使ったデバッグ、SOQLによるデータ取得、デバッグログの確認、そしてエラー処理の基本を理解することで、より効率的な開発が可能になります。

## 参考リンク
- [Apex Developer Guide](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/)
- [SOQL and SOSL Reference](https://developer.salesforce.com/docs/atlas.ja-jp.soql_sosl.meta/soql_sosl/)
- [Debug Logs](https://help.salesforce.com/articleView?id=code_debug_log.htm)