Обычно для выбора вещей мы используем код SOQL, например:

[ 
    SELECT Id, Blah1__c, Blah2__c, Blah3__c
    FROM Yada_Yada_Yada__c
    WHERE Id IN :idSet
];

Когда мы это сделаем, компилятор (и наши IDE) сообщит нам, если переменные, поля или объекты как-то неверны.

Для реализации Batchable мы хотели бы использовать Database.QueryLocator.

Однако соответствующее выражение для получения Database.QueryLocator выглядит так:

Database.getQueryLocator(
    'SELECT Id, Blah1__c, Blah2__c, Blah3__c '
    + ' FROM Yada_Yada_Yada__c '
    + ' WHERE Id IN :idSet '
);

Теперь нет ничего безопасного для типов. Мой компилятор не предупредит меня, если я назову что-то не так. Моя IDE не поможет мне, если я позже переименую "idSet". SFDC не помешает никому переименовать или удалить поля SObject или SObject.

Очевидно, я мог бы использовать запрос SOQL, чтобы получить список, а затем создать итерируемый объект, но это вернуло бы нежелательное ограничение регулятора.

Есть ли безопасный способ получить Database.QueryLocator?

8
Brian Kessler 17 Июл 2019 в 18:00

3 ответа

Лучший ответ

Предоставить жестко заданные поля в локаторе запросов? Я считаю, что вы можете это сделать, это дает проверку во время компиляции

Database.getQueryLocator([SELECT Id FROM Account]);

Источник:https://developer.salesforce .com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_database_batch.htm#apex_Database_QueryLocator_getQuery

7
Pranay Jaiswal 17 Июл 2019 в 19:07

Частично. Вы всегда можете установить статические ссылки на задействованные поля и объекты sObject и динамически построить запрос с помощью шаблонов. В любом случае мне никогда не нравилось видеть запросы, построенные путем конкатенации строк.

List<String> fieldList = new List<String>{
    String.valueOf(Account.Id),
    String.valueOf(Account.Name)
};

String query = String.format(
    'SELECT {0} FROM {1}',
    new List<String> {
        String.join(fieldList, ', '),
        String.valueOf(Account.sobjectType)
    }
);
System.debug(query);
Database.query(query);

11:31:26:004 USER_DEBUG [13]|DEBUG|ВЫБЕРИТЕ идентификатор, имя из учетной записи

Это, по крайней мере, включает в себя проверку зависимости метаданных системы. Конечно, становится намного грязнее, если вы хотите пройти через отношения.

Однако вы не сможете проверять ссылки на имена в привязках Apex, потому что вы не можете проанализировать локальное пространство имен в Apex. В лучшем случае вы могли бы включить его в метод построителя, но уязвимость, связанная с изменением, скажем, имен параметров на метод построителя, все еще существует.

5
David Reed 17 Июл 2019 в 18:35

У @PranayJaiswal есть лучший ответ, но вы также можете рассмотреть корпоративные шаблоны Force.com — уровень селектора, который использует queryFactory и ввод безопасных полей, например:

Database.QueryLocator ql = CasesSelector.newInstance().selectByCloseDateAsQueryLocator(someDateFilter);


public virtual class CasesSelector extends fflib_SObjectSelector implements ICasesSelector {
  public List<Schema.SObjectField> getSobjectFieldList() {
    return new List<Schema.SObjectField> {
      Case.Id,
      Case.CloseDate,
      Case.Subject,
      ...
    };
  }
 public virtual Database.QueryLocator selectByCloseDateAsQueryLocator(String dateFilter) {
  fflib_QueryFactory qF = new QueryFactory(false) // false=enumerate fields you want 
                           .selectFields(new Set<SObjectField> {
                              Case.Id, Case.CloseDate, ...}  
                           .setCondition('CloseDate = :dateFilter');
  return Database.getQueryLocator(qf.toSOQL());

}

public virtual Database.QueryLocator selectByCloseDateAsQueryLocator(String dateFilter) {
  fflib_QueryFactory qF = new QueryFactory(true) // use fields defined by class method getSobjectFieldList  
                           .setCondition('CloseDate = :dateFilter');
  return Database.getQueryLocator(qf.toSOQL());

}

Тем не менее, шаблон селектора fflib не поддерживает выражения setCondition с использованием Schema.SObjectField (аргументом для setCondition является строка), но вы можете сделать:

.setCondition(Case.CloseDate.getDescribe.getName() + ' = :dateFilter)

Если вы очень беспокоились о typesafe здесь.

Хорошая вещь в шаблоне заключается в том, что queryfactory позволяет легко составлять запросы из нескольких фабрик (чтобы вы могли создавать поиски и дочерние элементы), вы получаете все биты описания бесплатно, и, что мне больше всего нравится, селекторы легко мокабельны (ApexMocks или другие DI подходы), делая модульные тесты очень быстрыми.

Я не показываю всю эту ерунду, так как это более широкая тема — см. блог/книгу Trailhead и Эндрю Фосетта

3
cropredy 17 Июл 2019 в 21:07