Use transactions.
Transactions allow you to isolate changes to the database until you're done making all the changes. Nobody else can see those changes and nobody else can change those rows while you're working in your transaction. Then you commit
the transaction and everyone else can see your changes all at once. If there's an error, you can rollback
all the changes you made in your transaction.
In Perl, using DBI, the default is to not use transactions. Every statement is automatically committed. It is a very good idea to always turn transactions on. You typically do this when connecting to the database by setting AutoCommit off. You'll also want to turn on RaiseError so database errors will happen automatically, you don't have to write or die...
everywhere. Then, since you don't want to see errors twice, turn PrintError off.
my $dbh = DBI->connect(
'dbi:mysql:...', $user, $pass,
{ AutoCommit => 0, RaiseError => 1, PrintError => 0 }
);
Now everything you do will be in a transaction and nothing you do will be visible to anyone else until you run $dbh->commit
. Wrap all the work in an eval
block to catch any errors thrown now that RaiseError
is on. If you get an error, $dbh->rollback
will throw out any partial work.
eval {
my $insert_table_orders_sth = $dbh->prepare(
"INSERT INTO table_orders (...) VALUES (?, ?, ?)"
);
$insert_table_orders_sth->execute(@table_orders_values);
my $insert_table_orders_details_sth = $dbh->prepare(
"INSERT INTO table_orders_details (...) VALUES (?, ?, ?)"
);
for my $product ($cart->products) {
$insert_table_orders_details_sth->execute($product->values);
}
# All done, make the changes visible to everyone else.
$dbh->commit;
};
if($@) {
warn "There was a database error: $@";
# Wipe out anything you did above.
eval { $dbh->rollback };
# Do whatever else you might need to cleanup the error.
}