Apex Academy
Módulo 4·01·30 min

insert, update, delete, upsert

Las cuatro operaciones de Data Loader, escritas en una línea de Apex. Qué necesita cada una, qué pasa por dentro al guardar y por qué siempre trabajan mejor con listas.

En Admin esLas operaciones de Data Loader y los elementos Create/Update/Delete Records de Flow

✓ En esta sub-lección

  1. 01Crear, modificar, borrar y recuperar registros con insert, update, delete y undelete.
  2. 02Usar upsert con un campo de Id externo para crear o actualizar según exista.
  3. 03Explicar qué dispara un DML: reglas de validación, flows y triggers.

La nutria: ¿Te acuerdas? · Repaso del Módulo 3

Tienes los Ids de 200 cuentas en un Set. ¿Cómo las traes de la base de datos?

En el Módulo 3 aprendiste a leer la base de datos. Todo lo que has construido hasta ahora (objetos con new, campos cambiados, listas preparadas) vive solo en memoria y desaparece al terminar la . Para que algo se guarde de verdad hace falta : las instrucciones que escriben en la base de datos.

La nutria: El paralelo de Admin

La primera vez que vi esto pensé en Data Loader: Insert, Update, Upsert, Delete son exactamente las instrucciones de esta lección, con las mismas reglas (Update necesita el Id, Upsert un Id externo, lo borrado va a la Papelera). En Flow son Create, Update y Delete Records. Apex no inventa nada: te da las mismas operaciones con más control.
insert new Account(Name = 'Nimbus Logistics');

Data Loader → Insert, sin columna Id

Necesita: Ningún Id: te lo da Salesforce

IdNameRating
001…A1AcmeHot
001…B2GlobexWarm

Así está la org antes. Pulsa Ejecutar.

Cada operación de Data Loader tiene su instrucción, y cada una necesita lo mismo que allí.

insert: crear

Apex
Account acc = new Account(Name = 'Nimbus Logistics', Industry = 'Transportation');
System.debug(acc.Id);   // null: todavía no existe en la base de datos

insert acc;
System.debug(acc.Id);   // 001...: Salesforce le ha dado un Id
Después del insert, la variable ya tiene su Id, como el archivo success.csv de Data Loader. Con ese Id puedes crear los hijos enseguida.
Apex
List<Contact> contacts = new List<Contact>{
    new Contact(LastName = 'Ortega', AccountId = acc.Id),
    new Contact(LastName = 'Lindqvist', AccountId = acc.Id)
};
insert contacts;   // una sola instrucción para toda la lista

update: cambiar

update necesita el Id, igual que en Data Loader, porque así sabe qué registro cambiar. Lo habitual es consultar, modificar en memoria y actualizar. Pero si ya tienes el Id y solo quieres cambiar un campo, no hace falta consultar: basta un sObject con el Id y el campo, como un CSV de Data Loader con solo dos columnas.

Apex
// Consultar → modificar → actualizar
List<Account> cold = [SELECT Id, Rating FROM Account WHERE Rating = 'Cold' AND Industry = 'Retail'];
for (Account a : cold) {
    a.Rating = 'Warm';
}
update cold;

// Sin consultar: solo el Id y lo que cambia
update new Account(Id = acc.Id, Rating = 'Hot');
El bucle cambia los registros en memoria (referencias, Módulo 5); el update, fuera del bucle, los guarda todos de una vez.

delete y undelete: la Papelera

Apex
List<Lead> junk = [SELECT Id FROM Lead WHERE Email = null AND CreatedDate < LAST_N_DAYS:365];
delete junk;        // van a la Papelera, como al borrar desde la interfaz

// Recuperar: ALL ROWS hace que la consulta vea también lo borrado
List<Lead> back = [SELECT Id FROM Lead WHERE IsDeleted = true AND LastName = 'Ortega' ALL ROWS];
undelete back;

upsert: crear o actualizar

Cuando cargas datos que vienen de otro sistema (un ERP, una tienda online) no sabes si cada cliente ya existe en Salesforce. En Data Loader usas Upsert con un campo marcado como : si el valor ya está, actualiza; si no, crea. En Apex es igual, indicando ese campo detrás de la lista.

Apex
// ERP_Id__c es un campo de texto marcado como External ID
List<Account> fromErp = new List<Account>{
    new Account(ERP_Id__c = 'C-1001', Name = 'Nimbus Logistics'),
    new Account(ERP_Id__c = 'C-2040', Name = 'Aurora Foods')
};
upsert fromErp Account.Fields.ERP_Id__c;
Sin campo detrás, upsert usa el Id de Salesforce: los registros con Id se actualizan y los que no lo tienen se crean.

Tu código pasa por las mismas reglas que un usuario

Un insert desde Apex no es una puerta trasera. El registro recorre el mismo guardado que cuando alguien pulsa Guardar en la pantalla: se ejecutan tus reglas de validación, tus flows desencadenados por registro y los triggers del objeto (Módulo 6). Si una regla de validación rechaza el registro, el insert lanza una DmlException con el mismo mensaje que vería el usuario. Tu código no se salta la configuración de la org: convive con ella.

La nutria: El «obligatorio» del formato de página no te protege aquí

Esto me pilló en mi primera integración: un campo obligatorio solo en el formato de página se exige en la pantalla y en ningún sitio más, así que un insert desde código lo guarda vacío sin quejarse. Si un dato no puede faltar nunca, que lo exija la definición del campo (Required) o una regla de validación.

El encargo de este módulo

La nutria: Las seis piezas de la operación diaria

Vengo de la reunión con Northwind y estas son las seis piezas que quieren pasar a código: 1 · El alta de un cliente que acaba de firmar (Onboarding). 2 · La carga nocturna de leads del formulario web (Marketing). 3 · El cierre de trimestre, que marca como Hot las cuentas con negocio ganado. 4 · Una herramienta para medir cuánto gasta cada bloque. 5 · El alta de la pieza 1, ahora completa o inexistente. 6 · El escalado diario de casos de cuentas Hot (Soporte), que en los Módulos 6 y 7 se convertirá en un trigger.

La nutria: ¿Y por qué no un Flow? Para dar de alta un cliente, sí

Con franqueza: el alta de esta tarea es un Screen Flow de manual, con dos Create Records. Aprendes DML en Apex porque dentro de un trigger (Módulo 6) no hay Create Records: hay insert. Y porque varias piezas de la operación piden lo que Flow no da, como la lista de qué leads fallaron y por qué, o saber cuánto presupuesto llevas gastado a mitad de proceso. En esas lecciones te digo dónde está el hueco.