dbix::class vs. dbix::datamodel

65
26.06.22 - Page 1 Département Office DBIx::Class vs. DBIx::DataModel FPW 2012, Strasbourg [email protected] Département Office

Upload: ldami

Post on 17-May-2015

1.373 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

DBIx::Classvs.

DBIx::DataModel

FPW 2012, Strasbourg

[email protected]épartement

Office

Page 2: DBIx::Class vs. DBix::DataModel

Agenda

• Introduction• Schema definition• Data retrieval• Joins• Classes and methods• Resultset/Statement objects• Customization• Conclusion

Page 3: DBIx::Class vs. DBix::DataModel

Introduction

Page 4: DBIx::Class vs. DBix::DataModel

ORM layer

Database

DBD driver

DBI

Object-Relational Mapper

Perl program

Page 5: DBIx::Class vs. DBix::DataModel

ORM useful for …

• dynamic SQL generation– navigation between tables– generate complex SQL queries from Perl datastructures– better than phrasebook or string concatenation

• automatic data conversions (inflation / deflation)• transaction encapsulation • data validation• computed fields• caching• expansion of tree data structures coded in the relational

model• …

Page 6: DBIx::Class vs. DBix::DataModel

CPAN ORM Landscape

• Discussed here– DBIx::Class (a.k.a. DBIC)– DBIx::DataModel (a.k.a. DBIDM)

• Many other– Rose::DB, Jifty::DBI, Fey::ORM, ORM,

DBIx::ORM::Declarative, Tangram, Coat::Persistent, ORLite,DBR, DBIx::Sunny, DBIx::Skinny, DBI::Easy, …

DISCLAIMER- I'm not an expert of DBIC- I'll try to be neutral in comparisons, but …- Won't cover all topics

Page 7: DBIx::Class vs. DBix::DataModel

A bit of 2005 historyClass::DBI (1999)

Class::DBI::Sweet (29.05.05)- SQL::Abstract

DBIx::DataModel 0.10 (16.09.05)

SQL::Abstract (2001)

DBIx::Class 0.01 (08.08.05)- SQL::Abstract

DBIx::Class 0.03 (19.09.05)- prefetch joins

Class::DBI::Sweet 0.02 (29.06.05)-prefetch joins

DBIx::DataModel private (feb.05)

Page 8: DBIx::Class vs. DBix::DataModel

Some figures

• DBIC– thousands of users– dozens of contributors– 162 files– 167 packages– 1042 subs/methods– 16759 lines of Perl– 1817 comment lines– 19589 lines of POD– 70 transitive dependencies

• DBIDM– a couple of users– 1 contributor– 31 files– 40 packages– 175 subs/methods– 3098 lines of Perl– 613 comment lines– 8146 lines of POD– 54 transitive dependencies

Page 9: DBIx::Class vs. DBix::DataModel

straight from CPAN::SQLite

Our example : CPAN model

Author

Distribution Module

1

*

1 1..*

* *

contains ►

depends_on ►

Page 10: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

Schema definition

Page 11: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

DBIC Schema class

use utf8;package FPW12::DBIC;use strict;use warnings;

use base 'DBIx::Class::Schema';

__PACKAGE__->load_namespaces;

1;

Page 12: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

DBIC 'Dist' table class

use utf8;package FPW12::DBIC::Result::Dist;use strict; use warnings;use base 'DBIx::Class::Core';

