Apex Academy
Módulo 4·05·25 min

Savepoints y rollback

Marcar un punto de la transacción y, si lo que viene después sale mal, volver a él: deshacer solo una parte, sin tirar todo lo anterior.

En Admin esEl elemento Roll Back Records de un camino de error en Flow

✓ En esta sub-lección

  1. 01Explicar qué se deshace automáticamente cuando una transacción falla.
  2. 02Usar Database.setSavepoint() y Database.rollback() para deshacer una parte concreta.
  3. 03Conocer lo que un rollback no deshace y lo que cuesta en límites.

La nutria: ¿Te acuerdas? · Repaso de la lección 4

Limits.getQueries() devuelve 95 y Limits.getLimitQueries() devuelve 100. ¿Qué significa?

Ya sabes que un error sin tratar deshace la entera. Pero a veces no quieres eso: quieres intentar un paso y, si falla, dejar la base de datos como estaba justo antes de ese paso, y seguir con lo demás. Para eso existen los .

La nutria: El paralelo de Admin

En mis Flows, cuando un Create Records fallaba, conectaba un camino de error (fault path) y ponía en él el elemento Roll Back Records: deshace los cambios pendientes de ese flow y luego decides qué hacer, por ejemplo mandar un aviso. Database.rollback() es ese elemento, con una ventaja: tú eliges exactamente a qué punto volver.

Lo que ya ocurre solo

Si la transacción termina con un error sin tratar, Salesforce no guarda nada de ella: ni tus inserts, ni lo que hicieron los flows, ni los triggers. Por eso un usuario nunca ve «media cuenta guardada». Esta red de seguridad es automática y es todo o nada. Los savepoints sirven para cuando quieres algo intermedio.

Marcar y volver

Apex
Savepoint sp = Database.setSavepoint();   // 📍 marca: la base de datos está así

Account acc = new Account(Name = 'Aurora Foods');
insert acc;

List<Contact> contacts = new List<Contact>{
    new Contact(LastName = 'Nakamura', AccountId = acc.Id),
    new Contact(LastName = 'Okafor',   AccountId = acc.Id, Email = 'no-es-un-email')
};
List<Database.SaveResult> results = Database.insert(contacts, false);

Boolean anyFailed = false;
for (Database.SaveResult sr : results) {
    if (!sr.isSuccess()) {
        anyFailed = true;
    }
}

if (anyFailed) {
    Database.rollback(sp);   // ↩ vuelve a la marca: ni cuenta ni contactos
}
Sin el rollback quedaría una cuenta con un solo contacto: justo lo que el negocio no quiere. Con él, o se guarda todo el alta o nada.
  1. 1. Savepoint sp = Database.setSavepoint();
  2. 2. insert acc;
  3. 3. Database.insert(contacts, false);
  4. 4. Database.rollback(sp);

EN LA BASE DE DATOS

vacía

Paso 1 de 5

El alta de Aurora Foods: una cuenta y dos contactos. Okafor trae el correo mal escrito. Pulsa Reproducir.

Lo que ocurrió antes de la marca se queda. Lo de después, desaparece.

En este ejemplo el fallo se detecta mirando los SaveResult, como en la lección 2. En el Módulo 8 verás la otra forma habitual: capturar la excepción de un insert con try/catch y hacer el rollback dentro del catch. La mecánica del savepoint es la misma.

Lo que conviene saber antes de usarlo

  • setSavepoint() y rollback() cuentan cada uno como una instrucción DML en los límites. No los metas en un bucle.
  • El rollback deshace la base de datos, no tus variables. Después del rollback, acc sigue teniendo el Id que recibió en el insert. Si intentas insert acc otra vez, falla: un registro con Id no se puede insertar. Si necesitas reintentar, crea el objeto de nuevo o usa acc.clone() (Módulo 5).
  • Los contadores de límites no vuelven atrás: las consultas y DML que gastaste antes del rollback siguen gastados.
  • Solo deshace lo que se hizo después de esa marca, en esa misma transacción. No es un «deshacer» de ayer.

Dos detalles que se aprenden a golpes

  • Un savepoint no es gratis: cada Database.setSavepoint() y cada Database.rollback() cuentan como una instrucción DML del límite de 150. Úsalos donde hay una unidad que proteger, no en cada línea.
  • El rollback deshace la base de datos, no tus variables. Después de volver al savepoint, acc sigue teniendo el Id que recibió en el insert, aunque ese registro ya no exista. Si intentas insertarla otra vez tal cual, falla porque un insert no admite un Id: crea un registro nuevo o limpia el Id primero.

La nutria: Ya conoces el «todo o nada» de un guardado

Piensa en lo que ves cada día: cuando una regla de validación rechaza un registro en pantalla, no se guarda nada de ese guardado, ni el campo que estaba bien ni el que estaba mal. Toda la transacción funciona así por defecto: si algo falla sin tratar, se deshace entero. El savepoint sirve para el caso contrario: cuando has decidido capturar el error y seguir, te deja elegir qué parte deshacer.

La nutria: ¿Y por qué no un Flow? Roll Back Records se lo lleva todo

Para el alta de Aurora Foods sola, un fault path con Roll Back Records habría funcionado. El problema llega cuando el mismo proceso hace varias cosas: Roll Back Records deshace todo lo pendiente de la transacción. Si el proceso da de alta tres clientes y falla el tercero, se lleva también los dos que salieron bien. Con un savepoint antes de cada alta, solo se deshace la que falló.