Generating unique identifiers is a common requirement in software development. In Salesforce, Globally Unique Identifiers (GUIDs) or Universally Unique Identifiers (UUIDs) are often used to uniquely identify records or other entities. This article explores different methods of generating GUIDs/UUIDs in Salesforce Apex code, focusing on best practices and considerations for security and compliance.
A GUID (Globally Unique Identifier) and a UUID (Universally Unique Identifier) are essentially the same thing: a 128-bit number used to identify information in computer systems. These identifiers are designed to be unique across space and time, meaning that the probability of generating the same UUID twice is extremely low.
A typical GUID/UUID is represented as a string of hexadecimal digits, often grouped as follows:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Example:
13219ec0-3a81-44c5-a300-de14b7d0235f
Several approaches can be used to generate GUIDs/UUIDs in Salesforce Apex. Let's examine some of these, along with their pros and cons.
UUID
Class (Spring '24 and Later)Since the Spring '24 release, Salesforce provides a built-in UUID
class, which simplifies the process:
UUID uuidV4 = UUID.randomUUID(); // Returns a UUID instance, having a random UUID v4 ID.
String uuidV4Str = uuidV4.toString(); // Converts the UUID instance to its String representation
System.debug(uuidV4Str); // Example: 92a43945-23ca-4039-8450-7bfb6ade156a
Crypto.GenerateAESKey()
and EncodingUtil.ConvertToHex()
This method leverages Salesforce's cryptography capabilities:
Blob b = Crypto.GenerateAESKey(128);
String h = EncodingUtil.ConvertToHex(b);
String guid = h.substring(0,8) + '-' + h.substring(8,12) + '-' + h.substring(12,16) + '-' + h.substring(16,20) + '-' + h.substring(20);
System.debug(guid);
Crypto.GenerateAESKey()
for randomness.GuidUtil
ClassSeveral custom GuidUtil
classes have been developed and shared by the Salesforce community. Here's an example:
global class GuidUtil {
private static String kHexChars = '0123456789abcdef';
global static String NewGuid() {
String returnValue = '';
Integer nextByte = 0;
for (Integer i=0; i<16; i++) {
if (i==4 || i==6 || i==8 || i==10) returnValue += '-';
nextByte = (Math.round(Math.random() * 255)-128) & 255;
if (i==6) {
nextByte = nextByte & 15;
nextByte = nextByte | (4 << 4);
}
if (i==8) {
nextByte = nextByte & 63;
nextByte = nextByte | 128;
}
returnValue += getCharAtIndex(kHexChars, nextByte >> 4);
returnValue += getCharAtIndex(kHexChars, nextByte & 15);
}
return returnValue;
}
global static String getCharAtIndex(String str, Integer index) {
if (str == null) return null;
if (str.length() <= 0) return str;
if (index == str.length()) return null;
return str.substring(index, index+1);
}
}
// Usage:
Account acct = new Account(Name = 'Test Account');
if (acct.AccountUuid__c == null) acct.AccountUuid__c = GuidUtil.NewGuid();
Math.random()
is not cryptographically secure: The use of Math.random()
might not be suitable for security-sensitive applications.Math.random()
with Crypto.getRandomInteger()
is preferable.This approach aims to generate UUIDs that strictly adhere to version 4 standards:
public class GuidUtil {
static List<String> hexMap = new List<String> { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
public static String NewGuid() {
String randomStringAsHex = EncodingUtil.ConvertToHex(Crypto.GenerateAESKey(128));
String versionHexBits = randomStringAsHex.substring(14,16); // 7th byte
String variantHexBits = randomStringAsHex.substring(18,20); // 9th byte
Integer versionIntBits = convertHexToInt(versionHexBits);
Integer variantIntBits = convertHexToInt(variantHexBits);
Integer versionShiftedIntBits = versionIntBits & 15 | 64; // (i & 0x0f) | 0x40
Integer variantShiftedIntBits = variantIntBits & 63 | 128; // (i & 0x3f) | 0x80
String versionShiftedHexBits = convertIntToHex(versionShiftedIntBits); // Always begins with 4
String variantShiftedHexBits = convertIntToHex(variantShiftedIntBits); // Always begins with one of 8,9,a,b
String guid = randomStringAsHex.substring(0,8) + '-' + randomStringAsHex.substring(8,12) + '-' + versionShiftedHexBits + randomStringAsHex.substring(14,16) + '-' + variantShiftedHexBits + randomStringAsHex.substring(18,20) + '-' + randomStringAsHex.substring(20);
return guid;
}
static Integer convertHexToInt(String hex) {
Integer d0 = hexMap.indexOf(hex.substring(1,2));
Integer d1 = hexMap.indexOf(hex.substring(0,1));
Integer intval = d0 + (d1*16);
return intval;
}
static String convertIntToHex(Integer intval) {
String hs0 = hexMap.get(intval & 15); // i & 0x0f
String hs1 = hexMap.get(((intval >> 4) & 15)); //(i >> 4) & 0x0f
return hs1+hs0;
}
}
Crypto.GenerateAESKey()
for random bit generation.Crypto.generateDigest()
This method leverages the Crypto.generateDigest()
method, which provides another way to generate a unique, hash-based value.
String result = EncodingUtil.convertToHex( Crypto.generateDigest('MD5', Blob.valueOf(DateTime.now().getTime().format())) );
or
String result = EncodingUtil.convertToHex( Crypto.generateDigest('MD5', Blob.valueOf(Crypto.getRandomLong().format())) );
Uniqueness: While the chances of collision (generating the same UUID twice) are very low, they are not zero. If absolute uniqueness is critical, implement a check against existing UUIDs in your system.
Security: For applications requiring strong security, always use cryptographically secure random number generators like Crypto.getRandomInteger()
or Crypto.GenerateAESKey()
.
Performance: Consider the performance implications of UUID generation, especially in high-volume scenarios. Some methods are more computationally expensive than others.
UUID Version: Be aware of the different UUID versions and choose the appropriate version for your needs. Version 4 (random UUIDs) is the most common.
Testing: Thoroughly test your UUID generation implementation to ensure it generates valid and unique identifiers. Consider using a regular expression to validate that the generated string matches the UUID pattern.
Use the built-in UUID
class (Spring '24+) when possible. It provides a straightforward and supported way to generate UUIDs.
For older Salesforce versions, prioritize implementations that utilize Crypto.getRandomInteger()
or Crypto.GenerateAESKey()
for enhanced security.
Always include thorough unit tests to verify the correctness and uniqueness of the generated UUIDs.
Consider adding a uniqueness check in your application logic if absolute uniqueness is mandatory.
Generating GUIDs/UUIDs in Salesforce Apex is a common task with several viable approaches. Choosing the right method depends on your specific requirements, considering factors like Salesforce version, security needs, and performance. By carefully considering the options and following the best practices, developers can confidently implement robust and reliable UUID generation mechanisms in their Salesforce applications.