Manipolare i record con DML
Obiettivi di apprendimento
Al completamento di questa unità, sarai in grado di:
- 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
È possibile creare e modificare i record in Salesforce utilizzando il Data Manipulation Language (DML), ovvero il linguaggio per la manipolazione dei dati. Il DML offre un modo semplice per gestire i record, attraverso semplici istruzioni per inserirli, aggiornarli, unirli, cancellarli e ripristinarli.
Apex è un linguaggio incentrato sui dati che viene salvato su Lightning Platform e ha pertanto un accesso diretto ai dati in Salesforce. A differenza di altri linguaggi di programmazione che richiedono una configurazione aggiuntiva per connettersi alle origini dati, con Apex DML la gestione dei record è facilissima! Utilizzando le istruzioni DML, puoi eseguire rapidamente operazioni sui tuoi record Salesforce. In questo esempio, l'account Acme viene aggiunto a Salesforce. Per prima cosa viene creato un sObject Account, che viene successivamente passato come argomento all'istruzione insert, che salva in modo permanente il record in Salesforce.
// 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.
Il titolo specificato per due dei referenti del reparto finanziario dovrebbe essereFinancial 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 la cui proprietà idLookup sia impostata su true. Ad esempio, la proprietà idLookup del campo Email dell'oggetto Contact o User è 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
Il campo opzionale è il token di un campo. Ad esempio, per specificare il campo MyExternalID, l'istruzione è:
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 per Josh Kaplan, non due, perché l'operazione upsert ha trovato il record esistente e lo ha aggiornato anziché creare un nuovo record Contact (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 (Referente) perché la sua 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 la cui descrizione è stata 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
Apex contiene la classe Database integrata, in cui sono presenti metodi che eseguono operazioni DML e corrispondono alle loro controparti in DML.
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 facoltativo 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, con l'opzione di riuscita parziale non vengono generate eccezioni.
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 della classe Database si comporta come la relativa controparte nell'istruzione 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. Uno dei referenti 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.
Lavorare con i record correlati
È possibile creare e gestire record collegati tra loro attraverso le relazioni.
Inserire record correlati
Puoi inserire record correlati a record esistenti se è già stata definita una relazione tra i due oggetti, ad esempio una relazione di ricerca o record principale-record dettaglio. Un record è associato a un record correlato attraverso un ID di chiave esterna. Ad esempio, se si inserisce un nuovo referente, è possibile specificare il record account correlato al referente impostando il valore del campo AccountId
.
Questo esempio mostra come aggiungere un referente a un account (il record correlato) impostando il campo AccountId
sul referente. Referente e Account sono collegati attraverso una relazione di ricerca.
- Esegui questo snippet nella finestra utilizzando l'esecuzione Apex anonima della Developer Console.
Account acct = new Account(Name='SFDC Account'); insert acct; // Once the account is inserted, the sObject will be // populated with an ID. // Get this ID. ID acctID = acct.ID; // Add a contact to this account. Contact mario = new Contact( FirstName='Mario', LastName='Ruiz', Phone='415.555.1212', AccountId=acctID); insert mario;
- Ispeziona i referenti dell'organizzazione.
È stato creato un nuovo account (SDFC Account) il cui elenco correlato Contacts (Referenti) contiene il referente Mario Ruiz.
Aggiornare record correlati
I campi dei record correlati non possono essere aggiornati con la stessa chiamata all'operazione DML e richiedono una chiamata DML separata. Ad esempio, se si inserisce un nuovo referente, è possibile specificare il record account correlato al referente impostando il valore del campo AccountId
. Tuttavia, non è possibile cambiare il nome dell'account senza aggiornare l'account stesso con una chiamata DML separata. Allo stesso modo, quando si aggiorna un referente, se si desidera aggiornare anche l'account correlato al referente, è necessario effettuare due chiamate DML. Nell'esempio seguente un referente e l'account a cui è associato vengono aggiornati utilizzando due istruzioni update
.
// Query for the contact, which has been associated with an account. Contact queriedContact = [SELECT Account.Name FROM Contact WHERE FirstName = 'Mario' AND LastName='Ruiz' LIMIT 1]; // Update the contact's phone number queriedContact.Phone = '(415)555-1213'; // Update the related account industry queriedContact.Account.Industry = 'Technology'; // Make two separate calls // 1. This call is to update the contact's phone. update queriedContact; // 2. This call is to update the related account's Industry field. update queriedContact.Account;
Eliminare record correlati
L'operazione delete
supporta eliminazioni a cascata. Eliminando un oggetto principale, si eliminano automaticamente anche gli oggetti secondari, a patto che ciascun record secondario sia eliminabile.
Ad esempio, eliminando l'account creato in precedenza (SDFC Account), viene eliminato anche il referente ad esso correlato.
- Esegui questo snippet nella finestra utilizzando l'esecuzione Apex anonima della Developer Console.
Account[] queriedAccounts = [SELECT Id FROM Account WHERE Name='SFDC Account']; delete queriedAccounts;
- Controlla gli account e i referenti della tua organizzazione.
Vedrai che sia l'account che il referente correlato sono stati eliminati.
Informazioni sulle transazioni
Le operazioni DML vengono eseguite all'interno di una transazione. Tutte le operazioni DML di una transazione devono concludersi correttamente oppure, se si verifica un errore in un'operazione, l'intera transazione viene annullata e non viene eseguito il commit di alcun dato nel database. Il confine di una transazione può essere un trigger, il metodo di una classe, un blocco di codice anonimo, una pagina Apex o il metodo di un servizio web personalizzato. Ad esempio, se un trigger o una classe creano due account e aggiornano un referente e l'aggiornamento del referente non riesce per un errore della regola di convalida, l'intera transazione subisce un rollback e nessuno degli account viene salvato in modo permanente in Salesforce.
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)
- Apex Developer Guide: Exception Statements (Guida per gli sviluppatori Apex: Istruzioni per le eccezioni)