Skip to content

Commit e0f4531

Browse files
pgajek20ptaq0
andauthored
SOQL Cache (#140)
* SOQL Cache [Draft] (#137) * Cannot modify a collection while it is being iterated Fix (#133) * Cannot modify a collection while it is being iterated Fix Signed-off-by: Piotr PG Gajek <[email protected]> * Code refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * groupByWithDefaultFieldsAndAggregateFunction unit test Signed-off-by: Piotr PG Gajek <[email protected]> --------- Signed-off-by: Piotr PG Gajek <[email protected]> * Cache Init Signed-off-by: Piotr PG Gajek <[email protected]> * draft Signed-off-by: Piotr PG Gajek <[email protected]> * draft Signed-off-by: Piotr PG Gajek <[email protected]> * Cache Concept Signed-off-by: Piotr PG Gajek <[email protected]> * Cache SOQL Examples Signed-off-by: Piotr PG Gajek <[email protected]> * Cache refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * caching draft Signed-off-by: Piotr PG Gajek <[email protected]> * final draft Signed-off-by: Piotr PG Gajek <[email protected]> * SQOL Cache Signed-off-by: Piotr PG Gajek <[email protected]> * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * SOQLCache Signed-off-by: Piotr PG Gajek <[email protected]> * SOQLCache_Test Signed-off-by: Piotr PG Gajek <[email protected]> * SOQLCache_Test Signed-off-by: Piotr PG Gajek <[email protected]> * SOQLCache_Test Signed-off-by: Piotr PG Gajek <[email protected]> * docs placeholder Signed-off-by: Piotr PG Gajek <[email protected]> * Documentation Signed-off-by: Piotr PG Gajek <[email protected]> * handlefiltering Signed-off-by: Piotr PG Gajek <[email protected]> * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * Additional methods Signed-off-by: Piotr PG Gajek <[email protected]> * cache mocking Signed-off-by: Piotr PG Gajek <[email protected]> * Examples Signed-off-by: Piotr PG Gajek <[email protected]> * stucture changes Signed-off-by: Piotr PG Gajek <[email protected]> * Fix unit tests Signed-off-by: Piotr PG Gajek <[email protected]> * Unit Test Fix Signed-off-by: Piotr PG Gajek <[email protected]> * Cache Fixes Signed-off-by: Piotr PG Gajek <[email protected]> * SOQLCache_Test Signed-off-by: Piotr PG Gajek <[email protected]> * toObjectWithMultipleRows Signed-off-by: Piotr PG Gajek <[email protected]> * Default Sharing for cached selector Signed-off-by: Piotr PG Gajek <[email protected]> * SOQLCache_Test Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * website update Signed-off-by: Piotr PG Gajek <[email protected]> * mermaid Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * toId() (#139) Signed-off-by: Piotr PG Gajek <[email protected]> * soql cache Signed-off-by: Piotr PG Gajek <[email protected]> * Documentation Signed-off-by: Piotr PG Gajek <[email protected]> * Fix Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * SOQL Cache Test Signed-off-by: Piotr PG Gajek <[email protected]> * SOQLCacheTest Signed-off-by: Piotr PG Gajek <[email protected]> * PMD Signed-off-by: Piotr PG Gajek <[email protected]> * Refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * License Signed-off-by: Piotr PG Gajek <[email protected]> * Fix Unit Tests Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * documentation update Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * documentation 3.7.0 Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * mermaid Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> --------- Signed-off-by: Piotr PG Gajek <[email protected]> * SOQLCache_Test Fix Signed-off-by: Piotr PG Gajek <[email protected]> * fixed failing Platform Cache Signed-off-by: Maciej Ptak <[email protected]> --------- Signed-off-by: Piotr PG Gajek <[email protected]> Signed-off-by: Maciej Ptak <[email protected]> Co-authored-by: Maciej Ptak <[email protected]>
1 parent 1c59f1e commit e0f4531

File tree

81 files changed

+17731
-17258
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+17731
-17258
lines changed

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2023 BeyondTheCloud.Dev
3+
Copyright (c) 2025 BeyondTheCloud.Dev
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ Read about the features in the [documentation](https://soql-lib.vercel.app/docs/
9797
6. **Avoid query duplicates**
9898
7. **The default configuration for all queries**
9999
8. **Dynamic conditions**
100+
9. **Cache records**
100101

101102
----
102103

@@ -107,4 +108,4 @@ Read about the features in the [documentation](https://soql-lib.vercel.app/docs/
107108
## License notes
108109

109110
- For proper license management each repository should contain LICENSE file similar to this one.
110-
- Each original class should contain copyright mark: Copyright (c) 2023 BeyondTheCloud.Dev
111+
- Each original class should contain copyright mark: Copyright (c) 2025 Beyond The Cloud Sp. z o.o. (BeyondTheCloud.Dev)

config/project-scratch-def.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"edition": "Developer",
44
"features": [
55
"EnableSetPasswordInApi",
6-
"PersonAccounts"
6+
"PersonAccounts",
7+
"PlatformCache"
78
],
89
"settings": {
910
"lightningExperienceSettings": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<PlatformCachePartition xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<isDefaultPartition>false</isDefaultPartition>
4+
<masterLabel>SOQL</masterLabel>
5+
<platformCachePartitionTypes>
6+
<allocatedCapacity>1</allocatedCapacity>
7+
<allocatedPartnerCapacity>0</allocatedPartnerCapacity>
8+
<allocatedPurchasedCapacity>0</allocatedPurchasedCapacity>
9+
<allocatedTrialCapacity>0</allocatedTrialCapacity>
10+
<cacheType>Organization</cacheType>
11+
</platformCachePartitionTypes>
12+
<platformCachePartitionTypes>
13+
<allocatedCapacity>1</allocatedCapacity>
14+
<allocatedPartnerCapacity>0</allocatedPartnerCapacity>
15+
<allocatedPurchasedCapacity>0</allocatedPurchasedCapacity>
16+
<allocatedTrialCapacity>0</allocatedTrialCapacity>
17+
<cacheType>Session</cacheType>
18+
</platformCachePartitionTypes>
19+
</PlatformCachePartition>

force-app/main/default/classes/example/ExampleController.cls force-app/main/default/classes/examples/ExampleController.cls

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
public with sharing class ExampleController {
2-
32
@AuraEnabled
43
public static List<Contact> getContactsByRecordType(String recordType) {
54
return SOQL_Contact.query()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
public with sharing class SOQL_BusinessHoursCache extends SOQLCache implements SOQLCache.Selector {
2+
public static SOQL_BusinessHoursCache query() {
3+
return new SOQL_BusinessHoursCache();
4+
}
5+
6+
private SOQL_BusinessHoursCache() {
7+
super(BusinessHours.SObjectType);
8+
cacheInOrgCache();
9+
with(BusinessHours.Id, BusinessHours.Name);
10+
}
11+
12+
public override SOQL.Queryable initialQuery() {
13+
return SOQL.of(BusinessHours.SObjectType).whereAre(SOQL.Filter.with(BusinessHours.IsActive).isTrue());
14+
}
15+
16+
public SOQL_BusinessHoursCache byName(String name) {
17+
whereEqual(BusinessHours.Name, name);
18+
return this;
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
public with sharing class SOQL_OrgWideEmailAddressCache extends SOQLCache implements SOQLCache.Selector {
2+
public static SOQL_OrgWideEmailAddressCache query() {
3+
return new SOQL_OrgWideEmailAddressCache();
4+
}
5+
6+
private SOQL_OrgWideEmailAddressCache() {
7+
super(OrgWideEmailAddress.SObjectType);
8+
with(OrgWideEmailAddress.Id, OrgWideEmailAddress.DisplayName, OrgWideEmailAddress.Address);
9+
}
10+
11+
public override SOQL.Queryable initialQuery() {
12+
return SOQL.of(OrgWideEmailAddress.SObjectType);
13+
}
14+
15+
public SOQL_OrgWideEmailAddressCache byAddress(String address) {
16+
whereEqual(OrgWideEmailAddress.Address, address);
17+
return this;
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
public with sharing class SOQL_ProfileCache extends SOQLCache implements SOQLCache.Selector {
2+
public static SOQL_ProfileCache query() {
3+
return new SOQL_ProfileCache();
4+
}
5+
6+
private SOQL_ProfileCache() {
7+
super(Profile.SObjectType);
8+
cacheInOrgCache();
9+
with(Profile.Id, Profile.Name, Profile.UserType);
10+
}
11+
12+
public override SOQL.Queryable initialQuery() {
13+
return SOQL.of(Profile.SObjectType);
14+
}
15+
16+
public SOQL_ProfileCache byName(String name) {
17+
whereEqual(Profile.Name, name);
18+
return this;
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<apiVersion>62.0</apiVersion>
4+
<status>Active</status>
5+
</ApexClass>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
public with sharing class SOQL_UserCache extends SOQLCache implements SOQLCache.Selector {
2+
public static SOQL_UserCache query() {
3+
return new SOQL_UserCache();
4+
}
5+
6+
private SOQL_UserCache() {
7+
super(User.SObjectType);
8+
cacheInSessionCache();
9+
with(User.Id, User.Username, User.Name, User.Country);
10+
}
11+
12+
public override SOQL.Queryable initialQuery() {
13+
return SOQL.of(User.SObjectType).whereAre(SOQL.Filter.id().equal(UserInfo.getUserId()));
14+
}
15+
16+
public SOQL_UserCache byUsername(String username) {
17+
whereEqual(User.Username, username);
18+
return this;
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<apiVersion>62.0</apiVersion>
4+
<status>Active</status>
5+
</ApexClass>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* Copyright (c) 2025 Beyond The Cloud Sp. z o.o. (BeyondTheCloud.Dev)
3+
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/cache-manager/blob/main/LICENSE)
4+
*
5+
* PMD False Positives:
6+
* - CognitiveComplexity: It is a library and we tried to put everything into ONE class
7+
* - PropertyNamingConventions: It was intentional to make the lib more fluent and readable
8+
* - FieldNamingConventions: It was intentional to make the lib more fluent and readable
9+
**/
10+
@SuppressWarnings('PMD.CognitiveComplexity, PMD.PropertyNamingConventions, PMD.FieldNamingConventions')
11+
public with sharing class CacheManager {
12+
public interface Cacheable {
13+
Boolean contains(String key);
14+
Set<String> getKeys();
15+
Object get(String key);
16+
void put(String key, Object value);
17+
void remove(String key);
18+
}
19+
20+
public final static Cacheable ApexTransaction = new ApexTransactionCache();
21+
22+
public final static Cacheable SOQLOrgCache {
23+
get { return getOrgCache('SOQL'); }
24+
}
25+
26+
public final static Cacheable SOQLSessionCache {
27+
get { return getSessionCache('SOQL'); }
28+
}
29+
30+
// Implementation
31+
32+
private enum CacheType { ORG, SESSION }
33+
34+
private final static Map<CacheType, Map<String, Cacheable>> CACHE_MAP = new Map<CacheType, Map<String, Cacheable>>{
35+
CacheType.ORG => new Map<String, Cacheable>(),
36+
CacheType.SESSION => new Map<String, Cacheable>()
37+
};
38+
39+
private static Cacheable getOrgCache(String partitionName) {
40+
if (!CACHE_MAP.get(CacheType.ORG).containsKey(partitionName)) {
41+
CACHE_MAP.get(CacheType.ORG).put(partitionName, new OrgPlatformCache(partitionName));
42+
}
43+
return CACHE_MAP.get(CacheType.ORG).get(partitionName);
44+
}
45+
46+
private static Cacheable getSessionCache(String partitionName) {
47+
if (!CACHE_MAP.get(CacheType.SESSION).containsKey(partitionName)) {
48+
CACHE_MAP.get(CacheType.SESSION).put(partitionName, new SessionPlatformCache(partitionName));
49+
}
50+
return CACHE_MAP.get(CacheType.SESSION).get(partitionName);
51+
}
52+
53+
private static void validateKey(String key) {
54+
if (!Pattern.compile('^[a-zA-Z0-9]+$').matcher(key).matches()) {
55+
throw new IllegalArgumentException('Key must be alphanumeric, received key: ' + key);
56+
}
57+
}
58+
59+
private class ApexTransactionCache implements Cacheable {
60+
private final Map<String, Object> TRANSACTION_CACHE = new Map<String, Object>();
61+
62+
public Boolean contains(String key) {
63+
return this.TRANSACTION_CACHE.containsKey(key);
64+
}
65+
66+
public Set<String> getKeys() {
67+
return TRANSACTION_CACHE.keySet();
68+
}
69+
70+
public Object get(String key) {
71+
return this.TRANSACTION_CACHE.get(key);
72+
}
73+
74+
public void put(String key, Object value) {
75+
validateKey(key);
76+
this.TRANSACTION_CACHE.put(key, value);
77+
}
78+
79+
public void remove(String key) {
80+
this.TRANSACTION_CACHE.remove(key);
81+
}
82+
}
83+
84+
private abstract class PlatformCache implements Cacheable {
85+
private Cache.Partition platformCachePartition;
86+
87+
public PlatformCache(String partitionName) {
88+
this.platformCachePartition = getPartition(partitionName);
89+
}
90+
91+
protected abstract Cache.Partition getPartition(String partitionName);
92+
93+
public Boolean contains(String key) {
94+
return this.platformCachePartition.contains(key);
95+
}
96+
97+
public Set<String> getKeys() {
98+
return this.platformCachePartition.getKeys();
99+
}
100+
101+
public Object get(String key) {
102+
return this.platformCachePartition.get(key);
103+
}
104+
105+
public void remove(String key) {
106+
this.platformCachePartition.remove(key);
107+
}
108+
109+
public void put(String key, Object value) {
110+
validateKey(key);
111+
this.platformCachePartition.put(key, value);
112+
}
113+
}
114+
115+
private class OrgPlatformCache extends PlatformCache {
116+
public OrgPlatformCache(String partitionName) {
117+
super(partitionName);
118+
}
119+
120+
public override Cache.Partition getPartition(String partitionName) {
121+
return Cache.Org.getPartition(partitionName);
122+
}
123+
}
124+
125+
private class SessionPlatformCache extends PlatformCache {
126+
public SessionPlatformCache(String partitionName) {
127+
super(partitionName);
128+
}
129+
130+
public override Cache.Partition getPartition(String partitionName) {
131+
return Cache.Session.getPartition(partitionName);
132+
}
133+
}
134+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<apiVersion>62.0</apiVersion>
4+
<status>Active</status>
5+
</ApexClass>

0 commit comments

Comments
 (0)