I love Class::DBI for the usual reason - ability to gain abstraction by un-coupling all of the elements needed for database access. And I hate it for what I suspect are common reasons (at least for SQL freaks like me) - all of those ugly package declarations everywhere, thinking in terms of has_a and has_many syntax instead of SQL joins, having to redefine columns and primary keys (they're already in the SQL DDL, why do I need to re-specify them), using funky $class->create($data_hashref) and $class->retrieve($query_hashref) instead of SQL inserts and selects. Well, being a have-my-cake-and-eat-it-too kinda guy, I decided to try to code an example of a join that does not have any hard-coded package declarations, that does not use has_a and has_many, that does not require redefining columns and primary keys and that puts more emphasis on the SQL statements. I am not unpleased with the results shown below. I take back the bit about hating Class::DBI, I can use it without using the parts I find annoying.

My questions for the monks are these: what have I lost by using Class::DBI this way? Am I stuck in my SQL ways and just unable to see the light? Are there other, better, ways to accomplish what this script does?

The script below is pure Class::DBI, but foregoes the use of some of the more common methods such as has_a, has_many, create, retrieve. It makes use of two generic subroutines which also don't utilize those methods - get_config(), a quick hack to grab both SQL and data from the script's DATA section and setup_class(), a tiny factory that obviates the need for hard-coded package declarations. These subroutines are generic, they should work with almost any other schema although the get_config() would obviously need to be tweaked for file or database storage of the SQL statments and data.

The data set is a Professor table and a Subject table with the prof's id as the common key. The script joins the two tables and prints out a list of subjects taught by each professor. The script uses DBD::AnyData but it should work with any DBD that supports explicit join syntax. I tested with DBD::SQLite and DBD::Pg and it works fine with both.

-- start of cdbi.pl -- #!perl -w use strict; require 'setup-class.pl'; my @connect_args = ( 'dbi:AnyData:','me','mypass',{} ); my( $sql, $data ) = get_config(qw(Prof Subject)); for my $table(qw(Prof Subject)) { setup_class ( $table, $sql->{$table}, @connect_args ); eval { $table->sql_drop->execute }; $table->sql_create->execute; $table->sql_insert->execute( @$_ ) for ( @{$data->{$table}} ); } for my $row(Prof->search_query) { printf "Professor %s teaches %s.\n",$row->{pname},$row->{cname}; } $_->sql_drop->execute for qw(Prof Subject); __DATA__ create = CREATE TABLE Prof (pid INT PRIMARY KEY, pname VARCHAR(10)) insert = INSERT INTO Prof (pid,pname) VALUES(?,?) query = SELECT pname,cname FROM Prof NATURAL JOIN Subject drop = DROP TABLE Prof 1,Jones 2,Smith create = CREATE TABLE Subject(cid INT PRIMARY KEY,pid INT,cname VARCHA +R(10)) insert = INSERT INTO Subject(cid,pid,cname) VALUES(?,?,?) drop = DROP TABLE Subject 1,1,Chemistry 2,1,Biology 3,2,English -- end of cdbi.pl -- -- start of setup-class.pl -- use warnings; use strict; use Class::DBI; sub get_config { my @config = split /\n\n/,join'',<DATA>; my(%sql,%data); for my $table(@_) { my $sql_str = shift @config; my $data_str = shift @config; for my $stmt(split /\n/,$sql_str) { my($key,$value) = split /\s*=\s*/,$stmt,2; $sql{$table}->{$key}=$value; } for my $row(split /\n/,$data_str) { push @{ $data{$table} }, [split /,/,$row]; } } return \%sql, \%data; } sub setup_class { my($class,$cfg,@connect_args) = @_; my $table = delete $cfg->{table} || $class; my @columns; my $colstr = delete $cfg->{columns}; if ($colstr) { @columns = split /,/,$colstr; } elsif (my $create_str=$cfg->{create}) { $create_str =~ s/^[^\(]+\((.*)\)\s*$/$1/; @columns = map {s/^\s*(\S+)\s.*/$1/; $_} split/,/,$create_str; } else { die "No columns specified for table '$table'!\n"; } eval "Package $class"; no strict 'refs'; @{"$class\::ISA"} = ('Class::DBI'); $class->connection( @connect_args ); $class->table ( $table ); $class->columns ( All=>@columns ); $class->set_sql( $_ => $cfg->{$_}, undef, 0 ) for keys %$cfg; } 1; -- end of setup-class.pl --

In reply to Class::DBI - my way is not very sportsmanlike by jZed

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.