I've written the following DBIPool.pm to help manage my DBI handles.

Database aware functions need only call the pool with an alias, they dont need to keep the dsn/user/pass/%attrs around.

Also, DBIPool caches dbh's. Once your function goes out of scope, it stores the dbh for later usage.

I appreciate all comments and suggestions. Especially in the AUTOLOAD function. I'm using AUTOLOAD to dispatch the dbh calls to the actual $dbh. I haven't done much AUTOLOADing before and would be grateful for some advice.

Update: rescued from 60's as per grinder's suggestion
package DBIPool; use strict; use warnings; use vars '$AUTOLOAD'; use DBI; my %_Pools; my %_Logins; my $_DefaultAlias = ""; sub DefaultAlias { @_ ? ($_DefaultAlias = shift) : $_DefaultAlias; } sub SetLogin { my %args = ( alias => '', @_ ); $_Logins{$args{alias}} = \%args; } sub GetHandle { my $alias = shift; if ( ! $alias ) { $alias = DefaultAlias(); } # # check pool if there's an available connection # my $pool = _GetPool($alias); my $dbh; if ( @$pool > 0 ) { $dbh = shift @$pool; my $pooledDbi = new DBIPool($alias,$dbh); return $pooledDbi; } # # none exist, create an actual DBI connection # my $login = $_Logins{$alias}; if ( ! $login ) { Errors("dont know how to connect to [$alias]"); return; } $dbh = DBI->connect( $$login{dsn}, $$login{user}, $$login{password}, $login ); if ( ! $dbh ) { Errors("could not to connect to [$alias]"); return; } my $pooledDbi = new DBIPool($alias,$dbh); return $pooledDbi; } sub Errors { foreach (@_) { my ($package, $filename, $line) = caller; my $error = $_ . " at $filename line $line"; print STDERR $error . "\n"; push @_Errors, $error; } wantarray ? return @_Errors : \@_Errors; } sub _GetPool { my $alias = shift; my $pool = $_Pools{$alias}; if ( ! $pool ) { # create a pool my @newPool; $pool = \@newPool; $_Pools{$alias} = $pool; } return $pool; } sub new { my $self = bless( {}, shift ); $self->{alias} = shift; $self->{dbh} = shift; return $self; } sub AUTOLOAD { my $obj = shift; if ( ! ref $obj) { die "Unknown method [$AUTOLOAD]\n"; } if ( $AUTOLOAD =~ /::(.*)$/ ) { my $method = $1; if ( $method ne 'DESTROY') { my $dbh = $obj->{dbh}; if ($dbh->can($method)) { $dbh->$method(@_); } else { die "Unknown method [$method]\n"; } } } } sub DESTROY { my $self = shift; # # save dbh to a pool; # my $pool = _GetPool($self->{alias}); my $dbh = $self->{dbh}; push @$pool, $dbh; } sub END { # # explicitly disconnect and remove all dbh references # foreach my $alias ( keys %_Pools ) { my $pool = _GetPool($alias); while (my $dbh = shift @$pool ) { $dbh->disconnect; $dbh = 0; } } }

and here's and example of how to use it:
my $products_db_dsn = ""; my $products_db_user = ""; my $products_db_password = ""; my $customers_db_dsn = ""; my $customers_db_user = ""; my $customers_db_password = ""; DBIPool::SetLogin( alias => 'products_db', dsn => $products_db_dsn, user => $products_db_user, password => $products_db_password, ); DBIPool::SetLogin( alias => 'customers_db', dsn => $customers_db_dsn, user => $customers_db_user, password => $customers_db_password, ); ProcessAllProducts(); sub ProcessAllProducts { my $dbh = DBIPool::GetHandle('products_db'); my $sql = "select ProductID from Products"; my $sth = $dbh->prepare($sql); if ( $sth->execute ) { while (my $product = $sth->fetchrow_hashref ) { my $productID = $$product{ProductID}; ProcessProductRegions($productID); } } } sub ProcessProductRegions { my $productId = shift; my $dbh = DBIPool::GetHandle('products_db'); my $sql = "select RegionID from ProductRegions where ProductID = ? +"; my $sth = $dbh->prepare($sql); if ( $sth->execute($productId) ) { while (my $region = $sth->fetchrow_hashref ) { my $regionID = $$region{RegionID}; ProcessRegionCustomers($regionID); } } } sub ProcessRegionCustomers { my $regionID = shift; my $dbh = DBIPool::GetHandle('customers_db'); my $sql = "select c.* from RegionCustomers rc, Customers c where +rc.RegionID = ? and c.CustomerID = rc.CustomerID"; my $sth = $dbh->prepare($sql); if ( $sth->execute($regionID) ) { while (my $customer = $sth->fetchrow_hashref ) { # # do some stuff for the customer here # } } }

In reply to Pooling DBI handles by LanceDeeply

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.