publicwithsharingclassAtomic{// Warning!!! This code has not been tested thoroughly. It works, but
// may throw too many lock failures, etc. It may not work for your
// particular use case.
// We had a requirement that both accounts and contacts should have an
// auto-incrementing id, but the customer never wanted the ids to overlap.
// We could have just started the counters far enough apart to make sure
// the max of one would never reach the min of the other, but the
// customer didn't like that.
// The key to the whole thing is SOQL "select for update" syntax.
// See getAtomicSingleton() below.
// This will cause an error to be thrown if there are any changes to your
// record between the time you select and the time you update. This isn't
// ideal, but it's the only way I can think of to achieve atomicity.
// We created a custom object, Atomic__c, that will only ever have a single
// row and a single field, Account_Contact_Id__c.
// We will use a trigger to set the account and contact ids.
// We select this single row, add the number of IDs we are going to
// need in our trigger to the current Account_Contact_Id__c value.
// Then we update the row with it's new value. If it
// fails we try again up to 7 times.
publicclassMyExceptionextendsException{}// Return an array of integer ids which once returned will ever be returned
// again. So once 1-10 have been returned they will never be returned again.
// The smallest id returned next time this method returns successfully will
// be 11.
publicstaticInteger[]nextAccountContactIds(Integercount){if(count<1||count>200){thrownewMyException('Countoutofrange:'+count+'.Mustbebetween1and200(inclusive).');}returnnextAccountContactIds(count,0);}privatestaticInteger[]nextAccountContactIds(Integercount,Integerretry){Atomic__ca=getAtomicSingleton();IntegerstartingValue=1;if(a.Account_Contact_Id__c==null){a.Account_Contact_Id__c=startingValue;}Integer[]rv=buildArray(a.Account_Contact_Id__c.intValue(),count);a.Account_Contact_Id__c+=count;try{updatea;}catch(Exceptione){if(retry<7){returnnextAccountContactIds(count,++retry);}else{throwe;}}returnrv;}privatestaticInteger[]buildArray(IntegerstartingNumber,Integercount){Integer[]ints=newList<Integer>();for(Integeri=startingNumber;i<startingNumber+count;i++){ints.add(i);}returnints;}// The very first time this is called it will create the singleton row.
// It will NOT be atomic that very first time because we are doing an insert
// not a select for update. You may want to call nextAccountContactIds()
// manually once to seed the table before using it in a context that will
// require atomicity.
privatestaticAtomic__cgetAtomicSingleton(){Atomic__c[]aa=Database.query('select'+ChargentService.allFields(Atomic__c.SObjectType)+'fromAtomic__cwhereName=\'Singleton\'limit1forupdate');if(aa.size()==0){Atomic__ca=newAtomic__c();a.Name='Singleton';inserta;returna;}returnaa[0];}}