DBIx-DataModel v2.0 in detail

6,854 views

Published on

DBIx-DataModel is an object-relational mapping framework for Perl5. Schema declarations are inspired from UML modelling. The API provides efficient interaction with the DBI layer, detailed control on statement execution steps, flexible and powerful treatment of database joins. More on http://search.cpan.org/dist/DBIx-DataModel.
Talk presented at YAPC::EU::2011 Riga (updated from a previous version presented at FPW2010).

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
6,854
On SlideShare
0
From Embeds
0
Number of Embeds
206
Actions
Shares
0
Downloads
51
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

DBIx-DataModel v2.0 in detail

  1. 1. DBIx::DataModel 2.0 in detail YAPC::EU::2011, Riga [email_address] Département Office
  2. 2. Agenda <ul><li>Introduction : Object-Relational Mappings & UML </li></ul><ul><li>DBIx::DataModel 2.0 Architecture </li></ul><ul><li>Modelling the schema </li></ul><ul><li>Selecting data </li></ul><ul><li>Row objects and statement objects </li></ul><ul><li>Joins </li></ul><ul><li>Inserts and updates </li></ul><ul><li>Customization </li></ul><ul><li>Strengths and limitations of DBIx::DataModel </li></ul>today on CPAN : v 1.99_05
  3. 3. Object-Relational Mappers
  4. 4. Perl and databases Database DBD driver DBI Object-Relational Mapper Perl program
  5. 5. ORM principle r1 r2 ... c1 c2 c3 ... c3 c4 +c1: String +c2: String +c3: class2 r1 : class1 RDBMS r2 : class1 RAM table1 table2
  6. 6. Impedance mismatch <ul><li>SELECT c1, c2 FROM table1 </li></ul><ul><ul><ul><li> missing c3 , so cannot navigate to class2 </li></ul></ul></ul><ul><ul><ul><li>is it a valid instance of class1 ? </li></ul></ul></ul><ul><li>SELECT * FROM table1 LEFT JOIN table2 ON … </li></ul><ul><ul><ul><li> is it a valid instance of class1 ? </li></ul></ul></ul><ul><ul><ul><li> what to do with the c4 column ? </li></ul></ul></ul><ul><li>SELECT c1, c2, length(c2) AS l_c2 FROM table1 </li></ul><ul><ul><ul><li> no predeclared method in class1 for accessing l_c2 </li></ul></ul></ul>c1 c2 c3 c3 c4 +c1: String +c2: String +c3: class2 r1 : class1 RDBMS RAM table1 table2
  7. 7. The Unified Modeling Language (UML)
  8. 8. Example : CPAN model Author Distribution Module 1 * 1 * * multiplicity role class association dependent _distribs * prereq _modules contains ► assoc. name composition depends_on ►
  9. 9. Modelling a schema
  10. 10. Architecture Schema Source Table Join Statement My::DB My::DB::Table_n My::DB::AutoJoin:: row statement row DBIx::DataModel classes application classes objects schema  quite similar to DBI architecture (dbh, sth)
  11. 11. All definitions in one single file <ul><li>use DBIx::DataModel; </li></ul><ul><li>DBIx::DataModel-> Schema (&quot;My::DB&quot;) </li></ul><ul><li>-> Table (qw/Author author author_id /) </li></ul><ul><li>->Table(qw/Distribution distribution distrib_id/) </li></ul><ul><li>->Table(qw/Module module module_id /) </li></ul><ul><li>-> Association ([qw/Author author 1 /], </li></ul><ul><li>[qw/Distribution distribs 0..* /]) </li></ul><ul><li>-> Composition ([qw/Distribution distrib 1 /], </li></ul><ul><li>[qw/Module modules 1..* /]); </li></ul>creates package My::DB creates package My::DB::Author adds methods into both packages
  12. 12. Multiplicities <ul><li>&quot;$min..$max&quot; </li></ul><ul><li>&quot;*&quot; means &quot;0..POSIX::INT_MAX&quot; </li></ul><ul><li>&quot;1&quot; means &quot;1..1&quot; </li></ul><ul><li>$min == 0 ? joins are &quot;LEFT OUTER JOIN&quot; </li></ul><ul><li> : joins are &quot;INNER JOIN&quot; </li></ul><ul><li>$max > 1 ? default result is a list </li></ul><ul><li>: default result is a single object </li></ul>
  13. 13. Meta-Architecture Schema Table Join My::DB My::DB::Table_n My::DB::Auto_join Meta::Source Meta::Table Meta::Join meta::table meta::join meta::schema Meta::Schema Meta::Path Meta::Association Meta::Type meta::assoc meta::path meta::type
  14. 14. Data retrieval
  15. 15. Fetching one single record <ul><li># fetch from primary key </li></ul><ul><li># by default, retrieves all columns ('*') </li></ul><ul><li>my $author = My::DB-> table ('Author')-> fetch ( 'DAMI' ); </li></ul><ul><li># reach columns through the hashref API </li></ul><ul><li>while (my ($k, $v) = each %$author) { </li></ul><ul><li>print &quot;$k : $vn&quot;; </li></ul><ul><li>} </li></ul>
  16. 16. Multi-schema mode <ul><li># create a schema </li></ul><ul><li>my $dbh = DBI->connect(… ): </li></ul><ul><li>my $schema = My::DB->new(dbh => $dbh); </li></ul><ul><li># fetch data </li></ul><ul><li>my $author = $schema-> table ('Author')-> fetch ( 'DAMI' ); </li></ul>
  17. 17. Fetching a list of records <ul><li># select multiple records </li></ul><ul><li>my $recent_distribs = My::DB->table('Distribution') </li></ul><ul><li>-> select ( </li></ul><ul><li>-columns => [qw/ distrib_name d_release /], </li></ul><ul><li>-where => { d_release => {'>' => $some_date}}, </li></ul><ul><li>-order_by => [qw/ -d_release +distrib_name /], </li></ul><ul><li>); </li></ul><ul><li>foreach my $distrib (@$recent_distribs) { </li></ul><ul><li>... </li></ul><ul><li>} </li></ul>
  18. 18. Select API : overview <ul><li>my $result = $source-> select ( </li></ul><ul><li>-columns => @columns, , </li></ul><ul><li>-where => %where, </li></ul><ul><li>-group_by => @groupings, </li></ul><ul><li>-having => %criteria, </li></ul><ul><li>-order_by => @order, </li></ul><ul><li>-for => 'read only', </li></ul><ul><li>-post_SQL => sub { … }, </li></ul><ul><li>-pre_exec => sub { … }, </li></ul><ul><li>-post_exec => sub { … }, </li></ul><ul><li>-post_bless => sub { … }, </li></ul><ul><li>-page_size => …, -page_index => …, </li></ul><ul><li>-limit => …, -offset => …, </li></ul><ul><li>-column_types => %types, </li></ul><ul><li>-result_as => 'rows' || 'sth' || 'sql' </li></ul><ul><li>|| 'statement' || 'hashref' </li></ul><ul><li>); </li></ul>
  19. 19. Arguments to select()
  20. 20. SQL::Abstract::More : named parameters <ul><li>my $result = $source-> select ( </li></ul><ul><li>-columns => @columns, , </li></ul><ul><li>-where => %where, </li></ul><ul><li>-order_by => @order, </li></ul><ul><li>); </li></ul><ul><li> SQL::Abstract->new ->select($table, @columns, %where, @order) </li></ul>
  21. 21. SQL::Abstract::More : extensions <ul><li>-columns => [qw/col1 |alias1 max(col2) |alias2 /] </li></ul><ul><ul><li>SELECT col1 AS alias1, max(col2) AS alias2 </li></ul></ul><ul><li>-columns => [ -DISTINCT => qw/col1 col2 col3/] </li></ul><ul><ul><li>SELECT DISTINCT col1, col2, col3 </li></ul></ul><ul><li>-order_by => [qw/col1 + col2 – col3/] </li></ul><ul><ul><li>SELECT … ORDER BY col1, col2 ASC, col3 DESC </li></ul></ul><ul><li>-for => &quot;update&quot; || &quot;read only&quot; </li></ul><ul><ul><li>SELECT … FOR UPDATE </li></ul></ul>
  22. 22. Grouping <ul><li>-group_by => [qw/col1 col2 …/] </li></ul><ul><li>-having => { col1 => {&quot;<&quot; => val1} , col2 => ... } </li></ul><ul><ul><li>SELECT … GROUP BY col1, col2 HAVING col1 < ? AND col2 … </li></ul></ul><ul><li> separate call to SQL::Abstract and re-injection into the SQL </li></ul>
  23. 23. Paging <ul><li>-page_size => $num_rows, -page_index => $page_index </li></ul><ul><li># or </li></ul><ul><li>-limit => $num_rows, -offset => $row_index </li></ul><ul><li> either new call to $sth->execute(), or use scrollable cursors (DBIx.:DataModel::Statement::JDBC) </li></ul>starts at 1 starts at 0
  24. 24. Callbacks <ul><li>-post_SQL => sub { … }, </li></ul><ul><li>-pre_exec => sub { … }, </li></ul><ul><li>-post_exec => sub { … }, </li></ul><ul><li>-post_bless => sub { … }, </li></ul><ul><li>hooks to various states within the statement lifecycle (see later) </li></ul><ul><li>sometimes useful for DB-specific features </li></ul>
  25. 25. Polymorphic result <ul><li>-result_as => </li></ul><ul><ul><li>'rows' (default) : arrayref of row objects </li></ul></ul><ul><ul><li>'firstrow' : a single row object (or undef) </li></ul></ul><ul><ul><li>'hashref' : hashref keyed by primary keys </li></ul></ul><ul><ul><li>[hashref => @cols] : cascaded hashref </li></ul></ul><ul><ul><li>'flat_arrayref' : flattened values from each row </li></ul></ul><ul><ul><li>'statement' : a statement object (iterator) </li></ul></ul><ul><ul><li>'fast_statement' : statement reusing same memory </li></ul></ul><ul><ul><li>'sth' : DBI statement handle </li></ul></ul><ul><ul><li>'sql' : ($sql, @bind_values) </li></ul></ul><ul><ul><li>'subquery' : [&quot;($sql)&quot;, @bind] </li></ul></ul>  don't need method variants : select_hashref(), select_arrayref(), etc.
  26. 26. Row objects
  27. 27. A row object … <ul><li>is just a hashref </li></ul><ul><ul><li>keys are column names </li></ul></ul><ul><ul><li>values are column values </li></ul></ul><ul><ul><li>nothing else </li></ul></ul><ul><ul><ul><li>actually, when in multi-schema mode, there is an additional __schema field </li></ul></ul></ul><ul><li>is blessed into the table class </li></ul><ul><ul><li>has a metadm method (accessor to the metaclass) </li></ul></ul><ul><ul><li>has a schema method (accessor to the schema) </li></ul></ul><ul><ul><li>has methods for navigating to related tables </li></ul></ul><ul><li>can be dumped as is </li></ul><ul><ul><li>to Dumper / YAML / JSON / XML </li></ul></ul><ul><ul><li>to Perl debugger </li></ul></ul>
  28. 28. Columns … <ul><li>basically, are plain scalars, not objects </li></ul><ul><li>but can be &quot;inflated/deflated&quot; through a Type() </li></ul><ul><li>programmer chooses the column list, at each select() </li></ul><ul><ul><ul><li>-columns => @columns # arrayref </li></ul></ul></ul><ul><ul><ul><li>-columns => &quot; col1, col2 &quot; # string </li></ul></ul></ul><ul><ul><ul><li>-columns => &quot; * &quot; # default </li></ul></ul></ul><ul><li>objects have variable size ! </li></ul><ul><ul><li>if missing keys : runtime error </li></ul></ul><ul><ul><ul><li>when following joins </li></ul></ul></ul><ul><ul><ul><li>when updating and deleting </li></ul></ul></ul>
  29. 29. Navigation to associated tables <ul><li>Method names come from association declarations </li></ul><ul><li>Exactly like a select () </li></ul><ul><ul><li>automatically chooses –result_as => 'rows' || 'firstrow' from multiplicity information </li></ul></ul><ul><li># ->Association([qw/Author author 1 /], </li></ul><ul><li># [qw/Distribution distribs 0..* /]) </li></ul><ul><li>my $author = $distrib-> author (); </li></ul><ul><li>my $other_distribs = $author-> distribs ( </li></ul><ul><li>-columns => [qw/. . ./], </li></ul><ul><li>-where => { . . . }, </li></ul><ul><li>-order_by => [qw/. . ./], </li></ul><ul><li>); </li></ul>
  30. 30. Statement objects
  31. 31. Statement: an encapsulated query statement meta::source My::Source 1 1 * * schema dbh 0..1 * row next() / all() My::Schema meta::schema in single-schema mode in multi-schema mode singleton()
  32. 32. Statement lifecycle new sqlized prepared executed schema + source data row(s) new() sqlize() prepare() execute() bind() refine() bind() bind() bind() execute() next() / all() blessed column types applied -post_bless -pre_exec -post_exec -post_SQL
  33. 33. When to explicitly use a statement <ul><li>as iterator </li></ul><ul><ul><ul><li>my $statement = $source->select(..., -result_as => 'statement'); </li></ul></ul></ul><ul><ul><ul><li>while (my $row = $statement-> next ) { </li></ul></ul></ul><ul><ul><ul><li>. . . </li></ul></ul></ul><ul><ul><ul><li>} </li></ul></ul></ul><ul><li>for paging </li></ul><ul><ul><ul><li>$statement-> goto_page (123); </li></ul></ul></ul><ul><li>for loop efficiency </li></ul><ul><ul><ul><li>my $statement = My::Table-> join (qw/role1 role2/); </li></ul></ul></ul><ul><ul><ul><li>$statement- > prepare (-columns => ..., </li></ul></ul></ul><ul><ul><ul><li>-where => ...); </li></ul></ul></ul><ul><ul><ul><li>my $list = My::Table-> select (...); </li></ul></ul></ul><ul><ul><ul><li>foreach my $obj (@$list) { </li></ul></ul></ul><ul><ul><ul><li>my $related_rows = $statement-> execute ($obj)-> all ; </li></ul></ul></ul><ul><ul><ul><li>... </li></ul></ul></ul><ul><ul><ul><li>} </li></ul></ul></ul>
  34. 34. Fast statement <ul><li>like a regular statement </li></ul><ul><ul><li>but reuses the same memory location for each row </li></ul></ul><ul><ul><li>see DBI::bind_col() </li></ul></ul><ul><ul><ul><li>my $statement = $source->select( </li></ul></ul></ul><ul><ul><ul><li>. . . , </li></ul></ul></ul><ul><ul><ul><li>-result_as => ' fast_statement ' </li></ul></ul></ul><ul><ul><ul><li>); </li></ul></ul></ul><ul><ul><ul><li>while (my $row = $statement->next) { </li></ul></ul></ul><ul><ul><ul><li>. . . </li></ul></ul></ul><ul><ul><ul><li># DO THIS : print $row->{col1}, $row->{col2} </li></ul></ul></ul><ul><ul><ul><li># BUT DON'T DO THIS : push @results, $row; </li></ul></ul></ul><ul><ul><ul><li>} </li></ul></ul></ul>
  35. 35. Database joins
  36. 36. Basic join <ul><li>$rows = My::DB-> join (qw/ Author distribs modules /) </li></ul><ul><li>-> select (-where => ...); </li></ul>Author Distrib Module My::DB::AutoJoin::… DBIDM::Source::Join new class created on the fly
  37. 37. Left / inner joins <ul><li>->Association([qw/Author author 1 /], </li></ul><ul><li>[qw/Distribution distribs 0..* /]) </li></ul><ul><li># default : LEFT OUTER JOIN </li></ul><ul><li>->Composition([qw/Distribution distrib 1 /], </li></ul><ul><li>[qw/Module modules 1..* /]); </li></ul><ul><li># default : INNER JOIN </li></ul><ul><li># but defaults can be overridden </li></ul><ul><li>My::DB->join([qw/Author <=> distribs/)-> . . . </li></ul><ul><li>My::DB->join([qw/Distribution => modules /)-> . . . </li></ul>
  38. 38. Join from an instance <ul><li>$rows = $author-> join (qw/ distribs modules /)->select( </li></ul><ul><li>-columns => [qw/distrib_name module_name/], </li></ul><ul><li>-where => { d_release => {'<' => $date} }, </li></ul><ul><li>); </li></ul>SELECT distrib_name, module_name FROM distribution INNER JOIN module ON distribution.distrib_id = module.distrib_id WHERE distrib.author_id = $author->{author_id} AND d_release < $date
  39. 39. Insert / Update
  40. 40. Insert <ul><li>@ids = MyDB::Author-> insert ( </li></ul><ul><li>{ firstname => 'Larry', lastname => 'Wall' }, </li></ul><ul><li>{ firstname => 'Damian', lastname => 'Conway' }, </li></ul><ul><li>); </li></ul>INSERT INTO author(firstname, lastname) VALUES (?, ?)
  41. 41. Bulk insert <ul><li>@ids = MyDB::Author-> insert ( </li></ul><ul><li>[qw/firstname lastname/], </li></ul><ul><li>[qw/Larry Wall /], </li></ul><ul><li>[qw/Damian Conway /], </li></ul><ul><li>); </li></ul>
  42. 42. Insert into / cascaded insert <ul><li>@id_trees = $author-> insert_into_distribs ( </li></ul><ul><li>{distrib_name => 'DBIx-DataModel', </li></ul><ul><li>modules => [ </li></ul><ul><li>{module_name => 'DBIx::DataModel', ..}, </li></ul><ul><li>{module_name => 'DBIx::DataModel::Statement', ..}, </li></ul><ul><li>]}, </li></ul><ul><li>{distrib_name => 'Pod-POM-Web', </li></ul><ul><li>… </li></ul><ul><li>}, </li></ul><ul><li>-returning => {}, </li></ul><ul><li>); </li></ul>
  43. 43. Update <ul><li>$obj->{col1} = $new_val_1; </li></ul><ul><li>$obj->{col2} = $new_val_2; </li></ul><ul><li>. . . </li></ul><ul><li>$opj-> update ; </li></ul><ul><li># or </li></ul><ul><li>MyDB::Author-> update ({author_id => $id, col => $new_val}) </li></ul><ul><li># or </li></ul><ul><li>MyDB::Author-> update ($id, {col => $new_val}) </li></ul><ul><li># or (bulk update) </li></ul><ul><li>MyDB::Author-> update (-set => {col => $new_val}, </li></ul><ul><li> -where => %condition) </li></ul>
  44. 44. Transaction <ul><li>MyDB-> do_transaction (sub { </li></ul><ul><li>my $author = MyDB::Author->fetch($author_id, </li></ul><ul><li>{ -for => &quot;read only&quot;} </li></ul><ul><li>); </li></ul><ul><li>my $distribs = $author->distribs( -for => 'update'); </li></ul><ul><li>foreach my $distrib (@$distribs) { </li></ul><ul><li>my $id = $distrib->{distrib_id}; </li></ul><ul><li>MyDB::Distrib-> update ($id, {col => $val}); </li></ul><ul><li>} </li></ul><ul><li>}); </li></ul><ul><li>can be nested </li></ul><ul><li>can involve several dbh </li></ul><ul><li>no savepoints (yet) </li></ul>
  45. 45. Other features
  46. 46. Named placeholders / bind() <ul><li># introduce named placeholders </li></ul><ul><li>$statement-> prepare (-where => { </li></ul><ul><li>col1 => '?:foo ', </li></ul><ul><li>col2 => {&quot;<&quot; => '?:bar '}, </li></ul><ul><li>col3 => {&quot;>&quot; => '?:bar '}, </li></ul><ul><li>col3 => 1234, </li></ul><ul><li>}); </li></ul><ul><li># fill placeholders with values </li></ul><ul><li>$statement-> bind (foo => 99, bar => 88, other => 77); </li></ul><ul><li>$statement-> bind ($hashref); </li></ul>$sql @bind SELECT * FROM .. WHERE col1 = ? AND col2 < ? AND col3 = ? -- -- 1234 ?:foo ?:bar
  47. 47. Types (inflate/deflate) <ul><li># declare a Type </li></ul><ul><li>My::DB -> Type ( Multivalue => </li></ul><ul><li>from_DB => sub {$_[ 0 ] = [split /;/ , $_[ 0 ]] } , </li></ul><ul><li>to_DB => sub {$_[ 0 ] = join &quot;;&quot; , @$_[ 0 ] }, </li></ul><ul><li>); </li></ul><ul><li># apply it to some columns in a table </li></ul><ul><li>My::DB::Author->metadm-> define_column_type ( </li></ul><ul><li>Multivalue => qw/hobbies languages/, </li></ul><ul><li>); </li></ul>
  48. 48. Auto_expand <ul><li># declare auto-expansions </li></ul><ul><li>MyDB::Author -> define_auto_expand (qw/distributions/); </li></ul><ul><li>MyDB::Distribution -> define_auto_expand (qw/modules/); </li></ul><ul><li># apply to an object (automatically fetches all modules of all distributions of that author) </li></ul><ul><li>$author-> auto_expand (); </li></ul><ul><li># use the data tree </li></ul><ul><li>use YAML; print Dump($author); </li></ul>
  49. 49. Schema localization <ul><li>{ </li></ul><ul><li># a kind of &quot;local MyDB&quot;; </li></ul><ul><li>my $guard = MyDB-> localize_state (); </li></ul><ul><li># temporary change class data </li></ul><ul><li>MyDB->dbh($new_dbh, %new_options); </li></ul><ul><li>do_some_work_with_new_dbh(); </li></ul><ul><li>} # automatically restore previous state </li></ul>
  50. 50. Schema generator <ul><li>perl -MDBIx::DataModel::Schema::Generator </li></ul><ul><li>-e &quot; fromDBI ('dbi:connection:string')&quot; -- </li></ul><ul><li>-schema My::New::Schema > My/New/Schema.pm </li></ul><ul><li>perl -MDBIx::DataModel::Schema::Generator </li></ul><ul><li>-e &quot; fromDBIxClass ('Some::DBIC::Schema')&quot; -- </li></ul><ul><li>-schema My::New::Schema > My/New/Schema.pm </li></ul>
  51. 51. Auto_insert / Auto_update / No_update <ul><li>$table-> auto_insert_columns ( created_by => </li></ul><ul><li>sub { $ENV{REMOTE_USER} . &quot;, &quot; . localtime } </li></ul><ul><li>); </li></ul><ul><li>$table-> auto_update_columns ( </li></ul><ul><li>modified_by => sub {… } </li></ul><ul><li>); </li></ul><ul><li>$table-> no_update_columns (qw/ row_id /); </li></ul><ul><li> can also be declared for the whole schema </li></ul>
  52. 52. Extending / customizing DBIx::DataModel <ul><li>Schema hooks for </li></ul><ul><ul><li>SQL dialects (join syntax, alias syntax, limit / offset, etc.) </li></ul></ul><ul><ul><li>last_insert_id </li></ul></ul><ul><li>Ad hoc subclasses for </li></ul><ul><ul><li>SQL::Abstract </li></ul></ul><ul><ul><li>Table </li></ul></ul><ul><ul><li>Join </li></ul></ul><ul><ul><li>Statements </li></ul></ul><ul><li>Statement callbacks </li></ul><ul><li>Extending table classes </li></ul><ul><ul><li>additional methods </li></ul></ul><ul><ul><li>redefining _singleInsert method </li></ul></ul>
  53. 53. Conclusion
  54. 54. Strengths <ul><li>centralized definitions of tables & associations </li></ul><ul><li>efficiency </li></ul><ul><li>improved API for SQL::Abstract </li></ul><ul><li>clear conceptual distinction between </li></ul><ul><ul><li>data sources (tables and joinss), </li></ul></ul><ul><ul><li>database statements (stateful objects representing SQL queries) </li></ul></ul><ul><ul><li>data rows (lightweight blessed hashrefs) </li></ul></ul><ul><li>concise and flexible syntax for joins </li></ul><ul><li>used in production for mission-critical app </li></ul><ul><ul><li>(running Geneva courts) </li></ul></ul>
  55. 55. Limitations <ul><li>tiny community </li></ul><ul><li>no schema versioning </li></ul><ul><li>no object caching nor 'dirty columns' </li></ul><ul><li>no 'cascaded update' nor 'insert or create' </li></ul>
  56. 56. Lots of documentation <ul><li>SYNOPSIS AND DESCRIPTION </li></ul><ul><li>DESIGN </li></ul><ul><li>QUICKSTART </li></ul><ul><li>REFERENCE </li></ul><ul><li>COOKBOOK </li></ul><ul><li>MISC </li></ul><ul><li>INTERNALS </li></ul><ul><li>GLOSSARY </li></ul>
  57. 57. THANK YOU FOR YOUR ATTENTION
  58. 58. Bonus slides
  59. 59. ORM: What for ? <ul><li>[catalyst list] On Thu, 2006-06-08, Steve wrote: </li></ul><ul><li>Not intending to start any sort of rancorous discussion, </li></ul><ul><li>but I was wondering whether someone could illuminate </li></ul><ul><li>me a little? </li></ul><ul><li>I'm comfortable with SQL, and with DBI. I write basic </li></ul><ul><li>SQL that runs just fine on all databases, or more </li></ul><ul><li>complex SQL when I want to target a single database </li></ul><ul><li>(ususally postgresql). </li></ul><ul><li>What value does an ORM add for a user like me? </li></ul>
  60. 60. ORM useful for … <ul><li>dynamic SQL </li></ul><ul><ul><li>navigation between tables </li></ul></ul><ul><ul><li>generate complex SQL queries from Perl datastructures </li></ul></ul><ul><ul><li>better than phrasebook or string concatenation </li></ul></ul><ul><li>automatic data conversions (inflation / deflation) </li></ul><ul><li>expansion of tree data structures coded in the relational model </li></ul><ul><li>transaction encapsulation </li></ul><ul><li>data validation </li></ul><ul><li>computed fields </li></ul><ul><li>caching </li></ul><ul><li>… </li></ul> See Also : http://lists.scsys.co.uk/pipermail/catalyst/2006-June
  61. 61. Many-to-many implementation author_id author_name e_mail 1 * 1 * * * Author distrib_id module_id Dependency distrib_id distrib_name d_release author_id Distribution module_id module_name distrib_id Module 1 1 link table for n-to-n association
  62. 62. Writing SQL SQL is too low-level, I don't ever want to see it SQL is the most important part of my application, I won't let anybody write it for me
  63. 63. Why hashref instead of OO accessors ? <ul><li>Perl builtin rich API for hashes (keys, values, slices, string interpolation) </li></ul><ul><li>good for import / export in YAML/XML/JSON </li></ul><ul><li>easier to follow steps in Perl debugger </li></ul><ul><li>faster than OO accessor methods </li></ul><ul><li>visually clear distinction between lvalue / rvalue </li></ul><ul><ul><li>my $val = $hashref->{column}; </li></ul></ul><ul><ul><li>$hashref->{column} = $val; </li></ul></ul><ul><li>visually clear distinction between </li></ul><ul><ul><li>$row->{column} / $row->remote_table() </li></ul></ul>
  64. 64. Callback example <ul><li>WITH RECURSIVE nodetree(level, id, pid, sort) AS ( SELECT 1, id, parent, '{1}'::int[] FROM nodes WHERE parent IS NULL     UNION SELECT level+1,p.id, parent, sort||p.id FROM nodetree pr JOIN nodes p ON p.parent = pr.id </li></ul><ul><li>) SELECT * FROM nodetree ORDER BY sort; </li></ul>my $with_clause = &quot;WITH RECURSIVE …&quot;; DBIx::DataModel->Schema('Tst') ->Table(qw/Nodetree nodetree id/); my $result = Tst::Nodetree-> select ( -post_SQL => sub {my $sql = shift; $sql =~ s/^/$with_clause/; return $sql, @_ }, -orderBy => 'sort', );
  65. 65. Verbose form for definitions <ul><li>DBIx::DataModel-> define_schema ( </li></ul><ul><li>class => &quot;My::DB&quot; </li></ul><ul><li>); </li></ul><ul><li>My::DB->metadm-> define_table ( </li></ul><ul><li>class => &quot;Author&quot;, </li></ul><ul><li>db_name => &quot;author&quot;, </li></ul><ul><li>primary_key => &quot;author_id&quot; </li></ul><ul><li>): </li></ul>
  66. 66. New features in 2.0 <ul><li>metaclasses </li></ul><ul><ul><li>client can query about tables, associations, types, etc. </li></ul></ul><ul><ul><li>method namespace for regular objects is not polluted by meta-methods </li></ul></ul><ul><li>single-schema / multi-schema mode </li></ul><ul><li>misc additions </li></ul><ul><ul><li>bulk update & bulk delete </li></ul></ul><ul><ul><li>support for table inheritance </li></ul></ul><ul><ul><li>arbitrary clauses in joins </li></ul></ul><ul><li>API changes </li></ul><ul><ul><li>perlish_method_names() </li></ul></ul><ul><ul><li>SQL generation moved to SQL::Abstract::More </li></ul></ul><ul><ul><li>Params::Validate everywhere </li></ul></ul><ul><ul><li>deprecated Autoload() </li></ul></ul><ul><ul><li>compatibility layer : use DBIx::DataModel –compatibility => 1.0 </li></ul></ul>
  67. 67. Migration to v2.0 <ul><li>deploy DBIx::DataModel 2.0 </li></ul><ul><ul><li>-compatibility => 1.0 </li></ul></ul><ul><li>test </li></ul><ul><li>change client code according to new API </li></ul><ul><li>test </li></ul><ul><li>suppress compatibility layer </li></ul>
  68. 68. DBIx::DataModel history <ul><li>2005 : 1st CPAN publication (v0.10, 16.09.05) </li></ul><ul><li>2006 : YAPC::EU::06 Birmingham presentation </li></ul><ul><li>2008 : heavy refactoring (v1.03, 23.09.08) </li></ul><ul><ul><li>statement object </li></ul></ul><ul><ul><li>implicit schema name </li></ul></ul><ul><li>2011: heavy refactoring </li></ul><ul><ul><li>metaobject layer </li></ul></ul><ul><ul><li>multi-schema mode </li></ul></ul><ul><ul><li>API renaming </li></ul></ul>

×