- Apex Code 문법
- 변수 타입
- Data 지정 방법
- 제어문
- Loops
- Exception
- Control - Class
- Class 정의
- Class 상속
- Apex Properties
- Parameterized Interface
- Annotations
- 내장 변수
- Control - Testing Apex
- Test Class 사례
- System.assert
- Test 실행 방법
- Control - Trigger
- Trigger 개요
- Triggers and Bulk Triggers
- Trigger Sample 1
- Trigger Test Class Sample 1
- Trigger Sample 2
- Trigger Test Class Sample 2
- Control - Scheduler
- Control - Batch Apex
- Database.Batchable Interface
- Database.AllowsCallouts Interface
- Database.Stateful Interface
- Database.BatchableContext
- Database
- Batch Apex 사례
- Batch Job 모니터링
- Batch 제약 사항
- Control - Web Service
- Control - HTTP
- Control - RemoteAction
- Control - Email Service
- Custom Setting
- Sandbox
- Deploy
- Force.com 내장 개체
- System
- 암호화
- 인코딩
- Logging
- Dynamic Apex
- sObject 정보 획득
- Dynamic SOQL
- Dynamic Visualforce
- Managed Sharing
- JSONObject
- Apex Code 개발
- Request
- Cookie
- Session
- Config
- 참고 문헌
- 지원 업체
Force.com중 Apex Code를 정리 합니다.
Apex Code 문법
변수 타입
{|cellspacing="0" cellpadding="2" border="1" width="100%" bgcolor="#FFFFFF" align="center"
|-
|width="20%" align="center" valign="middle" style="background-color:#eee;"|String
|width="80%"|
문자열
선언
String item = new String();
String item = '~';
제공 함수
Integer length()
Boolean equals(String), Boolean equalsIgnoreCase(String), Integer compareTo(String)
Boolean startsWith(String), Boolean contains(String), Boolean endsWith(String)
Integer indexOf(String), Integer indexOf(String, Integer), Integer lastIndexOf(String)
String trim()
String replace(String, String), String replaceAll(String, String), String replaceFirst(String, String)
String substring(Integer), String substring(Integer, Integer)
List
split(String), List split(String, Integer) String toLowerCase(), String toLowerCase(String)
String toUpperCase(), String toUpperCase(String)
void addError(APEX_OBJECT 또는 String)
변환
Integer
Integer tmpInteger = Integer.valueOf(tmpStr);
String tmpStr = String.valufOf(tmpInteger);
Boolean
Boolean tmpBoolean = (tmpStr == 'true') ? true:false;
String tmpStr = (tmpBoolean) ? 'true':'false';
Date
Date tmpDate = Date.valufOf(tmpStr + ' 00:00:00');
tmpStr : yyyy-MM-dd
String tmpStr = tmpDate.year() + '-' + tmpDate.month() + '-' + tmpDate.day();
Datetime
Datetime tmpDatetime = Date.valufOf(tmpStr);
tmpStr : yyyy-MM-dd HH:mm:ss
String tmpStr = tmpDatetime.year() + '-' + tmpDatetime.month() + '-' + tmpDatetime.day() + ' ' + tmpDatetime.hour() + ':' + tmpDatetime.minute() + ':' + tmpDatetime.second();
Blob
Blob tmpBlob = Blob.valueOf(tmpStr);
String tmpStr = tmpBlob.toString(); align="center" valign="middle" style="background-color:#eee;"
선언
제공 함수
Integer size()
사용법
data[0], data[1], data2, ...
변환
Integer : 32-bits 정수, Long : 64-bits 정수
Integer = Integer.valueOf(String)
Long long = ~L; align="center" valign="middle" style="background-color:#eee;" Decimal : 32-bits 실수, Double : 64-bits 실수 align="center" valign="middle" style="background-color:#eee;" Boolean : true, false, null align="center" valign="middle" style="background-color:#eee;" Date : 날자, Datetime : 날자 + 시간
Date baseDate = Date.parse('2011. 12. 5');
Date var20111101 = baseDate.addMonths(-1).toStartOfMonth();
Date var20111130 = baseDate.toStartOfMonth().addDays(-1); align="center" valign="middle" style="background-color:#eee;" ID : 18 문자의 레코드 아이디 align="center" valign="middle" style="background-color:#eee;" - align="center" valign="middle" style="background-color:#eee;" 개체
선언
Account item = new Account();
Account item = new Account(name1 = 'value1', name2 = 'value2');
Account[] data = Query_문;
사용
item.~ : Field 값
item.getSObjectType() == Account.sObjectType : 'Account'라는 object type을 반환 align="center" valign="middle" style="background-color:#eee;" - align="center" valign="middle" style="background-color:#eee;" - align="center" valign="middle" style="background-color:#eee;"
선언
List
data = new List (); List
data = new List {val1, val2, val3}; List
data = new type0; List
data = new List (다른_List);
제공 함수
get(0), set(0, ~), clear();
add(~), remove(0), size(), isEmpty() align="center" valign="middle" style="background-color:#eee;"
선언
Set
data = new Set (); Set
data = new Set {val1, val2, val3}; Set
data = new Set (다른_Set);
제공 함수
clear(), isEmpty(), size()
add(data), contains(data), remove(data) align="center" valign="middle" style="background-color:#eee;"
선언
Map<keyType, valueType> name = new Set<keyType, valueType>();
Map<keyType, valueType> name = new Set<keyType, valueType> {key1 => val1, key2 => val2};
Map<ID, Contact> m = new Map<ID, Contact>(id, lastname from contact);
제공 함수
containsKey(key), keySet(), values(), clear(), isEmpty()
get(key), put(key, value), remove(key) align="center" valign="middle" style="background-color:#eee;"
선언
public enum Season {WINTER, SPRING, SUMMER, FALL}
사용법
Season e = Season.WINTER;
제공 함수
name(), ordinal(), values()
System-defined enums
System.StatusCode, System.XmlTag, System.LoggingLevel, System.RoundingMode
System.SoapType, System.DisplayType, ApexPages.Severity, Dom.XmlNodeType
|}
Primitive Data Types
Boolean : true, false
Date
Datetime
Double
ID : 18 Character Apex record identifier
Integer
String
배열 : [](.md) 로 표시
SObject Type, SObject Fields
Lists
List zzStr = new List();
zzStr.set(0, "tmpStr");
zzStr.get(0);
zzStr.clear();
Sets
Set zzStr = new Set();
zzStr.add("tmpStr");
zzStr.remove(1);
Maps
Map zzStr = new Map();
zzStr.put("zzName", "zzValue");
zzStr.get("zzName");
Not Support DML Statements
User, Profile, Territory, RecordType, Transaction,
WebLink, BusinessHours, BusinessProcess, CategoryNode, ProcessUserInfo
Currency
Integer, Double, Boolean, String, Datetime, Date, Math
List, Set, Map, SObject, Exception
Data 지정 방법
POJO (Plan Old Java Object)
제어문
if (~) {
} else {
}
Loops
continue, break
do {
statement;
break;
continue;
} while (Boolean_condition);
while (Boolean_condition) {
statement;
}
for (initialization; Boolean_exit_condition; increment) {
statement;
}
for (variable : array_or_set) {
statement;
}
for (variable : [inline_soql_query](inline_soql_query.md)) {
statement;
}
Exception
try {
throw ~;
} catch (Exception ex) {
//--- System.ArrayException, DmlException, MathException, NullPointerException
//--- QueryException, StringException, TypeException
} finally {
}
Control - Class
Class 정의
"설정 -> App 설정 -> 개발 -> Apex 클래스" 메뉴
public | private | global
[| abstract | with sharing | without sharing](virtual) class | interface ClassName
[implements InterfaceNameList] [ClassName](extends) {
[public | private | protected | global] [static] [final](final.md) String var = value;
[public | private | protected | global] [override] [static](static.md) String func() { }
}
with sharing
<- user’s permissions, field-level security, sharing rules
소유자 (사용자)
역할
공유 설정
프로필
Field Level Security
Transient String name; //--- view state에 저장되지 않음
Class 상속
virtual -> extend or override, abstract -> override
this, super
public virtual class Parent {
public virtual String getStrParent() {
return ‘Parent String’;
}
}
public class Child extends Parent {
public Child() {
super();
}
public override String getStrParent() {
return super.getStrParent() + ‘ : Child String’;
}
}
public virtual class Parent {
public String strParent = null;
public Parent() {
}
public void Func01() {
}
public String getStrParent() {
return strParent;
}
}
public class Child extends Parent {
public Child() {
super(); //--- Parent의 생성자를 호출 합니다.
//--- TODO : 여기에 코딩
}
//--- Parent의 Method를 재설정 합니다.
public override void Func01() {
getStrParent();
}
}
Interface 상속
public virtual interface Parent {
}
public interface Child extends Parent {
}
상속 확인
A instanceof B
Apex Properties
Apex Properties 정의
[public | private | protected | global] [virtual, abstract, overrid](static,)
String var { [public | private | protected] get; [~](~.md) set;}
Apex Properties
set 함수에서 value는 System이 생성하여 전달하는 인수값 입니다.
public class BasicClass {
public String varName {
get {
return varName;
}
set {
name = value;
}
}
}
Parameterized Interface
public virtual interface Pair {
public T getFirest();
public void setFirst(T val);
}
public StringPair implements Pair {
}
Annotations
@deprecated :
@future : 비동기적으로 실행되는 Method, 200 호출 / user, 24시간
@isTest : 테스트 클래스 표시
@ReadOnly : in Web services, Schedulable interface
@RemoteAction : JavaScript에서 함수 호출
내장 변수
{!$ObjectType.Account} === Schema.SObjectType.Account.getKeyPrefix()
{!$ObjectType.Account.fields.Name.label}
Object, sObject
NS__Object__c, NS__Field__c / NS.Class.Method()
Iterable, Iterators -> Database.batchable
Control - Testing Apex
Apex Code로 프로그램을 작성하면 이를 배포하기 위해서는 전체 코딩된 라인중 75% 이상이 테스트 되어야 합니다. (Code Coverage Result가 75% 이상) Force.com에서 제안하는 테스트 방식을 살펴보면 해당 코드가 한번 이상 수행이 되면 테스트가 된 것으로 처리를 하고 있습니다. 따라서 Code Coverage Result를 높이기만을 원한다면 다양한 테스트 코드를 작성할 필요는 없고 각각의 라인이 한번 이상 실행이 되도록 테스트 코드를 작성하면 됩니다.
Code Coverage Result를 높이는 방법
분기문, 제어문 등에서 각각의 코드 블럭이 실행될 수 있도록 데이터를 구성 합니다. (추천)
테스트가 완료된 코드 블럭에 의미없는 코드를 추가하여 라인수를 늘입니다. (비추천)
Test Class 사례
ClassName 클래스를 테스트하기 위한 Test 클래스 샘플
ClassName의 모든 라인이 수행될 수 있도록 Test 클래스를 작성하여야 함
배포 등을 위해서는 전체 라인중 75%가 테스트(Code Coverage Result)가 되어야 함
@IsTest
private class ClassNameTest {
private static testMethod void testMain() {
ClassName test = null;
//--- Test를 위한 사용자 설정
User user = [id from User where alias='auser'](select);
System.RunAs(u1) {
//--- Test를 위한 데이터 설정
manage = new Manage();
//--- Test 코드 작성
test = new ClassName();
test.setManage(manage);
System.assert(actual==expected, 'Character.isAscii(\'' + charactr + '\') returned ' + actual);
System.assertEquals(singletotalMiles, totalMiles);
Test.startTest(); //--- Limits를 초기화하고 테스트 시작
~
Test.stopTest();
}
}
}
Apex Batch 테스트 프로그램
@IsTest
private class BatEvaluationTest {
private static testmethod void testMain() {
BatEvaluation test = null;
List scope = null;
Test.StartTest();
//--- Apex Batch에 전달할 scope 데이터 생성
scope = new List();
scope.add(new Evaluation__c());
//--- Apex Batch의 각 Method를 별도로 실행
test = new BatEvaluation();
test.start(null);
test.execute(null, scope);
test.finish(null);
Test.stopTest();
}
}
System.assert
System.assert(boolean) : boolean 값이 true이면 OK
System.assert(boolean, e.getMessage()) : boolean 값이 true이면 OK, boolean 값이 false이면 두번째 인자를 메시지로 표시
System.assertEquals(dataA, dataB msg) : dataA와 dataB의 값이 동일하면 OK
System.assertNotEquals(dataA, dataB msg) : dataA와 dataB의 값이 다르면 OK
System.assert(BooleanExceptionMessage);
System.assert(false);
System.assert('a' == 'A');System.assertEquals(expectedValue, actualValueExceptionMessage);
System.assertEquals('Hello to you!', sayHelloWorld('to you!'));System.assertNotEquals(expectedValue, actualValueExceptionMessage);
Test 실행 방법
"설정 -> App 설정 -> 개발 -> Apex 클래스 -> 모든 테스트 실행"에서 테스트
"설정 -> App 설정 -> 개발 -> Apex 테스트 실행"에서 테스트
Eclipse에서 Class에서 오른쪽 마우스를 눌러 "Force.com -> Run Tests" 메뉴를 실행 합니다.
Control - Trigger
Trigger 개요
Trigger 종류
insert : before/after insert
update : before/after update
delete : before/after delete
upsert : before/after insert/update
merge : before/after delete, before update
undelete : after undelete (Account, Asset, Campaign, Case, Contract, Custom objects, Event, Lead, Opportunity, Product, Solution, Task)
Trigger 적용 예외
Cascading delete, Mass ~, …
Opportunity : amoutn, ForecastCategory, isWon, …
트리거 메뉴
"설정 -> App 설정 -> 사용자 정의 -> '개체' -> 트리거" 메뉴
"설정 -> App 설정 -> 작성 -> 개체 -> 트리거 '새로 만들기'"
Trigger 변수
Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap, Trigger.size
Trigger
isBefore, isAfter, isInsert, isUpdate, isDelete, isUndelete
public Integer size = Trigger.size;
public PaymentType__c[](.md) oldData = Trigger.old;
public PaymentType__c[](.md) newData = Trigger.new;
Trigger.old[idx], Trigger.new[idx](idx.md)
Trigger 사례
trigger StandardTrigger on Account (before insert, before update, before delete,
after insert, after update, after delete) {
//--- @future, 비동기 WebService
//--- sObject.addError('~');
//--- Trigger.size, oldMap, newMap, old (ReadOnly), new
if (Trigger.isInsert && Trigger.isBefore) {
for (Account item : Trigger.old) {
}
for (Account item : Trigger.new) {
//--- 수정 가능
}
}
if (Trigger.isInsert && Trigger.isAfter) {
for (Account item : Trigger.old) {
}
for (Account item : Trigger.new) {
}
}
}
Triggers and Bulk Triggers
- Bulk Triggers
Data import, Bulk Apex API calls, Mass actions
Recursive Apex Code methods and triggers that invoke bulk DML statements
trigger on bulk () {
//--- trigger_event : before insert, before update, before delete, after ~
//--- isInsert, isUpdate, isDelete, isUndelete, isBefore, isAfter
//--- Trigger.new, Trigger.newMap, Trigger.old, Trigger.oldMap, Trigger.size
//--- Trigger.oldMap.get(q.opportunity__c).addError('Cannot delete opportunity with a quote');
//--- Trigger.new[i](i.md).Primary__c.addError('Primary quote cannot be marked non-primary');
try {
Dictionary__c obj = new Dictionary__c(Name='Dictionary deploy test');
insert obj;
} catch(DmlException e) {
System.assert(e.getMessage().contains('first error: FIELD_CUSTOM_VALIDATION_EXCEPTION,'), e.getMessage());
}
} //--- 최대 32,000 characters
trigger helloWorldAccountTrigger on Account (before insert)
{
//--- before insert, before update, after insert, after update, after delete
//--- Trigger.isBefore
Account[](.md) accs = Trigger.new;
MyHelloWorld.addHelloWorld(accs);
Contact c = new Contact(lastName = 'Weissman');
c.accountId = a.Id;
insert c;
List aa = [id, name from account where name = 'Acme'](select);
c = [account.name from contact where id = :c.id](select);
c.account.name = 'salesforce.com';
c.lastName = 'Roth';
update c;
update c.account;
upsert
delete
delete
System.assertEquals('xxx', a.accountNumber);
}
Savepoint sp = Database.setSavepoint();
Database.rollback(sp);
throw ;
try {
} catch () {
}
Trigger Sample 1
/**
* 프로그램 명 : DictionaryTrigger.trigger Trigger
* 프로그램 설명 : Dictionary 개체용 Trigger 샘플
* 작성자 : 산사랑
* 작성일 : 2008.06.19 ~ 2008.06.19
*
* Copyright (c) 2000-2008 pnuskgh, All rights reserved.
*/
trigger DictionaryTrigger on Dictionary__c (before insert, before update, before delete)
{
Double tmpNum = 0.0;
if (Trigger.isBefore) {
if (Trigger.isInsert) {
for (Dictionary__c obj:Trigger.new) {
obj.num__c = 1;
}
}
if (Trigger.isUpdate) {
for (Dictionary__c obj:Trigger.new) {
if (obj.num__c == 11) {
obj.num__c = obj.num__c + 100;
} else {
obj.num__c = obj.num__c + 10;
}
if (150 < obj.num__c) {
obj.addError('Error : You can\'t update this record.');
obj.num__c.addError('Error : You can\'t update this record.');
}
}
}
if (Trigger.isDelete) {
for (Dictionary__c obj:Trigger.old) {
tmpNum = obj.num__c;
}
}
}
}
Trigger Test Class Sample 1
/**
* 프로그램 명 : DictionaryDeployClass Class
* 프로그램 설명 : DictionaryTrigger Trigger를 테스트하는 클래스
* 작성자 : 산사랑
* 작성일 : 2008.06.19 ~ 2008.06.19
*
* Copyright (c) 2000-2008 pnuskgh, All rights reserved.
*/
public class DictionaryDeployClass {
public static testmethod void DictionaryDeployTest()
{
Double tmpNum = 0.0;
System.debug('Start insert trigger test.');
Dictionary__c obj = new Dictionary__c(Name='Dictionary deploy test');
insert obj;
obj = [select Id, Name, num__c
from Dictionary__c
where Id = :obj.Id];
System.assertEquals(1, obj.num__c);
System.debug('Start update trigger test.');
tmpNum = obj.num__c + 10;
update obj;
obj = [select Id, Name, num__c
from Dictionary__c
where Id = :obj.Id];
System.assertEquals(tmpNum, obj.num__c);
try {
System.debug('Start delete trigger test.');
delete obj;
obj = [select Id, Name, num__c
from Dictionary__c
where Id = :obj.Id];
} catch(QueryException e) {
System.debug(e.getMessage());
System.assert(e.getMessage().contains('List has no rows for assignment to SObject'), e.getMessage());
}
}
}
Trigger Sample 2
/**
* 프로그램 명 : OpportunityTrigger.trigger Trigger
* 프로그램 설명 : Forecast 데이터만 영업기회에서 분리하여 관리
* 작성자 : 산사랑
* 작성일 : 2008.06.20 ~ 2008.06.20
*
* Copyright (c) 2000-2008 pnuskgh, All rights reserved.
*/
trigger OpportunityTrigger on Opportunity (before insert, after insert, before update, before delete)
{
if (Trigger.isAfter) {
if (Trigger.isInsert) {
for (Opportunity opp:Trigger.new) {
Forecast__c obj = new Forecast__c(OwnerId = opp.OwnerId, Name = opp.Name,
Amount__c = opp.Amount__c, GP__c = opp.GP__c,
Probability__c = opp.Probability, opportunity__c = opp.Id);
insert obj;
}
}
}
if (Trigger.isBefore) {
if (Trigger.isUpdate) {
for (Opportunity opp:Trigger.new) {
Forecast__c obj = [select Id, OwnerId, Name, Amount__c, GP__c, Probability__c, opportunity__c
from Forecast__c
where opportunity__c = :opp.Id];
obj.OwnerId = opp.OwnerId;
obj.Name = opp.Name;
obj.Amount__c = opp.Amount__c;
obj.GP__c = opp.GP__c;
obj.Probability__c = opp.Probability;
update obj;
}
}
if (Trigger.isDelete) {
for (Opportunity opp:Trigger.old) {
Forecast__c obj = [select Id, OwnerId, Name, Amount__c, GP__c, Probability__c, opportunity__c
from Forecast__c
where opportunity__c = :opp.Id];
delete obj;
}
}
}
}
Trigger Test Class Sample 2
/**
* 프로그램 명 : OpportunityDeployClass Class
* 프로그램 설명 : OpportunityTrigger Trigger를 테스트하는 클래스
* 작성자 : 산사랑
* 작성일 : 2008.06.20 ~ 2008.06.20
*
* Copyright (c) 2000-2008 pnuskgh, All rights reserved.
*/
public class OpportunityDeployClass {
public static testmethod void OpportunityDeployTest()
{
Boolean flagError = false;
Forecast__c obj = Null;
System.debug('Start insert trigger test.');
Opportunity opp = new Opportunity(Name = 'Deploy test', Amount__c = 100, GP__c = 20,
StageName = '수주확신(90%)', CloseDate = System.today());
insert opp;
opp = [select Id, OwnerId, Name, Amount__c, GP__c, Probability
from Opportunity
where Id = :opp.Id];
obj = [select Id, OwnerId, Name, Amount__c, GP__c, Probability__c, opportunity__c
from Forecast__c
where opportunity__c = :opp.Id];
System.assertEquals(opp.OwnerId, obj.OwnerId);
System.assertEquals(opp.Name, obj.Name);
System.assertEquals(opp.Amount__c, obj.Amount__c);
System.assertEquals(opp.GP__c, obj.GP__c);
System.assertEquals(opp.Probability, obj.Probability__c);
System.assertEquals(opp.Id, obj.opportunity__c);
System.debug('Start update trigger test.');
opp.GP__c = 30;
update opp;
obj = [select Id, OwnerId, Name, Amount__c, GP__c, Probability__c, opportunity__c
from Forecast__c
where opportunity__c = :opp.Id];
System.assertEquals(opp.OwnerId, obj.OwnerId);
System.assertEquals(opp.Name, obj.Name);
System.assertEquals(opp.Amount__c, obj.Amount__c);
System.assertEquals(opp.GP__c, obj.GP__c);
System.assertEquals(30, obj.GP__c);
System.assertEquals(opp.Probability, obj.Probability__c);
System.assertEquals(opp.Id, obj.opportunity__c);
try {
System.debug('Start delete trigger test.');
delete opp;
obj = [select Id, OwnerId, Name, Amount__c, GP__c, Probability__c, opportunity__c
from Forecast__c
where opportunity__c = :opp.Id];
} catch(QueryException e) {
if (e.getMessage().contains('List has no rows for assignment to SObject')) {
flagError = true;
} else {
throw e;
}
}
System.assert(flagError);
}
}
Control - Scheduler
"설정 -> App 설정 -> 개발 -> Apex 클래스 -> Apex 예약" 메뉴
"설정 -> 관리 설정 -> 모니터링 -> 예약된 작업" 메뉴에서 모니터링
Scheduler 생성
global class StandardScheduler implements Schedulable {
global void execute (SchedulableContext sc) {
Batchable batch = null;
//--- sc.getTriggerId() CronTrigger 개체
batch = new Batchable();
Database.executebatch(batch);
}
}
Scheduler 사용법
초(0-59) 분(0-59) 시(0-23) 일(1-31) � 월(1-12) 주(1-7, 1.일요일) 년(1970-2099)
String sch = ‘20 30 8 10 2 ? * ‘;
String id = system.schedule(‘name’, sch, StadnardScheduler);
Control - Batch Apex
Database.Batchable Interface
Apex Code에서 Batch 프로그램을 작성하려면 Database.Batchable Interface를 구현 하여야 합니다.
global (Database.QueryLocator | Iterable
) start(Database.BatchableContext bc) {} execute Method에서 처리할 레코드를 수집하여 반환 합니다.
global void execute(Database.BatchableContext BC, list<P>) {}
실제로 Batch 처리가 이루어지는 method 입니다.
Default로 200개의 records씩 전달되어 실행이 되며 각 execute은 별개의 Transaction으로 처리가 됩니다.
global void finish(Database.BatchableContext BC) {}
Batch 처리가 모두 완료된 후에 호출되는 method 입니다.
Database.AllowsCallouts Interface
Apex Batch에서 HTTP request를 호출하는 프로그램을 작성하려면 Database.AllowsCallouts Interface를 implement 하여야 합니다.
Database.Stateful Interface
Apex Batch 처리를 위한 Class에서 선언한 변수의 값을 계속 유지하려면 Database.Stateful Interface를 implement 하여야 합니다.
Database.BatchableContext
getJobId() : 해당 Apex Batch Job의 ID를 반환 합니다.
Database
executeBatch Method : Apex Batch를 실행 합니다.
ID batchId = null;
BatSession batch = null;
batch = new BatSession();
batchId = Database.executeBatch(batch);
//batchId = Database.executeBatch(batch, 200); //--- 200. 한번에 처리하는 레코드 갯수
Batch Apex 사례
Batch Apex 선언
Database.AllowsCallouts Interface : HTTP 호출시 implement
Database.Stateful : Class에서 선언한 변수 값을 유지하려면 implement
global class BatAccount implements Database.Batchable, Database.Stateful {
global Database.QueryLocator start(Database.BatchableContext ctx) {
return Database.getQueryLocator([Id, Name FROM Account](SELECT));
}
global void execute(Database.BatchableContext ctx, List scope) {
for (Account item : scope) {
}
}
global void finish(Database.BatchableContext ctx) {
ID id = ctx.getJobID(); //--- AsyncApexJob 개체
}
}
Batch Apex 사례
global class BatSession implements Database.Batchable, Database.Stateful {
global Database.QueryLocator start(Database.BatchableContext ctx) {
Datetime tmpDate = null;
tmpDate = Datetime.now();
tmpDate.addDays(-3);
return Database.getQueryLocator([SELECT Id
FROM Session__c
WHERE CreatedDate < :tmpDate]);
}
global void execute(Database.BatchableContext ctx, List scope) {
delete scope;
}
global void finish(Database.BatchableContext ctx) {
AsyncApexJob job = null;
AsyncApexJob job = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email
FROM AsyncApexJob
WHERE Id = :ctx.getJobId()];
}
}
Batch Job 모니터링
AsyncApexJob : Batch 진행 사항을 저장
"설정 -> 관리 설정 -> 모니터링 -> Apex 작업" 메뉴에서 모니터링
[Apex 작업](Salesforce_-_관리 설정.md#Apex 작업.md)
Batch 제약 사항
start method에서 QueryLocator를 사용할 경우 최대 5000만 레코드만 처리가 가능 합니다.
start method에서 Iterable을 사용할 경우 최대 레코드 제약은 그대로 적용이 됩니다.
Apex governor limits are reset for each execution of execute
동시에 최대 5개의 Batch Job만 처리가 가능 합니다.
A user can have up to 5 query cursors open at a time
Apex query cursors, Batch cursors, Visualforce cursors는 각각 최대 5개까지 동시에 사용이 가능 합니다.
Aggregate Query는 Batch Apex에서 start 함수의 결과로써 사용할 수 없습니다.
Control - Web Service
Web Services 생성 in Apex Code
global class StandardWebServices {
webService static String getTitle(String title, Account argAccount) {
return '[+ title + '](')';
}
}
Web Services 호출 in Visualforce Page
var account = sforce.sObject("Account"); //--- id, type, name
var title = sforce.apex.execute(“StandardWebServices”, “getTitle”,
{title:”~”, argAccount:account});
sforce.debug.trace = true;
Control - HTTP
Http http = null;
HttpRequest req = null;
HttpResponse res = null;
String strHeader = null;
String contentType = null, response = null;
try {
req = new HttpRequest();
req.setEndpoint('http://www.apexdevnet.com/');
req.setMethod('GET');
req.setHeader(‘Content-Type’, ‘application/soap+xml’);
req.setTimeout(60000);
req.setBody(‘name1=value1&name2=value2’);
req.setCompressed(false);
//--- Basic Authencation
strHeader = EncodingUtil.base64Endoce(Blob.valueOf(userid + ‘:’ + pwd))
req.setHeader(‘Authorization’, ‘BASIC ‘ + strHeader);
http = new Http();
res = http.send(req);
if (res.getStatusCode() == 404) {
} else {
contentType = resp.getHeader('Content-Type');
response = resp.getHeader('Content-Length');
this.theXMLDom = new xmldom(res.getBody());
dom.document doc = res.getBodyDocument();
dom.XmlNode [](.md) node = doc.getRootElement().getChildElements();
//--- Atachement로 저장
Attachment attach = new Attachment();
attach.Name = “~’;
attach.ContentType = “~”;
attach.body = Blob.valueOf(res.getBody());
attach.ParenetId = ~;
insert attach;
}
} catch (System.CalloutException e) {
this.theXMLDom = null;
}
Control - RemoteAction
RemoteAction은 Visualforce Page에서 JavaScript를 사용하여 Controller의 Method를 호출하는 방법 입니다.
Apex Code
global class MyJSController {
public String accountName { get; set; }
public static Account account { get; set; }
@RemoteAction
global static Account getAccount(String accountName) {
account = [
SELECT id, name, phone
FROM Account
WHERE name = :accountName ];
return account;
}
}
Visualforce Page
var accountNameJS = null;
accountNameJS = "오픈소스 비즈니스 컨설팅";
MyJSController.getAccount(
accountNameJS, //--- 전달되는 인자
function(result, event) { //--- result : 반환 값
if (event.status) {
//--- 정상 처리
//--- 반환된 값은 result.name
//--- 반환된 배열 result[2](2.md).name
} else if (event.type === 'exception') {
//--- Exception 오류 처리
} else {
//--- 오류 처리
}
},
{escape:true}
);
참고 문헌
Control - Email Service
Single Mail 발송
Messaging.SingleEmailMessage email = null;
Messaging.EmailFileAttachment file = null;
Messaging.SendEmailResult[](.md) result = null;
email = new Messaging.SingleEmailMessage();
email.setSubject(String);
email.setToAddress(String[](.md));
email.setPlainTextBody(String);
file = new Messaging.EmailFileAttatchment();
file.setFileName(‘~’);
file.setBody(Blob);
email.setFileAttachments(new Messaging.EmailFileAttachment[](.md) {file});
result = Messaging.sendEmail(new Messaging.SingleEmailMessag[](.md) {email});
Custom Setting
Setting__c.getValues(Name).Value__c
Setting__c : Custom Setting 개체명
Name : 레코드의 Name
Value__c : 레코드의 Value__c라는 필드의 필드값
Sandbox
Sandbox 명이 de일 경우, Sandbox의 로그인 아이디는 userid.de 입니다.
Deploy
Eclipse에서 deploy
클래스를 선택한 후 오른쪽 마우스를 누릅니다.
"Force.com -> Deploy to Server..." 메뉴를 선택하여 deploy 합니다.
Setup에서 deploy
운영의 "설정 -> App 설정 -> 배치 -> 연결 배포"에서 "인바운드 변경 허용"을 선택 합니다.
Sandbox의 "설정 -> App 설정 -> 배치 -> 아웃바운드 변경 세트"에서 변경세트를 작성하고 업로드 합니다.
운영의 "설정 -> App 설정 -> 배치 -> 인바운드 변경 세트"에서 업로드된 변경 세트를 배포 합니다.
Deploy의 제약 사항
Class와 Trigger를 75% 이상 테스트 되어야 합니다.
하나의 class가 deploy 되더라도 모든 class에서 검증이 들어 갑니다. (Source Coverage의 평균이 75% 이상이 되어야 합니다.)
Test Class는 실제 운영 서비스에 있는 테스트 클래스가 실행 되므로 먼저 테스트 클래스부터 deploy 해야 합니다.
Force.com 내장 개체
System
Debug
System.debug(Message);
System.debug(logLevel, Message);
:ERROR, WARN, INFO, DEBUG, FINE, FINER, FINEST
Date and Time
System.today() : 오늘 날자를 반환한다.
System.now() : 오늘 날자와 시간을 반환한다.
암호화
암호화 key 생성
Blob key = Crypto.generateAesKey(256)
암호화
String 암호문 = EncodingUtil.base64Encode(Crypto.encryptWithManagedIV('AES256', key, Blob.valueOf('평문')));
복호화
String 평문 = Crypto.decryptWithManagedIV('AES256', key, EncodingUtil.base64Decode('암호문')).toString();
해시키 생성
'MD5' + 해시키 = Crypto.generateDigest('MD5', '해시키로 변환할 문자열');
String과 Blob간 변환
Blob에 문자열이 저장되어 있을 경우
String strBlob = dataBlob.toString();
Blob dataBlob = Blob.valufOf(strBlob);
Blob에 바이너리가 저장되어 있을 경우
String strBlob = EncodingUtil.base64Encode(dataBlob);
Blob dataBlob = EncodingUtil.base64Decode(strBlob);
인코딩
인코딩 : EncodingUtil.base64Encode(~)
디코딩 : EncodingUtil.base64Decode(~)
URL 인코딩 : EncodingUtil.urlEncode(~, “UTF-8”)
URL 디코딩 : EncodingUtil.urlDecode(~, “UTF-8”)
Logging
System Logging Levels (System.LoggingLevel enum)
ERROR
WARN
INFO
DEBUG
FINE
FINER
FINEST
로그 남기기
System.debug('MsgTxt'); //--- Default Loggin Level인 Logginglevel.DEBUG 가 적용됨)
System.debug(Logginglevel.INFO, 'MsgTxt');Apex Code별 로그 레벨 설정
Apex 클래스 : "설정 -> App 설정 -> 개발 -> Apex 클래스" 메뉴에서 원하는 클래스를 선택한 후 "로그 필터" 탭에서 설정
Apex 트리거 : "설정 -> App 설정 -> 개발 -> Apex 트리거" 메뉴에서 원하는 트리거를 선택한 후 "로그 필터" 탭에서 설정
로그 확인
"시스템 로그" 메뉴에서 확인
"설정 -> 관리 설정 -> 모니터링 -> 디버그 로그"에서 확인
Dynamic Apex
sObject 정보 획득
{|cellspacing="0" cellpadding="2" border="1" width="100%" bgcolor="#FFFFFF" align="center"
|-
|width="25%" align="center" valign="middle" style="background-color:#eee;"|sObject
|width="75%" valign="top"|
sObject obj = new Custom__c();
Map<String, Schema.SObjectType> objAll = Schema.getGlobalDescribe();
표준 개체는 key에 namespace가 붙지 않습니다.
사용자 정의 개체는 namespace가 다를 경우, key에 "NS__" 와 같은 형태의 namespace가 앞에 붙습니다.
Object value = obj.get('name');
obj.put('name', 'value');
obj.put(field, 'value');
sObject objParent = obj.getSObject('ParentCustom__c');
SELECT Id, ParentCustom__c FROM Custom__c LIMIT 1
sObject objChild = obj.getSObjects('ChildCustom__c');
SELECT Id, (SELECT Name FROM ChildCustom__c LIMIT 1) FROM Custom__c align="center" valign="middle" style="background-color:#eee;" valign="top"
Schema.SObjectType objType = obj.getSObjectType();
Schema.SObjectType objType = Custom__c.sObjectType;
Schema.SObjectType objType = objDesc.getSObjectType();
Custom__c obj = (Custom__c)objType.newSObject(); align="center" valign="middle" style="background-color:#eee;" valign="top" Schema.DescribeSObjectResult objDesc = objType.getDescribe();
Schema.DescribeSObjectResult objDesc = Custom__c.sObjectType.getDescribe();
Schema.DescribeSObjectResult objDesc = Schema.SObjectType.Custom__c;
String objDesc.getName()
String objDesc.getLabel()
String objDesc.getKeyPrefix()
List<Schema.ChildRelationship> objDesc.getChildRelationships() align="center" valign="middle" style="background-color:#eee;" valign="top" Schema.SObjectField field = fieldDesc.getSObjectField();
Schema.SObjectField field = Custom__c.Field__c;
Map<String, Schema.SObjectField> fieldAll = Schema.SObjectType.Custom__c.fields.getMap();
표준 필드는 key에 namespace가 붙지 않습니다.
사용자 정의 필드는 namespace가 다를 경우, key에 "NS__" 와 같은 형태의 namespace가 앞에 붙습니다.
참고 문헌
Field Types
|-
|align="center" valign="middle" style="background-color:#eee;"|Schema.DescribeFieldResult
| valign="top"|
Schema.DescribeFieldResult fieldDesc = field.getDescribe();
Schema.DescribeFieldResult fieldDesc = Custom__c.Field__c.getDescribe();
Schema.DescribeFieldResult fieldDesc = Schema.sObjectType.Custom__c.fields.Field__c;
String fieldDesc.getLabel();
String fieldDesc.getName();
Schema.DisplayType fieldDesc.getType();
List <Schema.PicklistEntry> fieldDesc.getPicklistValues();
Object fieldDesc.getDefaultValue();
Schema.SOAPType Enum
String, Integer, Double, Boolean, Date, Datetime, Time
ID, anytype, base64binary
제공 함수
Integer getByteLength()
String getCalculatedFormula()
Schema.SObjectField getController()
Object getDefaultValue()
String getDefaultValueFormula()
Integer getDigits()
String getInlineHelpText()
String getLable()
Integer getLength
String getLocalName()
String getName()
List<Schema.PicklistEntry> getPicklistValues()
Integer getPrecision()
List<Schema.SObjectType> getReferenctTo()
String getRelationshipName()
Integer getRelationShipOrder()
Integer getScale()
Schema.SoapType getSoapType()
Schema.SObjectField getSobjectField()
Schema.DisplayType getType()
Boolean isAccessible()
Boolean isAutoNumber()
Boolean isCalculated()
Boolean isCaseSEnsitive()
Boolean isCreateable()
Boolean isCustom()
Boolean isDefaultedOnCreate()
Boolean isDependentPicklist()
Boolean isDeprecatedAndHidden()
Boolean isExternalId()
Boolean isFilterable()
Boolean isHtmlFormatted()
Boolean isIdLookup()
Boolean isNameField()
Boolean isNamePointing()
Boolean isNillable()
Boolean isRestrictedPicklist()
Boolean isSortable()
Boolean isUnique()
Boolean isUpdateable()
Boolean isWriteRequiresMasterRead()
|}
picklist entry
제약 사항
최대 100개의 fields 멤버를 사용할 수 있습니다.
Dynamic SOQL
동적으로 데이터 읽기
sObject item = Database.query(string_limit_1);
List data = Database.query(string);
동적으로 데이터 처리하는 샘플
List data = null;
Map columns = null;
data = Database.query(strQuery);
columns = data.getSObjectType().getDescribe().fields.getMap();
for (sObject item : data) {
for (String name : columns.keySet()) {
string value = null;
value = String.valueOf(item.get(name));
}
}
Dynamic Visualforce
변수 :
.,[‘’](‘~’.md)Map<String, Schema.SobjectField> = Schema.SobjectType.Account.fields.getMap();
Schema.SobjectField
getDescribe().isAccessible(), isCustom()
Component.NameSpace.~
Component.Apex.OutputText
{!$ObjectType.Account.FieldSets.~}
Field : Label, Type, Required, FieldPath, DBRequired
sObject.put(
, ~), get()StandardController
StandardSetController
reset(), addFields(List
)
Include
<apex:include pageName=“~” />
<apex:includeScript value=“{$Resource.~}” />
<apex:stylesheet value=“{$Resource.~}” />�
<apex:iframe src=“
” height=“” width=“~” scrolling=“true” frameborder=“false” />
Managed Sharing
공유 권한 : 소유자 -> 역할 -> 공유 설정 / 필드 접근성
User Managed Sharing
Access Level : None. Private, Read. Read Only, Edit. Read/Write, All. Full Access
개체명__Share
Modify All Data" 권한이 있어야 사용 가능
public class JobSharing {
public Boolean manualShare(ID recordId, ID userOrGroup) {
Job__Share share = new Job__Share();
share.ParentId = recordId;
share.UserOrGroup = userOrGroup; //--- Job.Recruiter__c
share.AccessLevel = ‘Read’;
share.RowCause = Schema.Job__Share.RowCause.Manual;
//--- 공유 이유가 Recruite일 경우 (사용자 정의 공유 이유)
//--- share.RowCause = Schema.Job__Share.RowCause.Recruite__c;
Database.insert(share);
}
}
JSONObject
apex-library의 JSONObject를 사용하여 JSON 데이터를 처리할 수 있습니다.
JSONObject Class
생성자 : JSONObject(String source)
Key 확인
Boolean has(String key)
SET keys()
데이터를 저장하는 함수
JSONObject putOpt(String key, JSONObject.value value)
데이터 읽어 오는 함수
JSONObject.value getValue(String key)
Object get(String key)
Object opt(String key) //--- Default는 null을 반환
String getString(String key)
//--- Integer.valueOf(string), Date.valueOf(string) 등을 사용하여 다른 type으로 변환 가능
Boolean getBoolean(String key)
Boolean optBoolean(String key) //--- Default는 false
Boolean optBoolean(String key, Boolean defaultValue)
String valueToString()
데이터 읽어 오는 함수의 응용
JSONObject snbJson = new JSONObject(strJson);
for (JSONObject.value jsonItem : snbJson.getValue('data').values) {
JSONObject item = null;
item = new JSONObject(jsonItem.valueToString());
}
JSONObject.value Class의 속성값
JSONObject obj
String str
Integer num
Double dnum
Boolean bool
List values
Apex Code 개발
Request
Apex Code에서
ApexPages.currentPage().getParameters().get('id');
VisualForce Page에서
{!$CurrentPage.parameters.cid}
Cookie
Cookie는 일반적으로 도메인을 기준으로 설정이 됩니다. 세일즈포스닷컴은 CRM 서비스를 제공하는 URL과 Visualforce의 URL이 다르므로 서로 다른 도메인의 URL을 가지게 됩니다.
세일즈포스닷컴의 URL 종류
Saleforce.com의 URL 예) https://na7.salesforce.com/home/home.jsp
Visualforce의 URL 예) https://c.na7.visual.force.com/apex/DeptTree
Apex Code에서 Cookie 사용 방법
Apex Code에서 저장한 Cookie는 이름의 앞에 "apex__"가 자동으로 붙어서 처리가 됩니다.
public class UtilCookie {
public static String getCookie(String name) {
Cookie cookie = null;
cookie = ApexPages.currentPage().getCookies().get(name);
if (cookie == null) {
return null;
} else {
return cookie.getValue();
}
}
//--- 저장되는 Cookie명은 "apex__" + name 입니다.
public static void setCookie(String name, String value) {
Cookie cookie = null;
cookie = new Cookie(name, value, null, -1, false);
ApexPages.currentPage().setCookies(new Cookie[](.md){ cookie });
}
}
JavaScript에서 Cookie 사용 방법
Session
사용자 세션을 활용하여 세션 정보를 관리하는 모듈을 작성 합니다.
Session의 종류
사용자 세션 (Apex Code 세션)
UserInfo.getSessionID();
API용 세션 (AJAX Toolkit 세션)
{!$Api.Session_ID}
var __sfdcSessionId = "{!GETSESSIONID()}";
//--- sforce.connection.sessionId 에 저장되어 사용됨
Salesforce 서비스의 세션
도메인이 달라 다른 세션이 생성 됩니다.
Session__c 개체
DaoSession
public class DaoSession {
public String sessionId {get; set;}
public DaoSession(String argSessionId) {
sessionId = argSessionId;
}
public String findData(String argName) {
Session__c session = null;
session = [SELECT Id, Name, SessionId__c, Name__c, Value__c, LastModifiedDate
FROM Session__c
WHERE SessionId__c = :sessionId
AND Name__c = :argName
LIMIT 1];
return session.Value__c;
}
public void insertData(String argName, String argValue) {
Session__c session = null;
session = new Session__c();
session.SessionId__c = sessionId;
session.Name__c= argName;
session.Value__c = argValue;
insert session;
}
public void updateData(String argName, String argValue) {
Session__c session = null;
session = [SELECT Id, Name, SessionId__c, Name__c, Value__c, LastModifiedDate
FROM Session__c
WHERE SessionId__c = :sessionId
AND Name__c = :argName
LIMIT 1];
session.Value__c = argValue;
update session;
}
public Void upsertData(String argName, String argValue) {
Session__c session = null;
try {
session = [SELECT Id, Name, SessionId__c, Name__c, Value__c, LastModifiedDate
FROM Session__c
WHERE SessionId__c = :sessionId
AND Name__c = :argName
LIMIT 1];
} catch (Exception ex) {
}
if (session == null) {
session = new Session__c();
session.SessionId__c = sessionId;
session.Name__c= argName;
session.Value__c = argValue;
insert session;
} else {
session.Value__c = argValue;
update session;
}
}
public Void deleteData(String argName) {
Session__c session = null;
session = [SELECT Id, Name, SessionId__c, Name__c, Value__c, LastModifiedDate
FROM Session__c
WHERE SessionId__c = :sessionId
AND Name__c = :argName
LIMIT 1];
delete session;
}
}
MgrSession
public class MgrSession {
public DaoSession dao = null;
public MgrSession() {
dao = new DaoSession(UserInfo.getSessionId());
}
public String getSession(String argName) {
return dao.findData(argName);
}
public Void setSession(String argName, String argValue) {
dao.upsertData(argName, argValue);
}
}
SchSession
"App 설정 -> 개발 -> Apex 클래스" 메뉴에서 "Apex 예약"에서 등록
"관리 설정 -> 모니터링 -> 예약된 작업"에서 등록된 작업을 조회
global class SchSession implements Schedulable {
global void execute(SchedulableContext sc) {
BatSession batch = null;
batch = new BatSession();
Database.executeBatch(batch);
}
}
BatSession
global class BatSession implements Database.Batchable {
global Database.QueryLocator start(Database.BatchableContext ctx) {
Datetime tmpDate = null;
tmpDate = Datetime.now();
tmpDate.addDays(-3);
return Database.getQueryLocator([SELECT Id
FROM Session__c
WHERE CreatedDate < :tmpDate]);
}
global void execute(Database.BatchableContext ctx, List scope) {
delete scope;
}
global void finish(Database.BatchableContext ctx) {
}
}
Config
ConfigSetting__c Custom Settings
MgrConfig
public class MgrConfig {
public MgrConfig() {
}
public String getConfig(String argName) {
ConfigSetting__c config = null;
config = ConfigSetting__c.getValues(argName);
return config.Value__c;
}
}
Config__c 개체
DaoConfig
public class DaoConfig {
public static String CATEGORY = 'global';
public DaoConfig() {
}
public String findData(String argCategory, String argName) {
Config__c config = null;
config = [SELECT Id, Name, Category__c, Name__c, Value__c
FROM Config__c
WHERE Category__c = :argCategory
AND Name__c = :argName
LIMIT 1];
return config.Value__c;
}
public String findData(String argName) {
return findData(CATEGORY, argName);
}
public void insertData(String argCategory, String argName, String argValue) {
Config__c config = null;
config = new Config__c();
config.Category__c = argCategory;
config.Name__c = argName;
config.Value__c = argValue;
insert config;
}
public void insertData(String argName, String argValue) {
insertData(CATEGORY, argName, argValue);
}
public void updateData(String argCategory, String argName, String argValue) {
Config__c config = null;
config = [SELECT Id, Name, Category__c, Name__c, Value__c
FROM Config__c
WHERE Category__c = :argCategory
AND Name__c = :argName
LIMIT 1];
config.Value__c = argValue;
update config;
}
public void updateData(String argName, String argValue) {
updateData(CATEGORY, argName, argValue);
}
public Void upsertData(String argCategory, String argName, String argValue) {
Config__c config = null;
try {
config = [SELECT Id, Name, Category__c, Name__c, Value__c
FROM Config__c
WHERE Category__c = :argCategory
AND Name__c = :argName
LIMIT 1];
} catch (Exception ex) {
}
if (config == null) {
config = new Config__c();
config.Category__c= argCategory;
config.Name__c= argName;
config.Value__c = argValue;
insert config;
} else {
config.Value__c = argValue;
update config;
}
}
public Void upsertData(String argName, String argValue) {
upsertData(CATEGORY, argName, argValue);
}
public Void deleteData(String argCategory, String argName) {
Config__c config = null;
config = [SELECT Id, Name, Category__c, Name__c, Value__c
FROM Config__c
WHERE Category__c = :argCategory
AND Name__c = :argName
LIMIT 1];
delete config;
}
public Void deleteData(String argName) {
deleteData(CATEGORY, argName);
}
}
MgrConfig
public class MgrConfig {
public DaoConfig dao = null;
public MgrConfig() {
dao = new DaoConfig();
}
public String getConfig(String argCategory, String argName) {
return dao.findData(argCategory, argName);
}
public String getConfig(String argName) {
return dao.findData(argName);
}
public Void setConfig(String argCategory, String argName, String argValue) {
dao.upsertData(argCategory, argName, argValue);
}
public Void setConfig(String argName, String argValue) {
dao.upsertData(argName, argValue);
}
}
참고 문헌
지원 업체
{{지원업체}}
[[Category:Salesforce|Category:Salesforce]]
[[Category:Cloud|Category:Cloud]]
분류: CRM