DBIx-DataModel
view release on metacpan or search on metacpan
lib/DBIx/DataModel/Doc/Design.pod view on Meta::CPAN
L<bless_from_DB()|DBIx::DataModel::Doc::Reference/"bless_from_DB()">
method. Objects blessed in that way can then benefit from all methods
provided by C<DBIx::DataModel>.
=item *
Hooks can be inserted at various stages of the statement
lifecycle : see parameters C<-post_SQL>, C<-pre_exec>, etc.
to the
L<select()|DBIx::DataModel::Doc::Reference/"select()">
method. This provides an opportunity for running driver-specific
or application-specific code at a particular point in the
lifecycle. If the same hook is needed in every statement,
another possibility is to subclass
L<DBIx::DataModel::Statement> and override the C<prepare()>,
C<execute()> or C<select()> methods.
=item *
The internal representation of a row object is just a plain Perl
hashref. Application code can take advantage of usual Perl idioms for
dealing with such hashrefs, for example for extracting keys, values or
slices of data, or for passing the whole datastructure to external
helper modules such as XML generators, Perl dumps, javascript JSON,
templates of the Template Toolkit, etc. Such modules need to walk on
the data tree, exploring keys, values and subtrees; so they cannot
work if data columns are implemented as object-oriented methods.
=back
=head2 Let the database do the work
In the spirit of collaborating with the database instead of hiding its
functionalities under an object-oriented cover, several tasks are
deliberately not included within the C<DBIx::DataModel> framework,
under the assumption that such tasks will be better handled by the
database directly.
=head3 Use RDBMS tools to create the schema
Besides basic SQL data definition statements,
RDBMS often come with their own helper tools for creating or modifying
a database schema (interactive editors for tables,
columns, datatypes, etc.). Therefore
C<DBIx::DataModel> provides no support in this area,
and assumes that the database schema is pre-existent.
To communicate with the database, the framework only needs to know a
bare minimum about the schema: table names, primary keys
and UML associations. No details are required about column names
or their datatypes.
=head3 Let the RDBMS check data integrity
Most RDBMS have facilities for checking or ensuring integrity rules :
foreign key constraints, restricted ranges for values, cascaded
deletes, etc. C<DBIx::DataModel> can also do some validation
tasks, by setting up column types with a C<validate> handler;
however, it is recommended to rather use the RDBMS for
performing data integrity checks, whenever possible.
=head3 Take advantage of database projections through variable-size objects
In many ORMs, columns in a table are in 1-to-1 correspondence
with attributes in the associated class; so any transfer between
database and memory systematically includes all the columns, both
for selects and for updates. Of course this has the advantage
of simplicity for the programmer; however, it may be very inefficient
if the client program only wants to read two columns from
a very big table.
Furthermore, unexpected concurrency problems may occur : in a scenario such as
client1 client2
======= =======
my $obj = My::Table->fetch($key); my $obj = My::Table->fetch($key);
$obj->set(column1 => $val1); $obj->set(column2 => $val2);
$obj->update; $obj->update;
the final state of the row should theoretically
be consistent for any concurrent execution of C<client1> and C<client2>.
However, if the ORM layer blindly updates I<all> columns, instead of just
the changed columns, then the final value of C<column1> or
C<column2> is unpredictable.
To diminish the efficiency problem, some ORMs offer the possibility
to partition columns into several I<column groups>. The ORM layer
then transparently fetches the appropriate groups in several steps,
depending on which columns are requested from the client. However,
this might be another source of inefficiency, if the client
frequently needs one column from the first group and one from the
second group.
With C<DBIx::DataModel>, the client code has precise control over
which columns to transfer, because these can be specified separately at
each method call. Whenever efficiency is not an issue, one
can be lazy and specify nothing, in which case the SELECT columns will
default to "*". Actually, the schema
I<does not know about column names>, except for primary and
foreign keys, and therefore would be unable to transparently
decide which columns to retrieve. Consequently, objects from a
given class may be of I<variable size> :
my $objs_A = My::Table->select(-columns => [qw/c1 c2/],
-where => {name => {-like => "A%"}};
my $objs_B = My::Table->select(-columns => [qw/c3 c4 c5/],
-where => {name => {-like => "B%"}};
my $objs_C = My::Table->select(# nothing specified : defaults to '*'
-where => {name => {-like => "C%"}};
Therefore the programmer has much more freedom and control, but of
course also more responsability : in this example, attempts to access
column C<c1> in members of C<@$objs_B> would yield an error.
( run in 0.563 second using v1.01-cache-2.11-cpan-39bf76dae61 )