FYI - This has just been sent to [EMAIL PROTECTED]

Probably best to discuss it there in whatever threads it spawns
(but do post here if you've a driver-development related question
or you're not subscribed to dbi-users)

Tim.

----- Forwarded message from Tim Bunce <[EMAIL PROTECTED]> -----

Delivered-To: [EMAIL PROTECTED]
Date: Thu, 3 Jan 2002 01:25:03 +0000
From: Tim Bunce <[EMAIL PROTECTED]>
To: [EMAIL PROTECTED]
Cc: Matt Sergeant <[EMAIL PROTECTED]>, Tim Bunce <[EMAIL PROTECTED]>
Subject: Important: Subclassing and Merging DBIx::AnyDBD into the DBI

Here's what I'm thinking, and developing, at the moment...
[Please read and think about it all before commenting]
[Also, I've CC'd Matt Sergeant <[EMAIL PROTECTED]>,
please ensure that he's CC'd on any replies. Thanks.]

Firstly, subclassing... (we'll use MyDBI as the example subclass here)

  The "mixed case with leading capital letter" method namespace
  will be reserved for use by subclassing of the DBI. The DBI
  will never have any methods of it's own in that namespace.
  (DBI method names are either all lower case or all upper case.)

  The need to call MyDBI->init_rootclass will be removed.

  Simply calling $dbh = MyDBI->connect(...) will be interpreted
  as a request to have the $dbh blessed into the MyDBI::db class
  (and a $dbh->prepare will return $sth blessed into MyDBI::st).
  A warning will be generated if @MyDBI::db::ISA is empty.

  Also, and this is where it gets interesting, calling:
      DBI->connect(,,, { RootClass => 'MyDBI' })
  will have the same effect as above, with the added feature that
  the DBI will try to automatically load MyDBI.pm for you.
  It'll ignore a failure to load due to the file not existing
  if the MyDBI class already exists.

  This feature dramatically opens up the scope of DBI subclassing.

  The idea behind it is that the $dbh object is no longer 'just'
  encapsulating a simple database connection, it can now encapsulate
  a high-level information repository that can be 'queried' at a
  more abstract level. So instead of just calling low-level
  do/prepare/execute/fetch methods you can now call higher-level
  methods that relate to your own data and concepts. More below.

  Typically a 'Sales Order Handling' database could now be given
  a SalesOrderDBI::db class containing high-level methods that deal
  directly with Sales Order Handling concepts and could do things
  like automatically trigger re-ordering when stocks get low.

  Also consider, for example, that DBD::Proxy would be able to dynamically
  load the subclass on the proxy server instead of the proxy client.
  The subclass can perform multiple DBI method calls before returning
  a result to the client. For example: $ok=$dbh->Check_Available($a,$b)
  on the proxy client triggers a $dbh->Check_Available($a,$b) call
  on the proxy server and that method may perform many selects to
  gather the info before returning the boolean result to the client.
  Performing the selects on the proxy server is far far more efficient.

  In terms of buzzwords, the dynamic loading of subclasses can
  translate into "Encapsulating Business Logic" and thowing in
  the proxy extends that to "3-Tier" :)

  Also, the ability to embed attributes into the DSN may lead to
  some interesting possibilities...
      DBI->connect("dbi:Oracle(PrintError=1,RootClass=OtherDBI):...",...)
  I'm not sure where that road leads but I suspect it'll be
  interesting, though I may disable it by default, or just provide
  a way to do so.


Next, merging in DBIx::AnyDBD functionality...

  Rather than describe Matt Sergeant's excellent DBIx::AnyDBD
  module I'll just describe my plans. You can take a look at
      http://search.cpan.org/search?dist=DBIx-AnyDBD
  to see the obvious inspiration.

  Calling
      $dbh = DBI->connect("dbi:Oracle:foo",,, { DbTypeSubclass => 1 })
  will return a $dbh that's blessed into a class with a name
  that depends on the type of database you've connected to.
  In this case 'DBI::Oracle::db'. @DBI::Oracle::db::ISA=('DBI::db')
  is automatically setup for you, if it's empty, so the inheritance
  works normally.

  For ODBC and ADO connections the underlying database type is
  determined and a class hierarchy setup for you. So an ODBC
  connection to an Informix database, for example, would be blessed
  into 'DBI::Informix::db' which would automatically be setup as
  a subclass of 'DBI::ODBC::db' which would be setup as a subclass
  of 'DBI::db'.

  The DBI will try to automatically load these classes for you.
  It'll ignore a failure to load caused by the file not existing.

  The idea behind this, if it's not dawned on you already, is to
  enable a simple way to provide alternate implementations of
  methods that require different SQL dialects for different
  database types. See below...


Finally, putting it all together...

  These two mechanisms can be used together so
      $dbh = MyDBI->connect("dbi:Oracle:foo",,, { DbTypeSubclass=>1 })
  will return a $dbh blessed into 'MyDBI::Oracle::db'.
  In fact users of DbTypeSubclass are strongly encouraged to also
  subclass into a non-'DBI' root class. They are a natural fit together.

  Imagine, for example, that you have a Sales Order Handling
  database and a SalesOrderDBI::db class containing high-level
  methods like automatically triggering re-ordering when stocks
  get low. Imagine you've implemented this, or just prototyped
  it, in Access and now want to port it to PostgreSQL...

  A typical porting process might now be...
  1/ Break up any large methods that include both high-level business
     logic and low-level database interactions.  Put the low-level
     database interactions into new methods called from the (now
     smaller) original method.
  2/ Add { DbTypeSubclass=>1 } to your connect() method call.
  3/ Move the low-level database interaction methods from the
     SalesOrderDBI::db class into the SalesOrderDBI::Access::db class.
  4/ Implement and test alternate versions using PostgreSQL in the 
     SalesOrderDBI::Pg::db class.
  Since PostgreSQL supports stored procedures you could move some
  of the business logic into stored procedures within the database.
  Thus your Access specific class may contains select statements
  but your PostgreSQL specific class may contain stored procedure calls.


Random extra thoughts...

  AUTOLOAD (ala Autoloader, SelfLoader etc) could be put to good
  and interesting uses in either handling large libraries of queries
  (load in demand) or even automatically generating methods with
  logic based on the method name:
    $dbh->Delete_from_table($table, $key)
  Oh, scary.

  The logic for mapping a connection into a hierarchy of classes
  will be extensible and overridable so that new or special cases
  can be handled, such as considering/including the database version.


Comments welcome. (But please trim replies, and be brief and to the point!)

Tim.

p.s. Don't forget to CC Matt Sergeant <[EMAIL PROTECTED]>

----- End forwarded message -----

Reply via email to