NAME

Activator::DB - Wrap DBI with convenience subroutines and consistant access accross all programs in a project.

Synopsis

use Activator::DB;
my $db = Activator::DB->connect('default'); # connect to default db
  • Get a single row:

    my @row     = $db->getrow( $sql, $bind, @args );
    my $rowref  = $db->getrow_arrayref( $sql, $bind, @args );
  • Get hashref of col->value pairs:

    my $hashref = $db->getrow_hashref( $sql, $bind, @args );
  • Get all rows arrayref (these are identical):

    my $rowsref = $db->getall( $sql, $bind, @args );
    my $rowsref = $db->getall_arrayrefs( $sql, $bind, @args );
  • Get all rows ref: with each row a hashref of cols->value pairs:

    my $rowsref = $db->getall_hashrefs( $sql, $bind, @args );
  • do any query ( usually INSERT, DELETE, UPDATE ):

    my $id = $db->do( $sql, $bind, @args );
  • do query, but return id instead of success.:

     my $id = $db->do_id( $sql, $bind, @args );
    ( NOTE: this is very mysql dependant at the moment)
  • Get data from a different db for a while:

    $db->connect('alt'); # connect to alternate db
    # do something
    
    $db->connect('def'); # reset to default connection
    # do something else
  • Transactions (NOT YET IMPLEMENTED)::

    my $altdb = Activator::DB->connect('altdb');
    $db->begin_work();
    $db->do( @stuff );
    $db->do( @more_stuff );
    $db->commit();

DESCRIPTION

Activator::DB module provides convenience and total consistency to accessing a database throughout a project. The idea is to reduce typing for the common cases, and remove worrying about connections. This module is a wrapper for DBI providing these advantages:

  • Provides connect string aliases centrally configured.

  • Provide consistent arguments handling to all query functions.

  • Connection caching without Apache::DBI (allows use of library/model code in crons AND website).

  • Connection and query debug dumps using your project or module level Activator::Log config, or on a per-query basis.

  • Allows all code in your project/team/company to access the db in a consistent fashion.

  • By default, dies on all errors enforcing try/catch programming

  • Implemented as a singleton so each process is guranteed to be using no more than one connection to each database from the pool.

Disadvantages:

  • If you know DBI, you don't necessarily know Activator::DB

  • NOT THREAD SAFE

  • Only tested with MySql

CONFIGURATION

This module uses Activator::Registry to automatically choose default databases, and Activator::Log to log warnings and errors.

Registry Setup (from Activator::Registry)

This module expects an environment variable ACT_REG_YAML_FILE to be set. If you are utilizing this module from apache, this directive must be in your httpd configuration:

SetEnv ACT_REG_YAML_FILE '/path/to/config.yml'

If you are using this module from a script, you need to insure that the environment is properly set using a BEGIN block:

BEGIN{
    $ENV{ACT_REG_YAML_FILE} ||= '/path/to/config.yml'
}

Registry Configuration

Add an Activator::DB section to your project YAML configuration file:

'Activator::Registry':
   log4perl<.conf>:         # Log4perl config file or definition
                            # See Logging Configuration below
  'Activator::DB':
    default:                # default configuration for all connections
      connection: <conn_alias>

  ## The rest of this section is optional
      config:
        debug:      0/1     # default: 0, affects all queries, all aliases
        reconn_att: <int>   # attempt reconnects this many times. default: 3
        reconn_sleep: <int> # initial sleep seconds between reconnect attempts.
                            # doubles every attempt. default: 1
      attr:                 # connection attributes. Only AutoCommit at this time
        AutoCommit: 0/1     # default: 1

  ## You must define at least one connection alias
    connections:
      <conn_alias>:
        user: <user>
        pass: <password>
        dsn: '<DSN>' # see perldoc DBI for descriptions of valid DSNs
                     # MySql Example: DBI:mysql:<DBNAME>:<DBHOST>

  ## These attributes and config are all optional, and use the default from above
        attr:
          AutoCommit: 0/1
        config:
           debug:     0/1   # only affects this connection

USAGE

This module can be used either pseudo-OO or static on multiple databases. I say pseudo-OO, because you don't call new: this module auto-vivicates a singleton object whenever you connect for the first time.

## pseudo-OO example:
my $db = Activator::DB->connect( 'db_alias' );
$db->query_method( $sql, $bind, @args );
$db->connect( 'alt_db_alias' );
$db->query_method( $sql, $bind, @args );
$db->connect( 'db_alias' );
$db->query_method( $sql, $bind, @args );
## Static formatted calls require that you dictate the connection for every request. So, the above can also be done as:
Activator::DB->query_method( $sql, $bind, connect => 'db_alias', @args );
Activator::DB->query_method( $sql, $bind, connect => 'alt_db_alias', @args );
Activator::DB->query_method( $sql, $bind, connect => 'db_alias', @args );
## However, the common use case for this module is:
my $db = Activator::DB->connect( 'db_alias' );
$db->query_method( $sql, $bind, @args );
  ### do some perl
