ApexSensei

Drills DML operations

Roll back parent and children on a child failure

DML operations · Intermediate · 15 min · Runs in your Salesforce Developer org · Apex Path

Save a parent Account and its Contacts under one `Savepoint`, and roll everything back if one child fails to insert. The second Contact is missing `LastName`, so the all-or-none insert throws `DmlException`. On the exception, `Database.rollback(sp)` erases the parent too, so nothing with the tag is left behind.

Lesson

Why this matters

Migrations and intake flows save a parent record and its children together. If one child is bad, you don't want the parent to stay behind without its children. One Savepoint taken before the first DML lets a single Database.rollback erase every save in the step.

The smallest working example

Savepoint sp = Database.setSavepoint();
try {
    insert parent;
    insert children;
} catch (DmlException dmlError) {
    Database.rollback(sp);
}

If insert children throws, the rollback undoes both inserts. No row with the parent's tag remains.

Name the parts

  • **Database.setSavepoint()** returns a handle you can roll back to. Take it before the first DML.
  • **try { ... }** holds the inserts that may fail.
  • **catch (DmlException dmlError)** runs when any DML in the block throws, including the children's all-or-none failure.
  • **Database.rollback(sp)** undoes every DML made after sp was taken, including the parent insert.
  • After rollback, a count by tag returns zero on the parent and zero on the children.

A common wrong version

insert parent;
Savepoint sp = Database.setSavepoint();
try {
    insert children;
} catch (DmlException dmlError) {
    Database.rollback(sp);
}

The savepoint sits after the parent insert. Rollback only undoes work done after sp, so the children go but the parent stays. The row is an orphan.

The fix

Take the savepoint before the parent insert:

Savepoint sp = Database.setSavepoint();
try {
    insert parent;
    insert children;
} catch (DmlException dmlError) {
    Database.rollback(sp);
    caught = true;
}

Now you try

Take the savepoint, insert the Account and two Contacts (the second is missing LastName), catch DmlException, roll back, then count rows by tag. Print Caught:, Account count: and Contact count: in that order.

Learn more

The challenge

Insert a tagged Account and two tagged Contacts under one Savepoint. The second Contact is missing LastName, which triggers an all-or-none failure. Roll everything back on the exception.

Examples:

  • The second Contact triggers DmlException → Caught: true
  • Rollback erases the parent save too → Account count: 0
  • All-or-none insert leaves no children either → Contact count: 0

Requirements:

  1. Database.setSavepoint() should be present before the first DML.
  2. Database.rollback(sp) should be present inside the catch.
  3. Caught: true should be printed from a boolean set inside the catch.
  4. Account count: 0 should be printed from a count by tag after the rollback.
  5. Contact count: 0 should be printed from a count by tag after the rollback.

Starter code

String tag = 'AS-D312-' + UserInfo.getUserId() + '-' +
    String.valueOf(Datetime.now().getTime());
Account acct = new Account(Name = tag);

Boolean caught = false;

Related drills

Try this drill free

You write the Apex yourself. ApexSensei runs it and tells you what passed and what did not.

‹ Undelete from the Recycle Bin · Merge duplicate Accounts ›