__PACKAGE__->table("dists");__PACKAGE__->add_columns(

dist_id => { data_type => "integer", is_nullable => 0, is_auto_increment => 1 },

dist_vers => { data_type => "varchar", is_nullable => 1, size => 20 }, ... # other columns );__PACKAGE__->set_primary_key("dist_id");__PACKAGE__->belongs_to ('auth', 'FPW12::DBIC::Result::Auth', 'auth_id');__PACKAGE__->has_many ('mods', 'FPW12::DBIC::Result::Mod', 'dist_id');__PACKAGE__->has_many ('depends', 'FPW12::DBIC::Result::Depends', 'dist_id');__PACKAGE__->many_to_many(prereq_mods => 'depends', 'mod');

# idem for classes Auth, Mod, etc.

Page 13: DBIx::Class vs. DBix::DataModel

DBIDM centralized declarations

use DBIx::DataModel;DBIx::DataModel->Schema("FPW12::DBIDM")# class table primary key # ===== ===== ===========->Table(qw/Auth auths auth_id /)->Table(qw/Dist dists dist_id /)->Table(qw/Mod mods mod_id /)->Table(qw/Depends depends dist_id mod_id/)# class role multipl. (optional join keys) # ===== ==== ======= ====================->Association([qw/Auth auth 1 auth_id /], [qw/Dist dists * auth_id /])->Composition([qw/Dist dist 1 /], [qw/Mod mods * /])->Association([qw/Dist dist 1 /], [qw/Depends depends * /])->Association([qw/Mod mod 1 /], [qw/Depends depends * /])->Association([qw/Dist used_in_dist * depends distrib /], [qw/Mod prereq_mods * depends mod /]);

Page 14: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

Schema def. comparison

• DBIC– one file for each class– regular Perl classes– full column info– 1-way "relationships"

• belongs_to• has_many• may_have• ….

– custom join conditions• any operator• 'foreign' and 'self'

• DBIDM– centralized file– dynamic class creation – no column info (except pkey)– 2-ways "associations" with

• multiplicities• role names• diff association/composition

– custom column names• only equalities

Page 15: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

Data retrieval

Page 16: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

DBIC: Search

use FPW12::DBIC;

my $schema = FPW12::DBIC->connect($data_source);

my @dists = $schema->resultset('Dist')->search( {dist_name => {-like => 'DBIx%'}}, {columns => [qw/dist_name dist_vers/]},);

foreach my $dist (@dists) { printf "%s (%s)\n", $dist->dist_name, $dist->dist_vers;}

Page 17: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

DBIDM: Search

use FPW12::DBIDM;use DBI;

my $datasource = "dbi:SQLite:dbname=../cpandb.sql";my $dbh = DBI->connect($datasource, "", "",

{RaiseError => 1, AutoCommit => 1});FPW12::DBIDM->dbh($dbh);

my $dists = FPW12::DBIDM::Dist->select( -columns => [qw/dist_name dist_vers/], -where => {dist_name => {-like => 'DBIx%'}},);

foreach my $dist (@$dists) { print "$dist->{dist_name} ($dist->{dist_vers})\n";}

Page 18: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

Simple search comparison

• DBIC– schema is an object– result is a list or a resultset

object– accessor methods for

columns– uses SQL::Abstract

• mostly hidden

• DBIDM– schema is a class (default) or an

object– result is an arrayref (default)– hash entries for columns– uses SQL::Abstract::More

• user can supply a custom $sqla obj

Page 19: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

SQL::Abstract new()

• special operators

– ex: DBMS-independent fulltext search

# where {field => {-contains => [qw/foo bar/]}

my $sqla = SQL::Abstract->new(special_ops => [ {regex => qr/^contains(:?_all|_any)?$/i, handler => sub { my ($self, $field, $op, $arg) = @_; my $connector = ($op =~ /any$/) ? ' | ' : ' & '; my @vals = ref $arg ? @$arg : ($arg); @vals = map { split /\s+/ } grep {$_} @vals; my $sql = sprintf "CONTAINS($field, '%s') > 0",

join $connector, @vals; return ($sql); # no @bind

}]);

Page 20: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

DBIC: Find single record

my $auth1 = $schema->resultset('Auth')->find(123);

my $auth2 = $schema->resultset('Auth') ->find({cpanid => 'DAMI'});

Page 21: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

DBIDM: Find single record

my $auth1 = FPW12::DBIDM::Auth->fetch(123);

my $auth2 = FPW12::DBIDM::Auth->search( -where => {cpanid => 'DAMI'}, -result_as => 'firstrow',);

Page 22: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

DBIC: Search args

• 1st arg : "where" criteria• 2nd arg : attributes

– distinct– order_by– group_by, having– columns # list of columns to retrieve from main table– +columns # additional columns from joined tables or

from functions – join # see later– prefetch # see later– for– page, rows, offset, etc.– cache

Page 23: DBIx::Class vs. DBix::DataModel

DBIDM: Select args

my $result = $source->select( -columns => \@columns,, -where => \%where, -group_by => \@groupings, -having => \%criteria, -order_by => \@order, -for => 'read only', -post_SQL => sub { … }, -pre_exec => sub { … }, -post_exec => sub { … }, -post_bless => sub { … }, -page_size => …, -page_index => …, -limit => …, -offset => …, -column_types => \%types, -result_as => $result_type,);

Page 24: DBIx::Class vs. DBix::DataModel

DBIDM: Polymorphic result

-result_as =>– 'rows' (default) : arrayref of row objects– 'firstrow' : a single row object (or undef)– 'hashref' : hashref keyed by primary keys– [hashref => @cols] : cascaded hashref– 'flat_arrayref' : flattened values from each row– 'statement' : a statement object (iterator)– 'fast_statement' : statement reusing same memory– 'sth' : DBI statement handle– 'sql' : ($sql, @bind_values)– 'subquery' : \["($sql)", @bind]

don't need method variants : select_hashref(), select_arrayref(), etc.

Page 25: DBIx::Class vs. DBix::DataModel

DBIDM: Fast statement

• like a regular statement– but reuses the same memory location for each row– see DBI::bind_col()

my $statement = $source->select( . . . , -result_as => 'fast_statement');

while (my $row = $statement->next) { . . . # DO THIS : print $row->{col1}, $row->{col2} # BUT DON'T DO THIS : push @results, $row;}

Page 26: DBIx::Class vs. DBix::DataModel

Advanced search comparison

• DBIC– 'find' by any unique

constraint– "inner" vs "outer" columns– result is context-dependent– optional caching

• DBIDM– 'fetch' by primary key only– all columns equal citizens– polymorphic result– no caching– statement-specific inflators– callbacks

Page 27: DBIx::Class vs. DBix::DataModel

Joins

Page 28: DBIx::Class vs. DBix::DataModel

Task : list distributions and their authors

Author

Distribution Module

1

*

1 1..*

* *

contains ►

depends_on ►

Page 29: DBIx::Class vs. DBix::DataModel

DBIC: Join

my @dists = $schema->resultset('Dist')->search( {dist_name => {-like => 'DBIx%'}}, {columns => [qw/dist_name dist_vers/], join => 'auth',

'+columns' => [{'fullname' => 'auth.fullname'}], },);

foreach my $dist (@dists) { printf "%s (%s) by %s\n",

$dist->dist_name, $dist->dist_vers, $dist->get_column('fullname'); # no accessor meth}

Page 30: DBIx::Class vs. DBix::DataModel

DBIC: Join with prefetch

my @dists = $schema->resultset('Dist')->search( {dist_name => {-like => 'DBIx%'}}, {columns => [qw/dist_name dist_vers/], prefetch => 'auth', },);

foreach my $dist (@dists) { printf "%s (%s) by %s\n",

$dist->dist_name, $dist->dist_vers, $dist->auth->fullname; # already in memory}

Page 31: DBIx::Class vs. DBix::DataModel

DBIDM: Join

my $rows = FPW12::DBIDM->join(qw/Dist auth/)->select( -columns => [qw/dist_name dist_vers fullname/], -where => {dist_name => {-like => 'DBIx%'}},);

foreach my $row (@$rows) { print "$row->{dist_name} ($row->{dist_vers}) " . "by $row->{fullname}\n";}

Dist Auth

..::AutoJoin::…

DBIDM::Source::Join

new class created on the fly

Page 32: DBIx::Class vs. DBix::DataModel

Multiple joins

• DBIC

join => [ { abc => { def => 'ghi' } }, { jkl => 'mno' },

'pqr' ]

• DBIDM

->join(qw/Root abc def ghi jkl mno pqr/)

Page 33: DBIx::Class vs. DBix::DataModel

Join from an existing record

• DBIC

my $rs = $auth->dists(undef, {join|prefetch => 'mods'});

• DBIDM

my $rows = $auth->join(qw/dists mods/)->select;

Page 34: DBIx::Class vs. DBix::DataModel

DBIC: left/inner joins

• attribute when declaring relationships# in a Book class (where Author has_many Books)

__PACKAGE__->belongs_to( author => 'My::DBIC::Schema::Author', 'author', { join_type => 'left' } );

• cannot change later (when invoking the relationship)

Page 35: DBIx::Class vs. DBix::DataModel

DBIDM: Left / inner joins

->Association([qw/Author author 1 /], [qw/Distribution distribs 0..* /])

# default : LEFT OUTER JOIN

->Composition([qw/Distribution distrib 1 /], [qw/Module modules 1..* /]);

# default : INNER JOIN

# but defaults can be overriddenMy::DB->join([qw/Author <=> distribs/)-> . . . My::DB->join([qw/Distribution => modules /)-> . . .

Page 36: DBIx::Class vs. DBix::DataModel

Join comparison

• DBIC– rows belong to 1 table class

only– joined columns are either

• "side-products", • prefetched (all of them, no

choice)

– fixed join type

• DBIDM– rows inherit from all joined

tables– flattening of all columns

– default join type, can be overridden

Page 37: DBIx::Class vs. DBix::DataModel

Speed

Page 38: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

Select speed

list mods(109349 rows)

join Auth dist mods(113895 rows)

DBI 0.43 secs 1.36 secs

DBIC regular 11.09 secs 15.50 secs

DBIC prefetch n.a. 146.29 secs

DBIC hashref inflator

10.06 secs 14.17 secs

DBIDM regular 4.00 secs 5.01 secs

DBIDM fast_statement

2.25 secs 3.28 secs

Page 39: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

Insert speed

insert (72993 rows)

DBI 5.8 secs

DBIC regular 40.35 secs

DBIC populate 5.6 secs

DBIDM regular

32.81 secs

DBIDM bulk 32.57 secs

Page 40: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

Classes and methods

Page 41: DBIx::Class vs. DBix::DataModel

DBIC : methods of a row

DB<1> m $auth # 152 methods_auth_id_accessor_cpanid_accessor_email_accessor_fullname_accessor_result_source_instance_accessoradd_to_distsauth_idcpaniddistsdists_rsemailfullnameresult_source_instancevia DBIx::Class::Core -> DBIx::Class::Relationship ->

DBIx::Class::Relationship::Helpers -> DBIx::Class::Relationship::HasMany: has_many

via DBIx::Class::Core -> DBIx::Class::Relationship -> DBIx::Class::Relationship::Helpers -> DBIx::Class::Relationship::HasOne: _get_primary_key

via DBIx::Class::Core -> DBIx::Class::Relationship -> DBIx::Class::Relationship::Helpers -> DBIx::Class::Relationship::HasOne: _has_one

via DBIx::Class::Core -> DBIx::Class::Relationship -> DBIx::Class::Relationship::Helpers -> DBIx::Class::Relationship::HasOne: _validate_has_one_condition

via DBIx::Class::Core -> DBIx::Class::Relationship -> DBIx::Class::Relationship::Helpers -> DBIx::Class::Relationship::HasOne: has_one

...

DB<4> x mro::get_linear_isa(ref $auth)0 ARRAY(0x13826c4) 0 'FPW12::DBIC::Result::Auth' 1 'DBIx::Class::Core' 2 'DBIx::Class::Relationship' 3 'DBIx::Class::Relationship::Helpers' 4 'DBIx::Class::Relationship::HasMany' 5 'DBIx::Class::Relationship::HasOne' 6 'DBIx::Class::Relationship::BelongsTo' 7 'DBIx::Class::Relationship::ManyToMany' 8 'DBIx::Class' 9 'DBIx::Class::Componentised' 10 'Class::C3::Componentised' 11 'DBIx::Class::AccessorGroup' 12 'Class::Accessor::Grouped' 13 'DBIx::Class::Relationship::Accessor' 14 'DBIx::Class::Relationship::CascadeActions' 15 'DBIx::Class::Relationship::ProxyMethods' 16 'DBIx::Class::Relationship::Base' 17 'DBIx::Class::InflateColumn' 18 'DBIx::Class::Row' 19 'DBIx::Class::PK::Auto' 20 'DBIx::Class::PK' 21 'DBIx::Class::ResultSourceProxy::Table' 22 'DBIx::Class::ResultSourceProxy'

Page 42: DBIx::Class vs. DBix::DataModel

DBIDM : methods of a row

DB<1> m $auth # 36 methodsdistsinsert_into_distsmetadmvia DBIx::DataModel::Source::Table:

_get_last_insert_idvia DBIx::DataModel::Source::Table:

_insert_subtreesvia DBIx::DataModel::Source::Table: _rawInsertvia DBIx::DataModel::Source::Table: _singleInsertvia DBIx::DataModel::Source::Table:

_weed_out_subtreesvia DBIx::DataModel::Source::Table: deletevia DBIx::DataModel::Source::Table:

has_invalid_columnsvia DBIx::DataModel::Source::Table: insertvia DBIx::DataModel::Source::Table: updatevia DBIx::DataModel::Source::Table ->

DBIx::DataModel::Source: TO_JSONvia DBIx::DataModel::Source::Table ->

DBIx::DataModel::Source: apply_column_handlervia DBIx::DataModel::Source::Table ->

DBIx::DataModel::Source: auto_expandvia DBIx::DataModel::Source::Table ->

DBIx::DataModel::Source: bless_from_DBvia DBIx::DataModel::Source::Table ->

DBIx::DataModel::Source: expandvia DBIx::DataModel::Source::Table ->

DBIx::DataModel::Source: fetch

DB<4> x mro::get_linear_isa(ref $auth)

0 ARRAY(0x145275c)

0 'FPW12::DBIDM::Auth'

1 'DBIx::DataModel::Source::Table'

2 'DBIx::DataModel::Source'

Page 43: DBIx::Class vs. DBix::DataModel

BelongsTo, HasMany, etc.

Schema Core ResultSet

My::DB My::DB::Result::Table_n

row resultset

DBIC classes

application classes

objects

schema

Class

Row

Componentised AccessorGroup

My::DB::ResultSet::Table_n

Relationship InflateColumn PK::Auto PK RSProxy::Table

BelongsTo, HasMany, etc.

RSProxyRLHelpers RLBase ResultSource

RS::Table

inheritanceinstantiation delegation

Page 44: DBIx::Class vs. DBix::DataModel

Schema

Source

Table Join Statement

My::DB My::DB::Table_n

My::DB::AutoJoin::

row statementrow

DBIDM classes

applicationclasses

objects

schema

Page 45: DBIx::Class vs. DBix::DataModel

DBIDM 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::PathMeta::Association Meta::Type

meta::assoc meta::path

meta::type

Page 46: DBIx::Class vs. DBix::DataModel

DBIC introspection methods

• Schema– sources(), source($source_name)

• ResultSource– primary_key– columns(), column_info($column_name)– relationships(), relationship_info($relname)

Page 47: DBIx::Class vs. DBix::DataModel

Architecture comparison

• DBIC– very complex class structure– no distinction front/meta

layser– methods for

• CRUD• navigation to related objs• introspection• setting up columns• setting up relationships• …

• DBIDM– classes quite close to DBI

concepts– front classes and Meta

classes– methods for

• CRUD• navigation to related objs• access to meta

Page 48: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

Resultset/Statement objects

Page 49: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

Example task

• list names of authors – of distribs starting with 'DBIx' – and version number > 2

Page 50: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

DBIC ResultSet chaining

my $dists = $schema->resultset('Dist')->search( {dist_name => {-like => 'DBIx%'}},);my $big_vers = $dists->search({dist_vers => { ">" => 2}});my @auths = $big_vers->search_related('auth', undef,

{distinct => 1, order_by => 'fullname'});

say $_->fullname foreach @auths;

# Magical join ! Magical "group by" !SELECT auth.auth_id, auth.email, auth.fullname, auth.cpanidFROM dists me JOIN auths auth ON auth.auth_id = me.auth_id WHERE ( ( dist_vers > ? AND dist_name LIKE ? ) ) GROUP BY auth.auth_id, auth.email, auth.fullname, auth.cpanid ORDER BY fullname

Page 51: DBIx::Class vs. DBix::DataModel

DBIDM Statement lifecycle

new

sqlized

prepared

executed

schema + source

data row(s)

new()

sqlize()

prepare()

execute()

bind()refine()

bind()

bind()

bind()execute()

next() / all()

blessedcolumn types applied

-post_bless

-pre_exec

-post_exec

-post_SQL

Page 52: DBIx::Class vs. DBix::DataModel

DBIDM Statement::refine()

my $stmt = FPW12::DBIDM->join(qw/Dist auth/)->select( -where => {dist_name => {-like => 'DBIx%'}}, -result_as => 'statement',);

$stmt->refine( -columns => [-DISTINCT => 'fullname'], -where => {dist_vers => { ">" => 0}}, -order_by => 'fullname',);

say $_->{fullname} while $_ = $stmt->next;

Page 53: DBIx::Class vs. DBix::DataModel

DBIDM subquery

my $stmt = FPW12::DBIDM::Dist->select( -where => {dist_name => {-like => 'DBIx%'}}, -result_as => 'statement',);

my $subquery = $stmt->select( -columns => 'auth_id', -where => {dist_vers => { ">" => 2}}, -result_as => 'subquery',);

my $auths = FPW12::DBIDM::Auth->select( -columns => 'fullname', -where => {auth_id => {-in => $subquery}},);

say $_->{fullname} foreach @$rows;

Page 54: DBIx::Class vs. DBix::DataModel

Statement comparison

• DBIC– powerful refinement

constructs• more "where" criteria• navigation to related source• column restriction• aggregation operators (e.g.

"count")

• DBIDM– limited refinement constructs– explicit control of status

Page 55: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

Other features

Page 56: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

Transactions

• DBIC

$schema->txn_do($sub);

– can be nested– can have intermediate

savepoints

• DBIDM

$sch->do_transaction($sub);

– can be nested– no intermediate savepoints

Page 57: DBIx::Class vs. DBix::DataModel

DBIC inflation/deflation

__PACKAGE__->inflate_column('insert_time', {inflate => sub { DateTime::Format::Pg->parse_datetime(shift); }, deflate => sub { DateTime::Format::Pg->format_datetime(shift) }, });

Page 58: DBIx::Class vs. DBix::DataModel

DBIDM Types (inflate/deflate)

# declare a TypeMy::DB->Type(Multivalue => from_DB => sub {$_[0] = [split /;/, $_[0]] }, to_DB => sub {$_[0] = join ";", @$_[0] },);

# apply it to some columns in a tableMy::DB::Author->metadm->define_column_type( Multivalue => qw/hobbies languages/,);

Page 59: DBIx::Class vs. DBix::DataModel

Extending/customizing DBIC

• Subclassing– result classes– resultset classes– storage– sqlmaker

Page 60: DBIx::Class vs. DBix::DataModel

Extending / customizing DBIDM

• Schema hooks for– SQL dialects (join syntax, alias syntax, limit / offset, etc.)– last_insert_id

• Ad hoc subclasses for– SQL::Abstract– Table– Join– Statements

• Statement callbacks• Extending table classes

– additional methods– redefining _singleInsert method

Page 61: DBIx::Class vs. DBix::DataModel

Not covered here

• inserts, updates, deletes• Schema generator• Schema versioning• inflation/deflation

Page 62: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

THANK YOU FOR YOUR ATTENTION

Page 63: DBIx::Class vs. DBix::DataModel

12.04.23 - Page 1

DépartementOffice

Bonus slides

Page 64: DBIx::Class vs. DBix::DataModel

Why hashref instead of OO accessors ?

• Perl builtin rich API for hashes (keys, values, slices, string interpolation)

• good for import / export in YAML/XML/JSON• easier to follow steps in Perl debugger• faster than OO accessor methods• visually clear distinction between lvalue / rvalue

– my $val = $hashref->{column};– $hashref->{column} = $val;

• visually clear distinction between – $row->{column} / $row->remote_table()

Page 65: DBIx::Class vs. DBix::DataModel

SQL::Abstract::More : extensions

• -columns => [qw/col1|alias1 max(col2)|alias2/]– SELECT col1 AS alias1, max(col2) AS alias2

• -columns => [-DISTINCT => qw/col1 col2 col3/]– SELECT DISTINCT col1, col2, col3

• -order_by => [qw/col1 +col2 –col3/]– SELECT … ORDER BY col1, col2 ASC, col3 DESC

• -for => "update" || "read only"– SELECT … FOR UPDATE