SOQL Cache
Apex Classes: SOQLCache.cls
and SOQLCache_Test.cls
.
The lib cache main class necessary to create cached selectors.
SOQLCache.of(Profile.SObjectType)
.with(Profile.Id, Profile.Name, Profile.UserType)
.whereEqual(Profile.Name, 'System Administrator')
.toObject();
Methods
The following are methods for using SOQLCache
:
with(SObjectField field)
with(SObjectField field1, SObjectField field2)
with(SObjectField field1, SObjectField field2, SObjectField field3)
with(SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4)
with(SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4, SObjectField field5)
with(List<SObjectField> fields)
with(String fields)
with(String relationshipName, SObjectField field)
with(String relationshipName, SObjectField field1, SObjectField field2)
with(String relationshipName, SObjectField field1, SObjectField field2, SObjectField field3)
with(String relationshipName, SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4)
with(String relationshipName, SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4, SObjectField field5)
with(String relationshipName, Iterable<SObjectField> fields)
CACHE STORAGE
cacheInApexTransaction
Queried records are stored in Apex cache (static variable) only for one Apex Transaction.
Very useful when data doesn't change often during one transaction like User
.
Apex Transaction is the default cache storage when none is specified.
Signature
Cacheable cacheInApexTransaction();
Example
public with sharing class SOQL_ProfileCache extends SOQLCache implements SOQLCache.Selector {
public static SOQL_ProfileCache query() {
return new SOQL_ProfileCache();
}
private SOQL_ProfileCache() {
super(Profile.SObjectType);
cacheInApexTransaction(); // <=== Cache in Apex Transaction
}
public override SOQL.Queryable initialQuery() {
return SOQL.of(Profile.SObjectType).systemMode().withoutSharing();
}
}
cacheInOrgCache
Signature
Cacheable cacheInOrgCache();
Example
public with sharing class SOQL_ProfileCache extends SOQLCache implements SOQLCache.Selector {
public static SOQL_ProfileCache query() {
return new SOQL_ProfileCache();
}
private SOQL_ProfileCache() {
super(Profile.SObjectType);
cacheInOrgCache(); // <=== Cache in Org Cache
}
public override SOQL.Queryable initialQuery() {
return SOQL.of(Profile.SObjectType).systemMode().withoutSharing();
}
}
cacheInSessionCache
Signature
Cacheable cacheInSessionCache();
Example
public with sharing class SOQL_ProfileCache extends SOQLCache implements SOQLCache.Selector {
public static SOQL_ProfileCache query() {
return new SOQL_ProfileCache();
}
private SOQL_ProfileCache() {
super(Profile.SObjectType);
cacheInSessionCache(); // <=== Cache in Session Cache
}
public override SOQL.Queryable initialQuery() {
return SOQL.of(Profile.SObjectType).systemMode().withoutSharing();
}
}
CACHE EXPIRATION
maxHoursWithoutRefresh
Default: 48 hours
All cached records have an additional field called cachedDate
. To avoid using outdated records, you can add maxHoursWithoutRefresh
to your query. This will check how old the cached record is and, if it’s too old, execute a query to update the record in the cache.
Signature
Cacheable maxHoursWithoutRefresh(Integer hours)
Example
SOQLCache.of(Profile.SObjectType)
.with(Profile.Id, Profile.Name, Profile.UserType)
.whereEqual(Profile.Name, 'System Administrator')
.maxHoursWithoutRefresh(12)
.toObject();
removeFromCache
The removeFromCache
method allows clearing records from the cache, triggering an automatic refresh the next time the query is executed.
Signature
Cacheable removeFromCache(List<SObject> records)
Example
trigger SomeObjectTrigger on SomeObject (after update, after delete) {
SOQLCache.removeFromCache(Trigger.new);
}
CACHE OPTIONS
allowFilteringByNonUniqueFields
By default, cached queries can only filter by unique fields (Id, Name, DeveloperName, or fields marked as unique in the schema). This method allows filtering by non-unique fields.
Signature
Cacheable allowFilteringByNonUniqueFields()
Example
SOQLCache.of(Profile.SObjectType)
.with(Profile.Id, Profile.Name, Profile.UserType)
.allowFilteringByNonUniqueFields()
.whereEqual(Profile.UserType, 'Standard')
.toObject();
allowQueryWithoutConditions
By default, cached queries require at least one condition. This method allows queries without any WHERE conditions.
Signature
Cacheable allowQueryWithoutConditions()
Example
public with sharing class SOQL_ProfileCache extends SOQLCache implements SOQLCache.Selector {
public static SOQL_ProfileCache query() {
return new SOQL_ProfileCache();
}
private SOQL_ProfileCache() {
super(Profile.SObjectType);
cacheInOrgCache();
allowQueryWithoutConditions();
with(Profile.Id, Profile.Name, Profile.UserType);
}
public override SOQL.Queryable initialQuery() {
return SOQL.of(Profile.SObjectType).systemMode().withoutSharing();
}
public List<Profile> getAllProfiles() {
// This would require implementing toList() method in SOQLCache
// For now, this demonstrates the concept of allowing queries without conditions
return new List<Profile>();
}
}
INITIAL QUERY
The initial query enables bulk population of records in the cache (if it is empty), ensuring that every subsequent query in the cached selector will use the cached records.
For instance:
public with sharing class SOQL_ProfileCache extends SOQLCache implements SOQLCache.Selector {
public static SOQL_ProfileCache query() {
return new SOQL_ProfileCache();
}
private SOQL_ProfileCache() {
super(Profile.SObjectType);
cacheInOrgCache();
with(Profile.Id, Profile.Name, Profile.UserType)
}
public override SOQL.Queryable initialQuery() { // <=== Initial query
return SOQL.of(Profile.SObjectType).systemMode().withoutSharing();
}
public SOQL_ProfileCache byName(String name) {
whereEqual(Profile.Name, name);
return this;
}
}
When the cache is empty, the initialQuery
will be executed to populate the data in the cache. This allows SOQL_ProfileCache.query().byName('System Administrator').toObject();
to retrieve the profile from the already cached records, instead of fetching records individually.
initialQuery
Signature
SOQL.Queryable initialQuery()
Example
public with sharing class SOQL_ProfileCache extends SOQLCache implements SOQLCache.Selector {
public static SOQL_ProfileCache query() {
return new SOQL_ProfileCache();
}
private SOQL_ProfileCache() {
super(Profile.SObjectType);
cacheInOrgCache();
with(Profile.Id, Profile.Name, Profile.UserType)
}
public override SOQL.Queryable initialQuery() { // <=== Initial query
return SOQL.of(Profile.SObjectType).systemMode().withoutSharing();
}
}
SELECT
All selected fields are going to be cached.
with field1 - field5
Signature
Cacheable with(SObjectField field)
Cacheable with(SObjectField field1, SObjectField field2);
Cacheable with(SObjectField field1, SObjectField field2, SObjectField field3);
Cacheable with(SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4);
Cacheable with(SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4, SObjectField field5);
Example
SOQLCache.of(Profile.SObjectType)
.with(Profile.Id, Profile.Name, Profile.UserType)
.whereEqual(Profile.Name, 'System Administrator')
.toObject();
SOQL.of(Profile.SObjectType)
.with(Profile.Id)
.with(Profile.Name)
.whereEqual(Profile.Name, 'System Administrator')
.toObject();
with fields
Use for more than 5 fields.
Signature
Cacheable with(List<SObjectField> fields)
Example
SOQLCache.of(Profile.SObjectType)
.with(new List<SObjectField>{ Profile.Id, Profile.Name, Profile.UserType })
.whereEqual(Profile.Name, 'System Administrator')
.toObject();
with string fields
NOTE! With String Apex does not create reference to field. Use SObjectField
whenever it possible. Method below should be only use for dynamic queries.
Signature
Cacheable with(String fields)
Example
SOQLCache.of(Profile.SObjectType)
.with('Id, Name, UserType')
.whereEqual(Profile.Name, 'System Administrator')
.toObject();
with relationship field
Signature
Cacheable with(String relationshipName, SObjectField field)
Example
SOQLCache.of(Account.SObjectType)
.with(Account.Id, Account.Name)
.with('Owner', User.Name)
.whereEqual(Account.Id, '001000000000000AAA')
.toObject();