[ Team LiB ] |
A.12 Singleton Database HandlesLet's say we have an object we want to be able to access anywhere in the code, without making it a global variable or passing it as an argument to functions. The singleton design pattern helps here. Rather than implementing this pattern from scratch, we will use Class::Singleton. For example, if we have a class Book::DBIHandle that returns an instance of the opened database connection handle, we can use it in the TransHandler phase's handler (see Example A-6). Example A-6. Book/TransHandler.pmpackage Book::TransHandler; use Book::DBIHandle; use Apache::Constants qw(:common); sub handler { my $r = shift; my $dbh = Book::DBIHandle->instance->dbh; $dbh->do("show tables"); # ... return OK; } 1; We can then use the same database handle in the content-generation phase (see Example A-7). Example A-7. Book/ContentHandler.pmpackage Book::ContentHandler; use Book::DBIHandle; use Apache::Constants qw(:common); sub handler { my $r = shift; my $dbh = Book::DBIHandle->instance->dbh; $dbh->do("select from foo..."); # ... return OK; } 1; In httpd.conf, use the following setup for the TransHandler and content-generation phases: PerlTransHandler +Book::TransHandler <Location /dbihandle> SetHandler perl-script PerlHandler +Book::ContentHandler </Location> This specifies that Book::TransHandler should be used as the PerlTransHandler, and Book::ContentHandler should be used as a content-generation handler. We use the + prefix to preload both modules at server startup, in order to improve memory sharing between the processes (as explained in Chapter 10). Book::DBIHandle, shown in Example A-8, is a simple subclass of Class::Singleton that is used by both handlers. Example A-8. Book/DBIHandle.pmpackage Book::DBIHandle; use strict; use warnings; use DBI; use Class::Singleton; @Book::DBIHandle::ISA = qw(Class::Singleton); sub _new_instance { my($class, $args) = @_; my $self = DBI->connect($args->{dsn}, $args->{user}, $args->{passwd}, $args->{options}) or die "Cannot connect to database: $DBI::errstr"; return bless $self, $class; } sub dbh { my $self = shift; return $$self; } 1; Book::DBIHandle inherits the instance( ) method from Class::Singleton and overrides its _new_instance( ) method. _new_instance( ) accepts the connect( ) arguments and opens the connection using these arguments. The _new_instance( ) method will be called only the first time the instance( ) method is called. We have used a reference to a scalar ($dbh) for the Book::DBIHandle objects. Therefore, we need to dereference the objects when we want to access the database handle in the code. The dbh( ) method does this for us. Since each child process must have a unique database connection, we initialize the database handle during the PerlChildInit phase, similar to DBI::connect_on_init( ). See Example A-9. Example A-9. Book/ChildInitHandler.pmpackage Book::ChildInitHandler; use strict; use Book::DBIHandle; use Apache; sub handler { my $s = Apache->server; my $dbh = Book::DBIHandle->instance( { dsn => $s->dir_config('DATABASE_DSN'), user => $s->dir_config('DATABASE_USER'), passwd => $s->dir_config('DATABASE_PASSWD'), options => { AutoCommit => 0, RaiseError => 1, PrintError => 0, ChopBlanks => 1, }, } ); $s->log_error("$$: Book::DBIHandle object allocated, handle=$dbh"); } 1; Here, the instance( ) method is called for the first time, so its arguments are passed to the new _new_instance( ) method. _new_instance( ) initializes the database connection. httpd.conf needs to be adjusted to enable the new ChildInitHandler: PerlSetVar DATABASE_DSN "DBI:mysql:test::localhost" PerlSetVar DATABASE_USER "foo" PerlSetVar DATABASE_PASSWD "bar" PerlChildInitHandler +Book::ChildInitHandler |
[ Team LiB ] |