$db->query_method( $sql, $bind, @args );
  ### do some perl
$db->query_method( $sql, $bind, @args );
  ### do some perl
... etc.

connect() Usage

my $db = Activator::DB->connect('my_db');   # connect to my_db
my $db->connect('default'); # connect to default db
my $db->connect('def');     # shortcut to default db
my $db->connect();          # shortercut to default db

connect() Caveat

Note that connect() always returns the singleton object, which in some usage patterns could cause some confusion:

my $db1->connect('db1');           # connect to db1
$db1->query( $sql, $bind, @args ); # acts on db1
my $db2->connect('db2');           # connect to db2
$db2->query( $sql, $bind, @args ); # acts on db2
$db1->query( $sql, $bind, @args ); # still acts on db2!

For this reason, it is highly recommended that you always use the same variable name (probably $db) for the Activator::DB object.

Query Methods Usage

Every query function takes named arguments in the format of:

Activator::DB->$query_method( $sql, $bind, opt_arg => <opt_value> );

Mandatory Arguments:

sql   : sql statement string
bind  : bind values arrayref

Optional Arguments: conn => alias of the db connection (default is 'default') NOTE: this changes the connection alias for all future queries attr => hashref of attributes to use for ONLY THIS QUERY Supported: AutoCommit debug => pretty print sql debugging lines

# NOT YET SUPPORTED
slice     => possible future support for DBI::getall_hashref
max_rows  => possible future support for DBI::getall_hashref

Examples:

## Simple query:
my @row = $db->getrow( $sql );
## Needy query:
my $res = $db->do( $sql, $bind,
      connect => 'altdb', # changes the alias for future connections!
      attr => { AutoCommit => 0, },
      debug => 1,
 );

Query Failures & Errors

All query methods die on failure, and must be wrapped in a try/catch block.

eval {
  Activator::DB->query_method( $sql, $bind, @args );
};
if ($@) {
  # catch the error
}

We highly recommend (and use extensively) Exception::Class::TryCatch which allows this syntactic sugar:

try eval {
  Activator::DB->query_method( $sql, $bind, @args );
};
if ( catch my $e ) {
   # rethrow, throw a new error, print something, AKA: handle it!
}

Errors Thrown:

connection failure         - could not connect to database
sql missing                - query sub called without 'sql=>' argument
connect missing            - static call without 'connect=>' argument
prepare failure            - failure to $dbh->prepare
execute failure            - failure to $dbh->execute
alias_config missing       - connection alias has no configuration
activator_db error         - sub _warn_or_die() died without error args passed in
fetch failure              - $sth->fetch* call failed
do failure                 - $dbh->do call failed

METHODS

getrow

getrow_arrayref

getrow_hashref

Prepare and Execute a SQL statement and get a the result of values back via DBI::fetchrow_array(), DBI::fetchrow_arrayref(), DBI::fetchrow_hashref() respectively. NOTE: Unlike DBI, these return empty array/arrayref/hashref (like DBI::fetchall_arrayref does, instead of undef) when there are no results.

Usage:

my @row     = $db->getrow( $sql, $bind, @args )
my $rowref  = $db->getrow_arrayref( $sql, $bind, @args )
my $hashref = $db->getrow_hashref( $sql, $bind, @args )

getall

getall_arrayrefs

getall_hashrefs

Prepare and Execute a SQL statement, and return a reference to the result obtained by DBI::fetchall_arrayref(). Returns an empty arrayref if no rows returned for the query.

  • getall() is an alias for getall_arrayrefs() and they both return an arrayref of arrayrefs, one arrayref of values for each row of data from the query.

    $rowrefs is [ [ row1_col1_val, row1_col2_val ], 
                  [ row2_col1_val, row2_col2_val ],
                ];
  • getall_hashrefs() returns an arrayref of of rows represented by hashrefs of column name => value mappings.

    $rowrefs is [ { col1 => val, col2 => val },
                  { col1 => val, col2 => val },
                ];
my $rowref = $db->getall( $sql, $bind, @args )
my $rowref = $db->getall_arrayrefs( $sql, $bind, @args )
my $rowref = $db->getall_hashrefs( $sql, $bind, @args )

do

Execute a SQL statement and return the number of rows affected. Dies on failure.

Usage:

my $res = $db->do( $sql, $bind, @args )

do_id

Execute a SQL statement that generates an id and return the id. Dies on failure.

Usage:

my $id = $db->do_id( $sql, $bind, @args )

SEE ALSO

DBI, Activator::Registry, Activator::Log, Activator::Exception, Exception::Class::DBI, Class::StrongSingleton, Exception::Class::TryCatch

AUTHOR

Karim Nassar

COPYRIGHT

Copyright (c) 2007 Karim Nassar <karim.nassar@acm.org>

You may distribute under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file.