Manipolare i record con DML
Obiettivi di apprendimento
- Utilizzare DML per inserire, aggiornare ed eliminare i record.
- Eseguire istruzioni DML in blocco.
- Utilizzare upsert per inserire o aggiornare un record.
- Generare un'eccezione DML.
- Utilizzare un metodo Database per inserire nuovi record con l'opzione di esito parziale ed elaborare i risultati.
- Sapere quando utilizzare le istruzioni DML e quando utilizzare i metodi Database.
- Eseguire operazioni DML su record correlati.
Manipolare i record con DML
// Create the account sObject Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100); // Insert the account by using DML insert acct;
Istruzioni DML
Queste sono le istruzioni DML disponibili.
insert
update
upsert
delete
undelete
merge
Ciascuna istruzione DML accetta un singolo sObject oppure un elenco (o un array) di sObject. Operare su un elenco di sObject è un modo più efficiente per elaborare i record.
Tutte queste istruzioni, tranne un paio, sono operazioni di database ben note. Le istruzioni upsert
e merge
sono specifiche di Salesforce e possono essere molto utili.
L'operazione DML upsert
crea nuovi record e aggiorna i record sObject con una singola istruzione, utilizzando un campo specificato per determinare la presenza di oggetti esistenti, oppure il campo ID se non è specificato alcun campo.
L'istruzione merge
unisce fino a tre record dello stesso tipo sObject in uno solo dei record, eliminando gli altri e ristabilendo i rapporti principale-secondario tra tutti i record correlati.
Campo ID assegnato automaticamente ai nuovi record
Quando si inseriscono i record, il sistema assegna un ID a ciascun record. Oltre al salvataggio del valore dell'ID nel database in modo permanente, il valore dell'ID viene inoltre compilato automaticamente nella variabile sObject utilizzata come argomento nella chiamata DML.
Questo esempio mostra come ottenere l'ID nell'oggetto sObject che corrisponde all'account inserito.
// Create the account sObject Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100); // Insert the account by using DML insert acct; // Get the new ID on the inserted sObject argument ID acctID = acct.Id; // Display this ID in the debug log System.debug('ID = ' + acctID); // Debug log result (the ID will be different in your case) // DEBUG|ID = 001D000000JmKkeIAF
DML in blocco
È possibile eseguire operazioni DML sia su un singolo sObject sia in blocco su un elenco di sObject. L'esecuzione di operazioni DML in blocco è il metodo consigliato perché consente di evitare di superare i limiti del governor, ad esempio il limite DML di 150 istruzioni per transazione Apex. Questo limite è stato fissato per garantire un accesso equo alle risorse su Lightning Platform. L'esecuzione di un'operazione DML su un elenco di sObject conta come un'unica istruzione DML, non come un'istruzione per ogni sObject.
In questo esempio i referenti vengono inseriti in blocco utilizzando un elenco di referenti in un'unica chiamata. Nell'esempio, i referenti vengono anche aggiornati in blocco.
- Esegui questo snippet nella Developer Console utilizzando l'esecuzione Apex anonima.
// Create a list of contacts List<Contact> conList = new List<Contact> { new Contact(FirstName='Joe',LastName='Smith',Department='Finance'), new Contact(FirstName='Kathy',LastName='Smith',Department='Technology'), new Contact(FirstName='Caroline',LastName='Roth',Department='Finance'), new Contact(FirstName='Kim',LastName='Shain',Department='Education')}; // Bulk insert all contacts with one DML call insert conList; // List to hold the new contacts to update List<Contact> listToUpdate = new List<Contact>(); // Iterate through the list and add a title only // if the department is Finance for(Contact con : conList) { if (con.Department == 'Finance') { con.Title = 'Financial analyst'; // Add updated contact sObject to the list. listToUpdate.add(con); } } // Bulk update all contacts with one DML call update listToUpdate;
- Ispeziona i referenti creati di recente nell'organizzazione.
Due referenti del reparto finanziario devono essere compilati con il titoloFinancial analyst
(Analista finanziario).
Esecuzione di upsert su record
Se esiste un elenco contenente un misto di record nuovi ed esistenti, è possibile elaborare gli inserimenti e gli aggiornamenti di tutti i record dell'elenco utilizzando l'istruzione upsert
. Upsert consente di evitare la creazione di record duplicati e può permettere di risparmiare tempo perché elimina la necessità di determinare preventivamente quali record esistono.
L'istruzione upsert
abbina gli sObject ai record esistenti confrontando i valori di un solo campo. Se non si specifica un campo quando si chiama questa istruzione, upsert
utilizza l'ID dell'sObject per abbinarlo ai record esistenti in Salesforce. In alternativa, è possibile specificare un campo da utilizzare per l'abbinamento. Per gli oggetti personalizzati, occorre specificare un campo personalizzato contrassegnato come ID esterno. Per gli oggetti standard, è possibile specificare qualsiasi campo con la proprietà idLookup impostata su "true". Ad esempio, il campo Email dell'oggetto Contact o User ha la proprietà idLookup impostata. Per verificare le proprietà di un campo, consulta la Guida di riferimento agli oggetti per Salesforce e Lightning Platform.
Sintassi dell'istruzione upsert
upsert sObject | sObject[]
upsert sObject | sObject[]
field
upsert sObjectList Account.Fields.MyExternalId;
Upsert utilizza la chiave primaria del record sObject (l'ID), un campo idLookup o un campo ID esterno per determinare se creare un nuovo record o aggiornarne uno esistente:
- Se non viene trovata una corrispondenza per la chiave, viene creato un nuovo record oggetto.
- Se viene trovata una sola corrispondenza per la chiave, il record dell'oggetto esistente viene aggiornato.
- Se vengono trovate più corrispondenze per la chiave, viene generato un errore e il record dell'oggetto non viene né inserito né aggiornato.
Questo esempio mostra come upsert aggiorna un record referente esistente e inserisce un nuovo referente in un'unica chiamata. Questa chiamata upsert aggiorna il referente esistente Josh e inserisce un nuovo referente, Kathy.
- Esegui questo snippet nella finestra Execute Anonymous (Esecuzione anonima) della Developer Console.
// Insert the Josh contact Contact josh = new Contact(FirstName='Josh',LastName='Kaplan',Department='Finance'); insert josh; // Josh's record has been inserted // so the variable josh has now an ID // which will be used to match the records by upsert josh.Description = 'Josh\'s record has been updated by the upsert operation.'; // Create the Kathy contact, but don't persist it in the database Contact kathy = new Contact(FirstName='Kathy',LastName='Brown',Department='Technology'); // List to hold the new contacts to upsert List<Contact> contacts = new List<Contact> { josh, kathy }; // Call upsert upsert contacts; // Result: Josh is updated and Kathy is created.
- Ispeziona tutti i referenti dell'organizzazione.
Nella tua organizzazione ci sarà un solo record Josh Kaplan, non due, perché l'operazione upsert ha trovato il record esistente e lo ha aggiornato anziché creare un nuovo record referente. Sarà presente anche un solo record referente per Kathy Brown.
In alternativa, è possibile specificare un campo da utilizzare per l'abbinamento dei record. Questo esempio usa il campo Email dell'oggetto Contact perché ha la proprietà idLookup impostata. L'esempio inserisce il referente Jane Smith, crea un secondo sObject di tipo Contact, lo compila con la stessa email, quindi chiama upsert
per aggiornare il referente utilizzando il campo email per la corrispondenza.
- Esegui questo snippet nella finestra Execute Anonymous (Esecuzione anonima) della Developer Console.
Contact jane = new Contact(FirstName='Jane', LastName='Smith', Email='jane.smith@example.com', Description='Contact of the day'); insert jane; // 1. Upsert using an idLookup field // Create a second sObject variable. // This variable doesn’t have any ID set. Contact jane2 = new Contact(FirstName='Jane', LastName='Smith', Email='jane.smith@example.com', Description='Prefers to be contacted by email.'); // Upsert the contact by using the idLookup field for matching. upsert jane2 Contact.fields.Email; // Verify that the contact has been updated System.assertEquals('Prefers to be contacted by email.', [SELECT Description FROM Contact WHERE Id=:jane.Id].Description);
- Ispeziona tutti i referenti dell'organizzazione.
Nell'organizzazione ci sarà solo un referente Jane Smith con la descrizione aggiornata.
Eliminare i record
Puoi eliminare i record salvati in modo permanente utilizzando l'istruzione delete
. I record eliminati non vengono eliminati definitivamente da Lightning Platform, ma rimangono nel Cestino per 15 giorni, periodo durante il quale possono essere ripristinati.
Questo esempio mostra come eliminare tutti i referenti il cui cognome è Smith. Se hai eseguito l'esempio del DML in blocco, l'organizzazione dovrebbe già contenere due referenti con il cognome Smith. Esegui questo snippet nella Developer Console utilizzando l'esecuzione Apex anonima, quindi verifica che non ci siano più referenti con il cognome Smith.
Contact[] contactsDel = [SELECT Id FROM Contact WHERE LastName='Smith']; delete contactsDel;
Eccezioni delle istruzioni DML
Se durante un'operazione DML si verifica un errore, viene restituita un'eccezione di tipo DmlException
. Per gestire le condizioni di errore, puoi intercettare le eccezioni nel tuo codice.
Questo esempio produce una DmlException
perché tenta di inserire un account privo del campo obbligatorio Name. L'eccezione viene intercettata nel blocco catch.
try { // This causes an exception because // the required Name field is not provided. Account acct = new Account(); // Insert the account insert acct; } catch (DmlException e) { System.debug('A DML exception has occurred: ' + e.getMessage()); }
Metodi Database
Questi metodi Database sono statici e vengono richiamati utilizzando il nome della classe.
Database.insert()
Database.update()
Database.upsert()
Database.delete()
Database.undelete()
Database.merge()
A differenza delle istruzioni DML, i metodi Database dispongono del parametro opzionale allOrNone che permette di specificare se l'operazione può riuscire parzialmente. Quando questo parametro è impostato su false
, qualora dovessero verificarsi degli errori su un insieme parziale di record, il commit dei record senza errori verrà eseguito, mentre per i record con errori verranno restituiti gli errori. Inoltre, non vengono generate eccezioni con l'opzione di riuscita parziale.
Ecco come chiamare il metodo insert
con allOrNone impostato su false
.
Database.insert(recordList, false);
I metodi Database restituiscono oggetti risultato contenenti informazioni sulla presenza o meno di errori per ciascun record. Ad esempio, le operazioni insert e update restituiscono ciascuna un array di oggetti Database.SaveResult
.
Database.SaveResult[] results = Database.insert(recordList, false);
Per impostazione predefinita, il parametro allOrNone è true
, il che significa che il metodo Database si comporta come la relativa controparte in DML e genera un'eccezione se si verifica un errore.
Le due istruzioni seguenti equivalgono all'istruzione insert recordList;
.
Database.insert(recordList);
E:
Database.insert(recordList, true);
Esempio: inserimento di record parzialmente riuscito
Vediamo un esempio che utilizza i metodi Database. Questo esempio si basa sull'esempio di DML in blocco, ma sostituisce l'istruzione DML con un metodo Database. Il metodo Database.insert()
viene chiamato con l'opzione di riuscita parziale. Un solo referente nell'elenco viene inizializzato di proposito senza alcun campo e causerà un errore perché il referente non può essere salvato senza il campo obbligatorio LastName. Tre referenti vengono inseriti e il referente senza alcun campo genera un errore. L'ultima parte di questo esempio esegue un'iterazione sui risultati restituiti e scrive messaggi di debug nel registro di debug.
- Esegui questo esempio nella finestra Execute Anonymous (Esecuzione anonima) della Developer Console.
// Create a list of contacts List<Contact> conList = new List<Contact> { new Contact(FirstName='Joe',LastName='Smith',Department='Finance'), new Contact(FirstName='Kathy',LastName='Smith',Department='Technology'), new Contact(FirstName='Caroline',LastName='Roth',Department='Finance'), new Contact()}; // Bulk insert all contacts with one DML call Database.SaveResult[] srList = Database.insert(conList, false); // Iterate through each returned result for (Database.SaveResult sr : srList) { if (sr.isSuccess()) { // Operation was successful, so get the ID of the record that was processed System.debug('Successfully inserted contact. Contact ID: ' + sr.getId()); } else { // Operation failed, so get all errors for(Database.Error err : sr.getErrors()) { System.debug('The following error has occurred.'); System.debug(err.getStatusCode() + ': ' + err.getMessage()); System.debug('Contact fields that affected this error: ' + err.getFields()); } } }
- Verifica i messaggi di debug (usa la parola chiave DEBUG per il filtro).
Dovrebbe essere segnalato un solo errore e dovrebbero essere stati inseriti tre referenti.
È meglio usare le istruzioni DML o i metodi Database?
- Usa le istruzioni DML se desideri che gli eventuali errori che si verificano durante l'elaborazione DML in blocco risultino nella generazione di eccezioni Apex che interrompono immediatamente il flusso di controllo (tramite l'utilizzo di blocchi
try. . .catch
). Questo comportamento è simile al modo in cui le eccezioni vengono gestite nella maggior parte dei linguaggi procedurali per database. - Utilizza i metodi della classe Database se desideri consentire una riuscita parziale di un'operazione DML in blocco: se per un record si verifica un errore, il resto dell'operazione DML può comunque concludersi correttamente. L'applicazione può quindi ispezionare i record rifiutati ed eventualmente riprovare l'operazione. Utilizzando questo formato, puoi scrivere un codice che non genera mai errori di eccezione DML. Al contrario, il tuo codice può utilizzare l'array di risultati appropriato per valutare se l'operazione è stata completata correttamente o se si sono verificati errori. Nota che i metodi Database includono anche una sintassi che supporta le eccezioni generate, in modo simile alle istruzioni DML.
Risorse
- Apex Developer Guide (Guida per gli sviluppatori Apex)
- Apex Developer Guide: Working with Data in Apex (Guida per gli sviluppatori Apex: Lavorare con i dati in Apex)
- Apex Developer Guide: Adding and Retrieving Data With DML (Guida per gli sviluppatori Apex: Aggiungere e recuperare dati con DML)
- Apex Developer Guide: Database Class (Guida per gli sviluppatori Apex: Classe Database)