Using with mod perl
From ClassDBI
UPDATE, June 10, 2007
The information on this page is now obsolete, but is here for reference for people using older versions of Ima::DBI. Anyone using version 0.35 or later of Ima::DBI can use Class::DBI normally with mod_perl. It is recommended that you follow the instructions given in the Ima::DBI documentation for setting up an automatic rollback after each request. Essentially the same instructions are shown at the bottom of this page.
On Tue, 26 Oct 2004 16:19:10 -0400, PerrinHarkins posted a message similar to the following on the mod_perl mailing list, in response to a user having problems getting Apache::DBI and Class::DBI to play nice together in the same scripts:
I plan to submit a patch for Ima::DBI to fix this, but in the meantime I am handling it by overriding db_Main and doing the connections myself.
Note that if you override db_Main like this, you should no longer use the connection() method in your Class::DBI base class.
Here's the code I use:
# Get Class::DBI's default dbh options
my $db_options = { __PACKAGE__->_default_attributes };
__PACKAGE__->_remember_handle('Main'); # so dbi_commit works
# override default to avoid using Ima::DBI closure
sub db_Main {
my $dbh;
if ( $ENV{'MOD_PERL'} and !$Apache::ServerStarting ) {
$dbh = Apache->request()->pnotes('dbh');
}
if ( !$dbh ) {
# $config is my config object. replace with your own settings...
$dbh = DBI->connect_cached(
$config->get('DbDSN'), $config->get('DbUser'),
$config->get('DbPass'), $db_options
);
if ( $ENV{'MOD_PERL'} and !$Apache::ServerStarting ) {
Apache->request()->pnotes( 'dbh', $dbh );
}
}
return $dbh;
}
I stashed the handle in pnotes() for some extra speed because Class::DBI requests it so frequently. Stashing in pnotes() is safe because it gets cleared out at the end of each request even if your code dies.
UPDATE: Simplified to use _remember_handle() call as suggested by Chris Hutchison.
-- PerrinHarkins
UPDATE: Moved _remember_handle() out of db_Main to prevent the handle list from growing out of control. Each time _remember_handle() is called it pushes the handle name on to a array stored in Ima::DBI. This list is used when calling Ima::DBI->commit, which is what is used in dbi_commit. (Previously, if you called db_Main five times, DBI->commit would also be called five times when calling dbi_commit.)
-- DavidJackOlrik
On Thu, 10 Feb 2005 18:42:35 -0500, Edward Sabol posted the following reply to the mailing list:
For getting automatic rollbacks working, I propose the following solution which requires modifying Apache::DBI. First, add the following sub to Apache/DBI.pm:
sub modperl_cleanup {
my $prefix = "$$ Apache::DBI ";
print STDERR "$prefix PerlCleanupHandler \n" if $Apache::DBI::DEBUG > 1;
my $dbh = $Connected{$Idx};
if ($dbh and $dbh->{Active} and !$dbh->{AutoCommit} and eval {$dbh->rollback}) {
print STDERR "$prefix PerlCleanupHandler rollback for $Idx \n" if $Apache::DBI::DEBUG > 1;
}
1;
}
You might notice that this is rather similar to Apache::DBI::cleanup(), but the logic is somewhat different. Apache::DBI::cleanup() only rolls back when $Apache::DBI::Rollback{$Idx} is set which, due to the way Ima::DBI works, only happens on the first request.
Then, in your mod_perl httpd.conf file, add the following:
PerlCleanupHandler Apache::DBI::modperl_cleanup
That fix should work with all DBI connections, not just Class::DBI stuff, I believe.
In an off-list e-mail discussion that Perrin and I had on this topic a few months ago, Perrin suggested a simpler solution:
$r->push_handlers(PerlCleanupHandler => sub {
MyClassDBI->dbi_rollback();
});
-- Edward Sabol
NOTE: This is not needed if you are using the custom db_Main code above, since it lets Apache::DBI continue to work for this.
If you try to load your Class::DBI modules with a PerlModule directive in httpd.conf eg:
PerlModule My::CDBI::Subclass
You may get an error like this:
foo method already exists in My::CDBI::Subclass at /usr/share/perl5/Class/DBI/Relationship/HasMany.pm line 13
This is a problem with PerlModule, which does not behave quite like "use" and tries to load the module twice. The solution is to not use PerlModule for preloading, and just put this in a startup.pl file instead:
use My::CDBI::Subclass;
Alternately, do this in your httpd.conf:
<Perl> use Domain::DBI; </Perl>
-- PerrinHarkins

