NAME
DBO - Database Object Mapping
SYNOPSIS
# First, generate the modules from the db.
$ dbo-gen -u john -p xxx --all --gen-modules --split-dir=. --module-prefix="Foo" dbname
# Use them..
use DBI;
use DBO;
DBO->dbh(DBI->connect(...));
package Customer;
use base qw(DBO::Base);
Customer->def_data_source ('my_db', 'cust_tbl');
Customer->def_column ('cust_id', { accessor => 'id', auto_increment => 1 });
Customer->def_column ('name'};
Customer->def_accessor ('address', { column => 'addr'});
Customer->def_accessor ('affiliate_id');
Customer->def_accessor ('now', { expr => 'now()', cache => 0} );
Customer->def_primary_key ([qw(cust_id cust_loc)]);
Customer->def_key ([qw(name affiliate_id)]);
Customer->def_has_a ('affiliate', 'Affiliate', +{})
Customer->def_has_a ('affiliate', [qw(name aff_id)], 'Affiliate_table', [qw(c_name id)]);
Customer->def_has_many ('orders', 'Order',+{})
Customer->def_has_many ('orders', 'Order', ['cust_id'], ['id'],+{});
Employee->def_has_many ('supervisors', 'Supervisor',
new DBO::Query->select('super_id')
->from('emp_super')
->join('employee')
->using('emp_id')
->where(+{emp_id => '?'}), sub { shift->emp_id });
Customer->def_select_group ([qw(name address)]);
## Main program
package main;
use DBO;
use Customer;
use Some_Other_DBO_Object;
DBO->resolve(); # IMPORTANT! Do this once, after all 'use' objects.
$all_customers = Customer->selectall_arrayref;
for (@$all_customers){
$_->name( ucfirst lc $_->name );
$_->update;
}
Customer->insert->values($name, $addr, $afid)->insert;
Customer->insert({ name => $name, addr => $addr, affiliate_id => $afid});
$cust = new Customer();
$cust->name($name);
$cust->address($addr);
$cust->affiliate_id($afid);
$cust->insert;
new DBO::Query::Insert(qw(name addr affiliate_id))
->into('cust_tbl')
->values($name,$addr,$afid)
->insert;
Customer->update->set( 'name', $new_name )->where({ cust_id => $cust_id })->update;
new DBO::Query::Update->set( name => '$new_name' )->where( "cust_id = $cust_id")->update;
$cust = new Customer($cust_id);
$cust->name($new_name);
$cust->update;
DESCRIPTION
DBO is a mapping from database tables (or joins) to perl objects. The object accessors are columns in the table (or join).
There are currently two layers to DBO, described below.
LAYER 1 - DBO::Query
The DBO::Query layer is a set of simple object wrappers around standard SQL statements, which maintain their own $dbh and $sth handles, and can remember bind_values. Here is an example:
my $rows = new DBO::Query($dbh)
->select(@column_names)
->from($table)
->selectall_arrayref(@values);
# Same thing:
my $rows = $dbh->selectall_arrayref(
"SELECT @{[join q/,/, @column_names]} FROM $table" );
When used alone (without LAYER 2), the DBO::Query objects offers a few advantages over straight DBI calls:
Advantages of using DBO::Query over DBI
Bind values can be assigned at any time.
Hides SQL and prevents string errors.
Cleaner code.
However, the real power comes in Layer 2, where DBO methods return partially constructed DBO::Query objects.
LAYER 2 - DBO::Base
The DBO::Base layer consists of a base class (DBO::Base) and some glue to the DBO::Query layer. The base class is intended to be subclassed for each table, view, and join in your database. Your subclass will define mappings to the database tables and columns, and will inherit useful methods for storing and retrieving its underlying data. The following example assumes that the 'Customer' object mappings have been defined already (as in the SYNOPSIS):
my $c = new Customer();
$c->name($name);
$c->insert;
$c->update;
$c->delete;
The layer 2 objects make extensive use of DBO::Query (Layer 1) objects. Many Layer 2 methods returns partially constructed DBO::Query objects. For example, to build a query on the Customer table, do this:
my $query = Customer->select->where->({region_code => '?'});
my $customers = $query->selectall_arrayref($region);
my $others = $query->selectall_arrayref($other_region);
There are two important things going on here. First, the $query object behaves like a layer 1 DBO::Query object, but it fetches Customer objects, not rows. Second, the query can be cached and reused just like a DBI prepared statement handle.
API GUIDELINES
These are the guidelines adhered to by the DBO public API. Any exceptions you discover are bugs.
- 1. Column names may be fully qualified.
-
Any API method that expects a column name should also accept the fully qualified column name as "$database.$table.$column". In most cases, the column name by itself is ok.
- 2. Keys are listrefs.
-
All keys (primary keys, foreign keys, etc.) should be specified as a reference to a perl array containing the column names or values. For flexibility, single-column primary keys can be specified as scalars.
- 3. Methods that look like DBI methods should behave like DBI methods.
-
Methods with names borrowed from DBI (e.g, prepare(), execute(), selectall_arrayref()) should behave like their DBI counterparts. If you are familiar with DBI, than there should be no surprises from DBO.
- 4. DBO objects never do more than you expect them to do.
-
It should be clear from your code whether it triggers a database action. For example, if you do not explicitly modify values in a DBO object by calling insert(), update(), delete(), etc., then the database should not be modified. The inverse is also true.
- 5. All DBO objects have a dbh() method.
-
All objects should contain, or have the ability to procure a database handle. Typically, this is done through package globals -- the top level being DBO::dbh(). The idea is that you need only set the $dbh handle once in your code.
- 6. A DBI handle ($dbh) can be passed to most methods.
-
Wherever sensible, DBO::Query and DBO::Base methods will accept a $dbh as an argument. The new(), prepare(), and execute() methods are sensible places to pass a $dbh handle. The $dbh will be safely plucked from the arguments. For example,
$query->execute(@bind_values, $dbh, @more_bind_values);
PUBLIC OBJECTS AND METHODS
This is a partial list of public objects and their methods.
- DBO::Query
-
Represents a query object. You can safely pass a $dbh to most methods, which will be stored for later prepare() and execute() calls.
- new
-
new Query() new Query(column_names) new Query($dbh,column_names)
- from
-
$q->from($table_name, $table_name...)
- join
-
$q->join($right) $q->join($left, $right)
- right_join
-
$q->right_join($right) $q->right_join($left, $right)
- left_join
-
$q->left_join($right) $q->left_join($left, $right)
- where
-
$q->where(EXPR, @bind_values)
- and
-
$q->and (EXPR, @bind_values)
- or
-
$q->or (EXPR, @bind_values)
- prepare
-
$q->prepare() $q->prepare($dbh)
- execute
-
$q->execute(@bind_values) $q->execute($dbh,@bind_values)
- fetchrow_arrayref
-
$q->fetchrow_arrayref()
- fetchall_arrayref
-
$q->fetchall_arrayref()
- fetchcol_arrayref
-
$q->fetchcol_arrayref()
- selectall_arrayref
-
$q->selectall_arrayref(@bind_values) $q->selectall_arrayref($dbh, @bind_values)
- selectcol_arrayref
-
$q->selectcol_arrayref(@bind_values) $q->selectcol_arrayref($dbh, @bind_values)
- clone
-
returns a deep copy. Not finished or tested! Note: The database handle will be shared by the clone, and the internal statement handle will set to undef in the clone.
- DBO::Query::Update
-
Represents an SQL UPDATE.
- new
-
new() new($dbh,[column_names,...])
- table
-
$q->table($table_name)
- set
-
$q->set({ name => value}) $q->set( name => value)
- update
-
Same as prepare->execute
$q->update() $q->update(@bind_values) $q->update($dbh,@bind_values)
- where
-
$q->where(EXPR, @bind_values)
- prepare
-
$q->prepare() $q->prepare($dbh)
- execute
-
$q->execute() $q->execute(@bind_values) $q->execute($dbh,@bind_values)
- DBO::Query::Insert
-
Represents an insertion object.
- new
-
new DBO::Query::Insert() new DBO::Query::Insert(@columns) new DBO::Query::Insert($dbh,[@columns])
- insert
-
Same as prepare->execute
$q->insert() $q->insert(@bind_values) $q->insert($dbh,@bind_values)
- columns
-
$q->columns(names or infos or values) $q->columns([names or infos or values])
- into
-
$q->into($table_name) $q->into($table_info)
- values
-
$q->values($values..) $q->values(Value objects....)
- prepare
-
$q->prepare() $q->prepare($dbh)
- execute
-
$q->execute(@bind_values) $q->execute($dbh,@bind_values)
- clone
-
returns a deep copy. Not finished or tested! @note The database handle will be shared by the clone, and the internal statement handle will set to undef in the clone.
- DBO::Query::Delete
-
- new
-
$d = new DBO::Query::Delete() $d = new DBO::Query::Delete($dbh)
- from
-
$d->from($tables)
- delete
-
Same as prepare->execute.
$d->delete() $d->delete(@bind_values) $d->delete($dbh,@bind_values)
- prepare
-
$d->prepare() $d->prepare($dbh)
- execute
-
$d->execute() $d->execute(@bind_values) $d->execute($dbh,@bind_values)
- where
-
$d->where(EXPR, @bind_values)
- and
-
$d->and (EXPR, @bind_values)
- or
-
$d->or (EXPR, @bind_values)
- DBO
- DBO::Base
-
This is the base class that provides all the functionality for your subclasses. It is not meant to be called directly.
- new
-
Do not override the new() method. Override the init() method instead.
$new_obj = new MyClass() $existing_obj = new MyClass($PrimaryKeyValue) ## these are not as useful $obj = new MyClass($pk_column) $obj = new MyClass([$pk_part1, $pk_part2] ) $obj = new MyClass($dbh)
- init
-
DBO will call the init() method from within new(). The default init() method is empty, and is meant to be optionally overridden in the subclass. For example,
sub init { my ($self, @args) = @_; ## etc. }
Where @args contains the arguments that were passed to new() that DBO::Base did not recognize. e.g., if you pass a $dbh to new(), then it will not be passed to through init() because DBO snatched it up.
- select
-
Returns a query that can be used to fetch objects of type MyClass (based on primary key). The @columns array may be column names, aliases, or accessors.
$query = MyClass->select(@optional_columns) $list_of_MyClass_objects = $query->selectall_arrayref;
- select_count
-
Returns a query object that will return
COUNT(*)
instead of an object.MyClass->select_count()
- selectall_arrayref
-
Returns a reference to an array of objects of type MyClass.
MyClass->selectall_arrayref() MyClass->selectall_arrayref(@bind_values) MyClass->selectall_arrayref($dbh, @bind_values)
- insert
-
When called as an object method, it immediately inserts a row in the database. If any part of the primary key is an auto_increment column, then the new primary key is fetched. When called as a class method, it returns a DBO::Query::Insert object.
$obj->insert; # SQL INSERT was performed. $query = MyClass->insert; # SQL was not executed.
- update
-
When called as an object method, it immediately updates the corresponding row in the database. If this objects has no primary key value, then an exception is raised. When called as a class method, it returns a DBO::Query::Update object.
$obj->update; # SQL UPDATE was performed. $query = MyClass->update; # SQL was not executed.
- delete
-
When called as an object method, it immediately deletes the row in the database corresponding to this object. When called as a class method, it returns a query that can be used to delete objects from the corresponding table.
$obj->delete; # SQL DELETE was executed. $query = MyClass->delete; # No SQL was executed.
- def_data_source
-
Define the data source for this object.
MyClass->def_data_source( 'database_name', 'table_name');
- def_accessor ( $accessor_name, \%options )
-
Customer->def_accessor( 'id', { column => 'cust_id', auto_increment => 1 } );
Options (explained below):
column => NAME auto_increment => BOOLEAN select_when_null => VALUE update_when_empty => VALUE select_trim => BOOLEAN update_trim => BOOLEAN string_mangle => BOOLEAN
- column => NAME
-
The column name in the database. The default is the accessor name.
- auto_increment => BOOLEAN
-
The column value is generated by the database, and should not be INSERTED.
- select_when_null => VALUE
-
Select VALUE when a column's value is NULL.
- update_when_empty => VALUE
-
Use VALUE instead of the empty string for updates and inserts.
- select_trim => BOOLEAN
-
Trim leading and trailing whitespace after selecting the value from the database.
- update_trim => BOOLEAN
-
Trim leading and trailing whitespace before updating or inserting.
- string_mangle => BOOLEAN
-
Apply all string mangling features to this column. This option is just a shortcut for:
{ select_trim => 1, update_trim => 1, select_when_null => '', update_when_empty => undef }
- def_column
-
Same as def_accessor, but the column name is first, and you can't specify an EXPR as a column. In the options hash, you can provide an
accessor
key to rename the accessor. - def_has_a
-
Define a relationship
MyClass->def_has_a ('affiliate', [qw(name aff_id)], 'Affiliate_table', [qw(c_name id)]); MyClass->def_has_a ($accessor, $many_key, $table, $one_key, $opts)
- def_has_many
-
Define a relationship.
MyClass->def_has_many ( $accessor, $table, [$one_columns], [$many_columns], $opts ); MyClass->def_has_many ( $accessor, $table, $opts ); MyClass->def_has_many ( $accessor, $table, $query, $bind_routine, $opts )
- def_select_group
-
Define a selection group.
MyClass->def_select_group ([ $cols ]) MyClass->def_select_group ( $group => [ $cols...] )
NAMESPACE ISSUES
Since your module ISA DBO::Base, it inherits all of DBO::Base's methods and attributes. Here is a comprehensive list of the methods your module will inherit. Any omissions should be considered a bug.
- Public Methods
-
These methods are documented elsewhere in this manual.
def_accessor def_column def_data_source def_has_a def_has_many def_key def_primary_key def_select_group dbh_reader dbh_writer delete init insert select select_count selectall_arrayref update
- Private Methods
-
These methods are private, and should not be overridden by your modules.
new # do not override. use init() instead _dbo* # do not call or override any method named _dbo*
PERFORMANCE
The DBO layer does not increase the asymptotic running time of your code as compared to straight DBI. That should keep most of you happy.
For the rest of you speed freaks, here are the ways that DBO optimizes for performance:
calls prepare_cached() instead of prepare()
selectall_arrayref delays object instantiation until necessary
pools reader/writer database handles (TODO)
reuses objects by primary key (TODO)
VERSION
DBO $Revision: 1.26 $
AUTHOR
John Millaway <millaway@cpan.org>