----
presentation_topic: dbic_chado
presentation_title: Introduction to DBIx::Class
presentation_subtitle:
presentation_place: Portland, OR
presentation_date: April 14, 2010
----
= What is DBIx::Class?
* Object-relational mapping framework for Perl
* is now the de-facto standard
* nice features
----
= What is DBIx::Class?
* query building (the magic of chainable ResultSets)
* cross-database deployment (using |SQL::Translator| in the backend)
* lots and lots of plugins availabe (dates, testing, ...)
----
= What it looks like
package My::Schema;
__PACKAGE__->load_namespaces;
package My::Schema::Result::Foo;
__PACKAGE__->table('foo');
__PACKAGE__->add_columns(qw( foo_id bar baz ));
__PACKAGE__->has_many('bars' => 'My::Schema::Result::Bar');
# also belongs_to many_to_many
package My::Schema::Result::Bar;
__PACKAGE__->table('bar');
# and so on
* use |DBIx::Class::Schema::Loader| to generate this from a DB
* use |$schema->deploy| to make a DB from this
----
= Basic Usage
* open the schema. ( actual DB connection is deferred )
+ my $schema = My::Schema->connect( 'dbi:Pg:...', $user, $pass );
+* get a ResultSet object representing a set of Rows
+ my $orgs_rs = $schema->resultset('Organism');
+* take a subset of those (another ResultSet)
+ my $chickeny_things = $orgs_rs->search({ genus => 'Gallus' });
+* first one in the result set
+ my $chicken = $chickeny_things->first; #< actual query is run
----
= Basic Usage
+* getting actual data
+ my $wombat = $orgs_rs->find( 232432 );
+ say $wombat->genus.' '.$wombat->species; #< print 'Vombatus ursinus' or something
# or search in list context returns all resulting rows
my @all_nightshades = $orgs_rs->search({ genus => 'Solanum' });
----
= Basic Usage
* traverse relationships (joined queries)
+ my $chickeny_things = $ck_rs->search({ genus => 'Gallus' });
+ my @chr = $chickeny_things->chromosomes; #< query is run here
+ my @genes = $chicken->chromosomes->features->search({ type => 'gene' });
+* loading
+ $orgs_rs>create({ genus => 'Mus', species => 'musculus' })
+ # or piecewise
+ my $new = $orgs_rs->new;
$new->genus('Mus'); $new->species('musculus');
$new->insert;
+* deleting
$chicken->chromosomes->features->search({ type => 'gene' })->delete;
----
= When not to use DBIC
* tiny app, tiny schema (setup overhead)
+* or tiny number of different queries
+* or hate objects
strengths/weaknesses ...
----
= Strengths and Weaknesses
* strengths
+** chainable ResultSets
+** lazy querying
+** good support for custom SQL
+** prefetching
+** lots of nice extensions
+* weaknesses
+** non-ORM-y things are possible, but can be cumbersome
----
= Chainable ResultSets
my $styx_tracks =
$music_schema->resultset('Artist')
->search({ name => 'Styx' })
->albums
->tracks;
SELECT ...
FROM artist
JOIN albums ON ...
JOIN tracks ON ...
WHERE artist.name = 'Styx'
----
= Lazy Querying
* does not run the query until data is actually needed
# fetch all
my @tracks = $styx_tracks->all;
# or iterate
while( my $track = $styx_tracks->next ) { ... }
----
= Custom SQL
* can make a view |ResultSource| for big tuned queries
package My::Schema::SummarizedFrobs;
__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
__PACKAGE__->result_source_instance->view_definition(<<'');
SELECT omg_so_much_stuff
UNION
SELECT ( SELECT blah blah blah FROM blah ),
( blah blah blah ),
WHERE blah blah blah
UNION
blah blah blah
__PACKAGE__->add_column( <cols in your view> )
----
= Custom SQL
* for custom conditions, searches accept |SQL::Abstract| syntax
$schema->resultSet('Sequence::Feature')
->search({
'me.feature_id' =>
\[ "IN( select feature_id from clone_feature where clone_id = ?)",
[ dummy => $self->clone_id ],
],
},
{ rows => 10,
order_by => [qw[ me.name me.type_id ]],
having
});
----
= Prefetching
# query will get the wombat organism, and also prefetch *all its
# chromosomes and features on those chromosomes*
my $rs = $schema->resultset('Organism')->search(
{ genus => 'Vombatus', species => 'ursinus' },
{ prefetch => { chromosomes => features => } },
);
(in a real biological DB this would probably blow your memory)
----
= Extensions
* |DBIx::Class::Helpers|
** |fREW|'s miscellaneous things
+* |DBIx::Class::Cursor::Cached|
** tunable, flexible resultset caching
+* |DBIx::Class::Ordered|
** nice for doing ordering or ranking columns in a table
+* |DBIx::Class::InflateColumn::DateTime|, |DBIx::Class::Timestamp|
** work with dates, times, and timestamps as |DateTime| objects
----
= Thread-Safe / Fork-Safe
* |theory|'s |DBix::Connector|
----
= Non-linear Join
# get features via the potato organism, also joining in the cvterms table
my $potato_bacs =
$schema->resultset('Organism')
->search({ species => 'Solanum tuberosum' })
->search_related( 'features',
{ 'type.name' => 'BAC_clone'},
{ 'join' => 'type' },
);
----
= Non-linear Join
# the equivalent bare SQL
my $potato_bacs = $dbh->selectall_arrayref( <<EOS, undef, 'Solanum tuberosum', 'BAC_clone');
SELECT features.feature_id
, features.dbxref_id
, features.organism_id
, features.name
, features.uniquename
, features.residues
, features.seqlen
, features.md5checksum
, features.type_id
, features.is_analysis
, features.is_obsolete
, features.timeaccessioned
, features.timelastmodified
FROM organism me
LEFT JOIN feature features
ON features.organism_id = me.organism_id
JOIN cvterm type
ON type.cvterm_id = features.type_id
WHERE species = ? AND type.name = ?
EOS
----
= Convenient Loading of Relationships
$chado->resultset( 'Cv::Cv' )
->find_or_create({ name => 'My Fake Ontology' })
->create_related( 'cvterm',
{ name => 'MyFakeTerm' });
makes the SQL:
SELECT me.cv_id
, me.name
, me.definition
FROM cv me
WHERE ( me.name = 'my fake ontology' )
INSERT INTO cv ( name )
VALUES ( 'my fake ontology' )
RETURNING cv_id
INSERT INTO cvterm ( cv_id, name )
VALUES ( ?, 'MyFakeTerm' )
----
= Transactions
$schema->txn_do(sub {
$schema->resultset('Cv::Cv')
->find_or_create({ name => 'My Fake Ontology' })
->create_related( 'cvterm', { name => 'MyFakeTerm' } );
});
----
= The Real Advantages of DBIC
* easier to manipulate and assemble queries
----
= The Real Advantages of DBIC
* complex joined queries (Chado queries) are very easy and compact
----
= The Real Advantages of DBIC
* SQL syntax errors are more difficult to make
----
= The Real Advantages of DBIC
* it's all objects. you can delegate to them, pass them around, etc.
* HOWEVER:
+** usually you don't want to subclass them
** but, see |DBIx::Class::Manual::Cookbook|
----
== That's All
* The END