(免责声明:APEX和数据库编程新手,编码新手)
我正在尝试构建代码,这将从一个触发器调用,该触发器在一个特定的表中创建一个记录(我将称此为WebID记录),任何时候都可以创建一个其他对象。这是相当直接的,但约束是,WebID记录有一个文本字段(string WebID_Value__c;
)被用作一个ID,需要是唯一的(和索引,但可能不相关在这里)。这不是一个自动编号字段,因为ID也不能是连续的。
因为触发器使用bulbulbulfied代码,并且一次创建多个记录的可能性非常大,所以我编写了一个函数(list<WebID__c> CreateWebID(integer count)
)来创建nWebID记录并尝试将它们插入数据库。在成功输入所有n记录后,返回它们。
到目前为止,一切顺利。
我遇到的问题是,什么是最好的方法来捕捉非常不常见的边缘情况,即生成两次相同的随机ID(曾经)?我想我可以使用数据库。插入([list], false)并查看数据库。SaveResult[]查找任何错误,但随后我们遇到了我被警告永远不要做的事情:do NOT PUT DML IN A LOOP.
(我生成的ID有281,474,976,710,656种可能的排列,但我正在制作一个系统的原型,该系统可能被大量创建记录的人访问。最终的设计可能会有超过8位数字的ID。也就是说,我想为这种特殊的边缘情况编码,以防万一。)
我的策略应该是什么?
tl;dr:我想创建并插入一个记录列表,生成的id保证是唯一的(即使与以前的函数运行相比),并使函数始终返回请求的条目数。
这是我的代码。(同样,APEX和数据库编码是新手,所以如果发现其他问题,请指出。)
public class WebIDHelper {
public static final string[] ValidChars = new string[] {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'};
public static final integer IDLENGTH = 8;
public static final integer ValidCharsLength = ValidChars.size();
public static string GenerateID()
{
//generated an 8 character long ID which looks to all the world like a
//base64 encoded string (we don't care about the value being stored, just
//that it's unique), replacing the last two characters with - and _ since
//this will be used in a URL at some point.
string ID = '';
for (integer i = 0; i < IDLENGTH; i++)
{
ID = ID + ValidChars[(integer) (math.random() * ( ValidCharsLength ))];
}
return ID;
}
public static list<WebID__c> CreateWebID(integer count)
{
//generate a number of unique WebID instances (defined by count), insert
//them, return them. If a non-unique value for WebID_Value__c is found,
//the record being entered gets a new value set for WebID_Value__c.
list<WebID__c> MainList = new list<WebID__C>();
//create the WebID instances
for (integer i = 0; i < count; i++)
{
WebID__c item = new WebID__c(WebID_Value__c = GenerateID());
MainList.add(item);
}
//add to database
Database.SaveResult[] srList = Database.insert(MainList, false);
//to-do: StackExchange question: do we look through srList in a loop,
//generate new IDs, and try to insert again, in a loop? if not, what's
//the right way to do this?
return MainList;
}
}
我想我解决了。告诉过你妈妈没有养大不…别介意。
代码现在查看Database的结果。插入并计算字段名为WebID_Value__c且状态码表示重复的任何错误。如果找到任何错误,该函数将使用找到的错误数递归调用自身,并将结果附加到返回的列表中。它将递归三层,然后放弃并抛出异常(只有在已经有大量web记录时才会发生这种情况)。这是膨胀的,所以这整个东西最多只算作四个dml操作,即使我使用DataLoader导入记录。
未测试,可能有bug
public class WebIDHelper {
public static final string[] ValidChars = new string[] {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'};
public static final integer IDLENGTH = 8;
public static final integer ValidCharsLength = ValidChars.size();
public static string GenerateID()
{
//generated an 8 character long ID which looks to all the world like a
//base64 encoded string (we don't care about the value being stored, just
//that it's unique), replacing the last two characters with - and _ since
//this will be used in a URL at some point.
string ID = '';
for (integer i = 0; i < IDLENGTH; i++)
{
ID = ID + ValidChars[(integer) (math.random() * ( ValidCharsLength ))];
}
return ID;
}
public class WebIDException extends Exception {}
public static list<WebID__c> CreateWebID(integer count)
{
return CreateWebID(count, 0);
}
static list<WebID__c> CreateWebID(integer count, integer recursioncount)
{
if (recursioncount > 2)
{
throw new WebIDException('Couldn't find new ID after 3 attempts.');
}
//generate a number of unique WebID instances (defined by count), insert
//them, return them. If a non-unique value for WebID_Value__c is found,
//the record being entered gets a new value set for WebID_Value__c.
list<WebID__c> MainList = new list<WebID__C>();
//create the WebID instances
for (integer i = 0; i < count; i++)
{
WebID__c item = new WebID__c(WebID_Value__c = GenerateID());
MainList.add(item);
}
//add to database
Database.SaveResult[] srList = Database.insert(MainList, false);
Integer FailedDuplicateID = 0;
for (Database.SaveResult sr : srList)
{
if (!sr.issuccess())
{
for (Database.Error err : sr.getErrors())
{
for (string f : err.fields)
{
if (f == 'WebID_Value__c')
{
if (err.statuscode == system.statuscode.DUPLICATE_VALUE)
{
FailedDuplicateID++;
}
}
}
}
}
}
if (FailedDuplicateID > 0)
{
MainList.addall(CreateWebID(FailedDuplicateID, recursioncount + 1));
}
return MainList;
}
}