[Catalyst-commits] r12374 - trunk/examples/CatalystAdvent/root/2009/pen

zarquon at dev.catalyst.perl.org zarquon at dev.catalyst.perl.org
Mon Dec 14 23:50:45 GMT 2009


Author: zarquon
Date: 2009-12-14 23:50:44 +0000 (Mon, 14 Dec 2009)
New Revision: 12374

Removed:
   trunk/examples/CatalystAdvent/root/2009/pen/dbic-txn.pod
Log:
day 15

Deleted: trunk/examples/CatalystAdvent/root/2009/pen/dbic-txn.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2009/pen/dbic-txn.pod	2009-12-14 23:49:22 UTC (rev 12373)
+++ trunk/examples/CatalystAdvent/root/2009/pen/dbic-txn.pod	2009-12-14 23:50:44 UTC (rev 12374)
@@ -1,179 +0,0 @@
-=head1 Transactions and DBIx::Class
-
-Database transactions help ensure the integrity and consistency of your data.
-Here I will present a few (contrived) examples where they are useful.
-
-=head2 Atomicity
-
-Often a Model operation, in the sense of business logic, requires a group of
-closely related (or dependent) database operations.  These should be
-atomic.  That is if any single operation in the group fails, then the whole
-group of operations will fail and your data remains consistent.
-
-Suppose you wrote some code such as:
-
-    sub create_user : Local {
-        my ($self, $c) = @_;
-
-        # we love hash slices
-        my ($username, $first_name, $last_name, $token_id) =
-            @{ $c->req->params }{qw/username first_name last_name token_id/};
-
-        unless ($c->model('Token')->validate($token_id)) {
-            $c->flash->{error} = "Bad token: $token_id";
-            $c->detach('/error');
-        }
-
-        my $user = $c->model('DB::User')->create({
-            username => $username,
-            first_name => $first_name,
-            last_name => $last_name,
-        });
-
-        $user->add_to_tokens({ token_id => $token_id });
-
-        $c->flash->{message} = "Created user ${username}!";
-    }
-
-This looks perfectly reasonable, but there are two operations here, one which
-creates a user entry, and one that creates a token entry for it.
-
-What could possibly go wrong between them? Lots of things, such as the web
-server going down in flames, the database server crashing, alien invasion, and
-so on. While the chances of this are small, repairing erroneous data in a
-database is a painful, expensive and hazardous process.
-
-Also we had to use an unnatural control flow here, normally we'd validate the
-token before it is created, rather than before the user is created.
-
-Enter C<txn_do>, from L<DBIx::Class::Schema>.
-
-    $c->model('DB')->txn_do(sub {
-        my $user = $c->model('DB::User')->create({
-            username => $username,
-            first_name => $first_name,
-            last_name => $last_name,
-        });
-
-        $c->model('Token')->validate($token_id) or die "Bad token: $token_id";
-
-        $user->add_to_tokens({ token_id => $token_id });
-    });
-
-Now that the operation is atomic, your user entries as well as any related
-entries are guaranteed to be consistent, and we are using a more natural
-control flow.
-
-Any exceptions from C<txn_do> are re-thrown and a C<ROLLBACK> is issued,
-leaving the database untouched by the code enclosed by the transaction.
-
-Exceptions can be cleaned up in your C<end> handler, by getting them from C<<
-$c->error >>, or using something like
-L<Catalyst::Action::RenderView::ErrorHandler>. However, in some cases you may
-want to wrap them in an eval (or one of L<Try::Catch>, L<Try::Tiny>) to detach
-in order to short-circuit a chain for example.
-
-It is also recommended to factor out database code into the relevant
-L<DBIx::Class::ResultSet> methods. ResultSet classes go into the
-C<lib/MyApp/Schema/ResultSet> directory (for example), next to the C<Result>
-directory and inherit from C<DBIx::Class::ResultSet>.
-
-=head2 txn_scope_guard
-
-A different control flow for transactions is possible with C<txn_scope_guard>,
-from L<DBIx::Class::Storage>.
-
-With this method, an object is created and a transaction is started; if the
-object goes out of scope before C<< ->commit >> is called on it, via an
-exception or any other means, a C<ROLLBACK> will be issued.
-
-This can be useful if you don't want to deal with catching exceptions from
-C<txn_do>.  Another reason you might choose to use C<txn_scope_guard> over
-C<txn_do> is that C<txn_do> will attempt to reconnect to the database and
-re-run your code if you lost your connection; C<txn_scope_guard> does not
-have this overhead.
-
-Here's the previous example using C<txn_scope_guard>:
-
-    sub create_user : Local {
-        my ($self, $c) = @_;
-
-        my ($username, $first_name, $last_name, $token_id) =
-            @{ $c->req->params }{qw/username first_name last_name token_id/};
-
-        my $scope_guard = $c->model('DB')->txn_scope_guard;
-
-        my $user = $c->model('DB::User')->create({
-            username => $username,
-            first_name => $first_name,
-            last_name => $last_name,
-        });
-
-        unless ($c->model('Token')->validate($token_id)) {
-            $c->flash->{error} = "Bad token: $token_id";
-            $c->detach('/error');
-        }
-
-        $user->add_to_tokens({ token_id => $token_id });
-
-        $c->flash->{message} = "Created user ${username}!";
-
-        $scope_guard->commit;
-    }
-
-Here on C<< $c->detach >> the C<$scope_guard> goes out of scope, and a
-C<ROLLBACK> is issued, as well as on any exception. The changes are committed
-at the end of the action.
-
-=head2 Nested Transactions
-
-Many databases support a feature called 'savepoints', which are nested
-transactions. They allow you to roll back a group of operations that are part
-of a larger group of operations.
-
-L<DBIx::Class> supports savepoints for the MySQL, Oracle, Postgres, MSSQL
-and Sybase databases.
-
-You have to add C<< auto_savepoint => 1 >> to your C<connect_info> to enable
-this feature:  see L<DBIx::Class::Storage::DBI> for details.
-
-    sub validate_batch : Local {
-        my ($self, $c) = @_;
-        my @urls = split "\n", $c->req->param('urls');
-
-        $c->model('DB')->txn_do(sub {
-            my $batch = $c->user->add_to_batches({});
-            my $validated_count = 0;
-
-            for my $url (@urls) {
-                try {
-                    $c->model('DB')->txn_do(sub {
-                        my $url = $c->model('DB::Url')->find_or_create({ url => $url });
-                        my $result = $c->model('Validator')->validate($url);
-                        $batch->add_to_urls({ url => $url->id, result => $result });
-                        $validated_count++;
-                    });
-                } catch {
-                    push @{ $c->flash->{errors} }, $_;
-                }
-            }
-
-            die "Could not validate any URLs" unless $validated_count;
-
-            $batch->update({ url_count => $validated_count });
-        });
-    }
-
-In this example, we are validating a batch of URLs, if any single URL fails it
-will not be added to any tables in the database, if they all fail then nothing
-will be added to the database.
-
-=head2 Transactions, a good idea!
-
-As you can see, the L<DBIx::Class> facilities for database transactions can
-make your code safer and simpler.
-
-=head1 AUTHOR
-
-Caelum: Rafael Kitover <rkitover at cpan.org>
-




More information about the Catalyst-commits mailing list