NAME
Class::AutoDB - Almost automatic object persistence coexisting with human-engineered database
VERSION
Version 1.28
SYNOPSIS
# code that defines persistent class
#
package Person;
use base qw(Class::AutoClass);
use vars qw(@AUTO_ATTRIBUTES %AUTODB);
@AUTO_ATTRIBUTES=qw(name sex id friends);
%AUTODB=
(collection=>'Person',
keys=>qq(name string, sex string, id integer));
Class::AutoClass::declare;
########################################
# code that uses persistent class
# create and store new objects
#
use Class::AutoDB;
use Person;
my $autodb=new Class::AutoDB(database=>'test'); # open database
# make some objects. not yet stored in database
my $joe=new Person(name=>'Joe',sex=>'M',id=>1);
my $mary=new Person(name=>'Mary',sex=>'F',id=>2);
my $bill=new Person(name=>'Bill',sex=>'M',id=>3);
# set up friends lists. each is a list of Person objects
$joe->friends([$mary,$bill]);
$mary->friends([$joe,$bill]);
$bill->friends([$joe,$mary]);
# store objects in database
$autodb->put_objects;
# retrieve existing objects
#
use Class::AutoDB;
use Person;
my $autodb=new Class::AutoDB(database=>'test');
# retrieve list of objects
my @persons=$autodb->get(collection=>'Person'); # everyone
my @males=$autodb->get(collection=>'Person',sex=>'M'); # just the boys
# do something with the retrieved objects, for example, print friends lists
for my $person (@persons) {
my @friend_names=map {$_->name} @{$person->friends};
print $person->name,"'s friends are @friend_names\n";
}
# retrieve and process objects one-by-one
my $cursor=$autodb->find(collection=>'Person');
while (my $person=$cursor->get_next) {
# do what you want with $person, for example, print friends list
my @friend_names=map {$_->name} @{$person->friends};
print $person->name,"'s friends are @friend_names\n";
}
# connect auto-persistent objects with engineered tables
# assume database has human-engineered tables
# Dept(id int, name varchar(255)), EmpDept(emp_id int, dept_id int)
# this query retrieves the names of Joe's departments
use DBI;
my $dbh=$autodb->dbh;
my $depts=$dbh->selectcol_arrayref
(qq(SELECT Dept.name FROM Dept, EmpDept, Person
WHERE Dept.id=EmpDept.dept_id AND EmpDept.emp_id=Person.id
AND Person.name='Joe'));
########################################
# new features in verion 1.20
# retrieve objects using SQL
# assuming the above database (with human-engineered tables Dept and EmpDept),
# this query retrieves Person objects for employees in the toy department
my @toy_persons=
$autodb->get
(sql=>qq(SELECT oid FROM Dept, EmpDept, Person
WHERE Dept.id=EmpDept.dept_id AND EmpDept.emp_id=Person.id
AND Dept.name='toy'));
# retrieve all objects
my @all_objects=$autodb->get;
# delete objects
#
$autodb->del(@males); # delete the boys
DESCRIPTION
This class works closely with Class::AutoClass to provide almost transparent object persistence that can coexist with a human-engineered database. The auto-persistence mechanism provides hooks for connecting the two parts of the database together.
Caveat: The current version only works with MySQL.
For applications where performance is not pressing, you can use this class for all your persistent data. In other cases, you can use it for structurally complex, but low volume, parts of your database, while storing performance-critical data in carefully engineered tables. This class is also handy for prototyping persistent applications and lets you incrementally replace auto-persistent components with engineered tables as your design proceeds.
Persistence model
This section presents a brief overview of the class. Please see later sections for details.
You declare a class to be persistent by defining the %AUTODB variable in the module. Class::AutoClass (specifically, Class::AutoClass::declare) uses %AUTODB to set everything up.
In simple cases, you set %AUTODB to 1, eg,
%AUTODB=1;
This causes your class to be persistent, but provides no way to search for objects and no convenient hook for connecting these objects to the engineered parts of your database (unless a superclass provides these capabilities).
More typically, you set %AUTODB to a hash of the form
%AUTODB=(
collection=>'Person',
keys=>qq(name string, sex string, id number));
This associates a persistent collection, called 'Person', with your class and says that the Person collection has three search keys: 'name', 'sex', and 'id'. Collections provide a way to search for objects and are the hooks for connecting auto-persistent objects with the rest of your database.
After setting up your classes to use Class::AutoDB, you use it as follows.
First you connect your program to the database by running Class::AutoDB's 'new' method. Then you typically invoke 'get' or 'find' to retrieve some number of "top level" objects. Then, you operate on objects as usual. If you touch an object that has not yet been retrieved from the database, the system will automatically fetch it for you.
The retrieval process reconstructs the original object network. You never get duplicate copies of a persistent object no matter how many times you retrieve the object and no matter what path you use to reach it. In our running example, it doesn't matter whether you get all Person objects first and then traverse their friends lists, or get Person objects one-by-one and and traverse their friends lists as you go. The end result is the same: you end up with one copy of each Person object, and the various friends lists point to these objects as expected.
You can store specific objects in the database by invoking the 'put' method on the object (deprecated) or on AutoDB (preferred). You can store all objects using the 'put_objects' method on AutoDB.
$joe->put; # store one object (deprecated form)
$autodb->put($joe); # store one object (preferred form)
$autodb->put_objects; # store all objects
'put' stores the object and any Perl structures (typically ARRAYs or HASHes) to which the object points except for other persistent objects. The storage process converts references to persistent objects into a representation (called an 'oid' -- stands for 'object identifier') that the retrieval process can use later to fetch the object on demand.
Thus, when 'putting' the 'Joe' object, the software stores Joe's 'name', 'sex', and 'id' (which are simple strings and numbers), and an ARRAY of oids representing Joe's friends. Your program is responsible for making sure each friend object is also stored.
The difference between persistent and non-persistent objects is that the system treats each persistent object as an independent unit but considers non-persistent objects to be part of the persistent objects that point to them. If two persistent objects point to the same non-persistent object, the system will store separate copies of the non-persistent object when you 'put' the persistent ones, and will retrieve separate copies of the non-persistent object when you retrieve the persistent ones. This is usually an error but may be useful in special circumstances.
Suppose we extend our running example by adding a Name class to represent the 'name' attribute of Person objects. If you expect each Person to have a separate Name object (which seems likely), it will work fine for Name to be non-persistent. However, if you want Persons to share Names, you should declare Name to be persistent.
As of version 1.20, it is possible to delete persistent objects using the 'del' method. Deleting an object removes it from memory and the database. However, it does not remove references to the deleted objects that may be held in program variable or other objects. In boolean context, a deleted object evaluates to undef; in a string context it evaluates to an empty string.
Defining a persistent class
You declare a class to be persistent by defining the %AUTODB variable in the module. Class:::AutoDB works closely with Class::AutoClass and in typical usage, Class::AutoClass::declare uses %AUTODB to set everything up.
There is no way to arrange for some instances of a class to be persistent while others are not. Also, there is no way at present for a subclass to be nonpersistent if any of its superclasses are persistent.
%AUTODB specifies two properties of the persistent class (both optional).
- 1. Collections
-
Collections provide a way to search for objects and are the hooks for connecting auto-persistent objects with the rest of your database. You can define any number of collections for your class (including none), and each collection can have any number of search keys (again including none).
A collection can contain objects from many different classes. (This is Perl after all -- what else would you expect ??!!)
- 2. Transients
-
Transients are keys of your object that you do not want Class::AutoDB to store in the database when you 'put' the object. These may be values that are computed as needed and kept in the object for convenience, or items tied to the program's execution (eg, an open filehandle). Needless to say, transient keys are not resurrected when objects are retrieved.
Collections
Collections contain references to objects (specifically oids), not objects themselves. This is why it works to have multiple collections per class, and multiple classes per collection.
Search keys are typed: valid types are string, integer, float, object, or the phrase list(<data type>), eg, list(integer). It also works to use abbreviations for the types; any prefix is fine. These are translated into MySQL types as follows:
string -> longtext
integer -> int
float -> double
object -> bigint (unsigned)
The types 'object' and 'list(object)' only work on objects whose persistence is managed by Class::AutoDB.
For each collection, the system creates a relational table with an 'oid' column to connect the collection to the object being described, and additional columns for the search keys. List-valued search keys are stored in separate link tables. For our running example, the system would create the following table:
Person(oid bigint, name longtext, sex longtext, id int)
If multiple classes use the same collection, it is okay (even sensible!) for one class to define the search keys, while the other classes just refer to the collection by name. If multiple classes define search keys for the collection, the definitions are 'added': the search keys for the collection are the union of all the definitions. It is an error for multiple classes to give different types to the same search key.
A similar situation arises in class hierarchies. A subclass inherits the collections used by its superclasses. The subclass need not define %AUTODB, but if it does, its values are 'added' to those of its superclasses: the system associates the class with the union of the collections specified by the class and its superclasses; if the subclass defines additional search keys for some of these collections, the systems adds these to the search keys defined in the superclasses. It is an error for a subclass to define the type of a search key differently than its superclasses.
It is also possible to programmatically define collections, using the 'register' method of AutoDB.
Forms of %AUTODB
There are several valid forms for %AUTODB.
unset
If you leave %AUTODB unset, the class inherits the persistence properties of its superclasses. If all superclasses are nonpersistent, the class is also nonpersistent.
single false value, eg, %AUTODB=0, or %AUTODB=undef
This is presently illegal. We reserve this form to specify that a class be nonpersistent even if its superclasses are persistent.
%AUTODB=1
This declares the class to be persistent but defines no collections or transients. The class uses the collections and transients defined by its superclasses, if any. Note that setting %AUTODB to 1 really sets it to the hash (1=>undef).
The remaining forms set %AUTODB to a hash whose elements specify the class's collections or transients.
There are several forms for specifying collections.
standard single collection form
The hash has elements 'collection' and 'keys'. For example
%AUTODB=( collection=>'Person', keys=>qq(name string, sex string, id integer));
The meaning is obvious: 'collection' gives the name of the collection, and 'keys' its search keys.
You can specify 'keys' as a comma-separated string of "key type" pairs, a HASH of key=>type pairs, or an ARRAY of keys. In the first two cases, the type is optional; in all cases, type defaults to 'string'. Here are some examples.
keys=>qq(name string, sex string, id integer) keys=>'name, sex, id integer' keys=>{name=>'string', sex=>'string', id=>'integer'} keys=>{name=>'', sex=>'', id=>'integer'} keys=>[qw(name sex id)]
The remaining forms can specify any number of collections (including none or one).
The examples that follow extend our running example with a second collection, 'HasName', which contains objects of many classes which have 'name' attributes.
HASH of collections form
The hash has a 'collections' element ('collection' also works) and does not have a 'keys' element. The 'collections' element is a HASH of collection-name=>keys pairs. As above, you can specify the keys as strings of "key type" pairs, HASHes of key=>type pairs, ARRAYs of keys, or a single key.
%AUTODB=( collections=>{Person=>{name=>'string', sex=>'string', id=>'integer'}, HasName=>'name'});
list of collections form
The hash has a 'collections' element ('collection' also works) and may have a 'keys' element. The 'keys' element, if present, applies to all collections. This can make sense if a base class is defining core properties of several collections with the expectation that subclasses will refine the collections with additional keys.
The 'collections' element can be a whitespace- or comma-separated string of collection names or an ARRAY of collection names.
%AUTODB=(collections=>'Person HasName'); %AUTODB=(collections=>[qw(Person HasName)]);
This form is best used when the collections are defined elsewhere.
Transients are specified by a hash elements whose key is 'transients'. In the following examples, imagine that Person objects have keys 'name_prefix' and 'sex_word'; the former might contain honorifics like 'Dr' extracted from the name; the latter might be full word sex descriptors computed from the single letter codes, eg 'female'.
You can specify transients keys as a whitespace- or comma-separated string of keys, or an ARRAY of keys.
transients=>qq(name_prefix sex_word)
transients=>[qw(name_prefix sex_word)]
Putting it together, here are two examples of typical usage.
%AUTODB=(
collection=>'Person',
keys=>qq(name string, sex string, id integer),
transients=>qq(name_prefix sex_word));
%AUTODB=(
collections=>{Person=>qq(name string, sex string, id integer),
HasName=>'name'},
transients=>[qw(name_prefix sex_word)]);
Type mismatches
If you store data of the wrong type into a search key, Perl-ish data conversion occurs. (It might be better for the software to throw an error).
a non-numeric value stored in a numeric key is converted to 0; this includes references
a reference stored in a string key is stringified, yielding something like HASH(0x18a41e0); this is probably not what you had in mind
any non-persistent value stored in an object key is converted to NULL
Defining collections programmatically
It is also possible to programmatically define collections using the 'register' method of AutoDB. Every valid form of %AUTODB also work as the argument to 'register'. In addition, 'register' can take a 'class' parameter which specifies a class to be associated with the collections being defined.
'class' can be a whitespace- or comma-separated string of class names, or an ARRAY of class names. Here are two examples.
$autodb->register(
collections=>{Person=>qq(name string, sex string, id integer),
HasName=>'name'});
$autodb->register(
class=>'Person', collections=>'Person, HasName');
When you 'register' new collections or add search keys to existing ones (as in the first example above), the system creates or alters database tables as needed to implement your changes. These effects are persistent, of course.
When you 'register' new associations between classes and collections (as in the second example above), these changes are in effect for the current session only. This makes it possible to dynamically change the collections into which new objects are put.
Creating and initializing the database
Before you can use Class::AutoDB, you have to create a MySQL database that will hold your data. We do not provide a means to do this here, since you may want to put your AutoDB data in a database that holds other data as well. The database can be empty or not. AutoDB creates all the tables it needs -- you need not (and should not) create these yourself.
Important note: Hereafter, the term 'database' refers to the tables created by AutoDB. Phrases like 'create the database' or 'initialize the database' refer to these tables only, and not the entire MySQL database that contains the AutoDB database.
We provide methods to create or drop the entire database (meaning, of course, the AutoDB database, not the MySQL database) or to create individual collections.
Class::AutoDB maintains a registry that describes the collections stored in the database. The software registers collections as it encounters class definitions . The system consults the registry when running queries, when writing objects to the database, and when modifying the database schema.
When 'new' connects to the database, it reads the registry saved from the last time Class::AutoDB ran. It merges this with an in-memory registry that generally reflects the currently loaded classes. 'new' merges the registries, and stores the result for next time if the new registry is different from the old.
Caveat: You can only have one active AutoDB object at a time. Grievous errors will occur if you run 'new' a second time on a different database. Sorry. We will fix this.
Current design
Caveat: The present implementation assumes MySQL. It is unclear how deeply this assumption affects the design.
Every object is stored as a BLOB constructed by a slightly modified version of Data::Dumper. The database contains a single 'object table' for the entire database whose schema is
create table _AutoDB (
oid bigint unsigned not null,
primary key (oid),
object longblob
);
The oid is a unique object identifier assigned by the system. An oid is a permanent, immutable identifier for the object.
For each collection, there is one table we call the base table that holds scalar search keys, and one table per list-valued search key. The name of the base table is the same as the name of the collection; there is no way to change this at present. For our Person example, the base table would be
create table Person (
oid bigint unsigned not null, --- foreign key pointing to _AutoDB
primary key (oid), --- also primary key here
name longtext,
sex longtext,
id int
);
If a Person has a significant_other key (also a Person), the table would look like this:
create table Person (
oid bigint unsigned not null, --- foreign key pointing to _AutoDB
primary key (oid), --- also primary key here
name longtext,
sex longtext,
id int,
significant_other bigint unsigned --- foreign key pointing to _AutoDB
);
The data types specified in the 'keys' parameter are used to define the data types of these columns. They are also used to ensure correct quoting of values bound into SQL queries. It is safe to use 'string' as the data type even if the data is numeric unless you intend to run 'raw' SQL queries against the database and want to do numeric comparisons.
For each list valued search key, we need another table which (no surprise) is a classic link table. The name is constructed by concatenating the collection name and key name, with a '_' in between.
If 'friends' were a search key in the Person collection. the link table would be
create table Person_friends (
oid bigint unsigned not null, --- foreign key pointing to _AutoDB
friends bigint unsigned --- foreign key pointing to _AutoDB
--- (will be a Person)
);
A small detail: since the whole purpose of these tables is to enable querying, indexes are created for each column by default (indexes can be turned off by specifying index=>0 to the AutoDB constructor).
When the system stores an object, it converts any object references contained therein into the oids for those objects. In other words, objects in the database refer to each other using oids rather than the in-memory object references used by Perl. There is no loss of information since an oid is a permanent, immutable identifier for the object.
When an object is retrieved from the database, the system does NOT immediately process the oids it contains. Instead, the system waits until the program tries to access the referenced object at which point it automatically retrieves the object from the database. In a future release, options may be provided to retrieve oids earlier.
If a program retrieves the same oid multiple times, the system short circuits the database access and ensures that only one copy of the object exists in memory at any point in time. If this weren't the case, a program could end up with two objects representing Joe, and an update to one would not be visible in the other. If both copies were later stored, one update would be lost. The core of the solution is to maintain an in-memory hash of all fetched objects (keyed by oid). The software consults this hash when asked to retrieve an object; if the object is already in memory, it is returned without even going to the database.
Deleting an object converts its in-memory representation to a special kind of deleted-oid, removes the object from all its collections, and changes its BLOB in the object column of the _AutoDB table to NULL. We leave the object's oid in the _AutoDB table to allow better error handling on subsequent attempts to access the object. See "Object deletion details" for further discussion.
UNIVERSAL methods
UNIVERSAL, the base class of everything, defines four methods: isa, DOES, can, and VERSION. Logically these are class methods, because they return the same answer for all objects of a given class. In practice, programmers frequently invoke these on objects.
When invoked on an oid, the system intercepts the call and redispatches the method to the oid's real class. The system does not retrieve the object from the database.
When invoked on a deleted-oid, the system intercepts the call and generates an error to be consistent with how all other method invocations are handled. See "Object deletion details".
Data::Dumper details
We rely on "freezer" and "toaster" methods to convert references to persistent objects into oids when objects are stored, and convert oids back to references when objects are fetched. The native support for "freezer" and "toaster" methods in Data::Dumper (as of version 2.125) falls short of our needs in two respects:
- 1. Data::Dumper requires that the "freezer" method modify the object being dumped, rather than letting the method return a modified copy of the object.
- 2. If any class has a "toaster" method, Data::Dumper assumes all classes do and emits a call to the method even if the object in question can't do it.
We modified Data::Dumper to fix these problems. To avoid any confusion with the official version of the code, we renamed the modified version Class::AutoDB::Dumper and include it in our code tree.
Data::Dumper provides both pure Perl and much faster C (.xs) implementations. We modified both implementations.
Caveat: The build process automatically compiles the C implementation if your system has a C compiler. We see a few compiler warnings in this process. These seem benign and can be ignored. The Data::Dumper docs claim that the code will fall back to the pure Perl implementation if the C version is not available. We haven't checked this claim.
Object deletion details
Object deletion is a bit messy, since it is not a Perl concept. In standard Perl, an object exists until all references to the object cease to exist. As a result, the programmer never has to worry about stale references. The situation changes when we introduce object deletion: it is now possible for a variable to contain a reference pointing to an object that has been deleted; likewise, an object can contain a reference (technically an Oid) pointing to a deleted object.
The closest Perl analog is weak references. The standard Perl rule stated above -- "an object exists until all references to the object cease to exist" -- does not apply to weak references. A weak reference can go stale (in other words, point to an object that no longer exists). When this happens, Perl sets the reference to undef, and any attempt to access the object via the stale reference generates an error.
We adopt similar semantics for object deletion. We do not convert stale references to undef (would require delving into Perl internals -- doable but not worth the effort), but we detect attempts to invoke methods on deleted objects and 'confess' with appropriate error messages. This complicates the application, but there seems to be no better choice.
An application that uses object deletion extensively has to guard against attempts to invoke methods on deleted objects. One way to do this is to test the object before accessing it. For example, if $object contains a reference to an object that might be deleted, you might use this code snippet:
if ($object) { # $object will test 'true' if it still exists
# okay to invoke methods on $object here
} else { # 'else' $object has been deleted
# deal with deleted object here
}
To avoid such complications, we recommend using object deletion only in situations where you can confidently remove all references to the deleted objects. For example, we use it when re-initializing entire data structures.
The 'del' method operates on the in-memory and database representations of the objects being deleted.
It converts the in-memory representation to an OidDeleted, which is a special kind of Oid. Any attempt to invoke application methods on an OidDeleted confesses.
On the database, 'del' updates the object table (_AutoDB) and all tables implementing collections that contain the object. In _AutoDB, 'del' sets the 'object' column to NULL thereby removing the object's BLOB representation from the database; it leaves the object's oid in _AutoDB so we can detect future attempts to access the deleted object and generate an appropriate error message. (If we were to delete the oid, it would look like the object was never stored, and future accesses would generate a misleading error message). In collection tables, 'del' removes all rows representing the object; the code basically deletes the object from any table that 'put' would have put it in.
'del' does not remove references to the deleted object as this would require scanning the entire database.
Queries ('get', 'find') will never retrieve a deleted object (although they may retrieve objects containing references to deleted object), and 'count' will not include deleted objects in its count.
METHODS AND FUNCTIONS
'new' and manage database connections
new
Title : new
Usage : my $autodb=new Class::AutoDB database=>'test'
-- OR --
my $autodb=new Class::AutoDB database=>'test',host=>'localhost',
user=>'fake',password=>'fake'
-- OR --
my $autodb=new Class::AutoDB database=>'test',create=>1
-- OR --
my $autodb=new Class::AutoDB database=>'test',drop=>1
-- OR --
my $autodb=new Class::AutoDB database=>'test',alter=>1
-- OR --
my $autodb=new Class::AutoDB database=>'test',read_only_schema=>1
-- OR --
many other combinations
Function: Connect to database, control schema modification
Returns : New Class::AutoDB object
Args : Connection parameters (MySQL only, at present). All except 'database'
are optional
database Database name. No default
host Database host (or server). Defaults to localhost
server Synonym for 'host'
user Database username. Defaults to current user, more
precisely, USER environment variable
password Database user's password. Defaults to no password
pass Synonym for 'password'
dbd Name of DBD driver. Defaults to 'mysql' which is only one
currently supported
dsn DSN (data source) string in the format supported by DBD
driver. For MySQL, the format is
"dbi:mysql:database_name[:other information]". See DBI and
DBD::mysql. Usually computed from other parameters
socket MySQL socket. Usually not defined
sock Synonym for 'socket'
port Database server port. Usually not defined
timeout MySQL inactive session timeout in seconds. The system
default (8 hours unless changed by your database admin) is
usually fine
dbh Connected database handle returned by DBI::connect.
'new' usually gets this by calling DBI::connect itself
The connection parameters provided are ones we find useful in our
work. Many more are possible. If you need other parameters, you can
construct a DSN or open a DBH in your code and pass it in.
Schema modification parameters
Control schema changes carried out automatically by 'new'. Illegal to
set more than one to true. When all left unspecified or undef, system
adopts this default:
1) if database does not exist, it is created;
2) if database exists, but some needed collections do not exist, they
are created;
3) existing collections are not altered.
create Controls whether the database may be created.
undef see default described above
true forces database creation
defined but false database not created even if it does
not exist
alter Controls whether schema may be altered
undef see default described above
true schema changes allowed. collections and
search keys added as needed
defined but false schema alterations not allowed
drop Controls whether database dropped
true database dropped
any false value database not dropped
read_only_schema
Single option to turn off 'create', 'alter', 'drop'
true no schema changes allowed
any false value schema changes allowed under control of
other parameters
AutoDB attributes
You can access many parameters to 'new' using method notation. Most of these should be set only by 'new', but this is not enforced. Attributes related to the database connection, eg, 'database', 'host', etc., are computed from the DBH or retrieved from the database server if the DBH is set.
To get the value of attribute xxx, just say
$xxx=$autodb->xxx;
To set an attribute, say
$autodb->xxx($new_value);
The available attributes are
database Database name
host Database host (or server)
server Synonym for 'host'
user Database username
password Database user's password
pass Synonym for 'password'
dbd Name of DBD driver. Must be 'mysql' at present
dsn DSN (data source) string in the format supported by DBD driver
socket MySQL socket. Usually not defined
sock Synonym for 'socket'
port Database server port. Usually not defined
dbh Connected database handle returned by DBI::connect
timeout MySQL inactive session timeout in seconds. The system default (8 hours (28800 seconds) unless changed by your database admin) is usually fine. The value returned is retrieved from the database server and can change out from under you. If the server decides the timeout is too small, it resets it to the system default.
read_only_schema Single option to turn off 'create', 'alter', 'drop'
Note that the 'create', 'alter', and 'drop' parameters cannot be accessed in this way, because the methods that performed the indicated actions have the same names. Sorry.
renew
Title : renew
Usage : $autodb->renew(read_only_schema=>1)
Function: Reinitialize $autodb object
Returns : same Class::AutoDB object
Args : same as 'new'
Notes : Uses parameters set in 'new' unless changed by args
Reinitializes the AutoDB object, disconnects from the database, and reconnects. This method is rarely needed; usually you want reconnect .
connect
Title : connect
Usage : $autodb->connect
-- OR --
$autodb->connect(database=>'test')
Function: Connect to database. Does nothing if already connected
Returns : database handle (DBH) if successful, else undef
Args : same as 'new'
Notes : Uses connections parameters set in 'new' unless changed by args
'new' calls this method to establish the database connection. Since, at present, 'new' will fail if it is unable to make the connection, there is little reason to call 'connect' later. You should use reconnect to change the connection.
disconnect
Title : disconnect
Usage : $autodb->disconnect
Function: Disconnect from database
Returns : undef
Args : none
Users rarely need to run this, because connections go away automatically when your program ends.
reconnect
Title : reconnect
Usage : $autodb->reconnect
-- OR --
$autodb->reconnect(database=>'test')
Function: Disconnect from database and then connect
Returns : database handle (DBH) if successful, else undef
Args : same as 'new'
Notes : Uses connections parameters set in 'new' unless changed by args
This is the method to use if you want to change connection parameters, such as the database or user. It's okay to use this even if you have already done a 'disconnect'.
is_connected
Title : is_connected
Usage : $autodb->is_connected
Function: Tell if there is a usuable database connection
Returns : undef
Args : none
This really just sees if the DBH is set. Use ping to test if the connection is live.
ping
Title : ping
Usage : $autodb->ping
Function: Tell if the database connection is live
Returns : undef
Args : none
We recommend using this as a last resort, because of potential performance problems. In most cases, is_connected is adequate. Actually, in most cases, you can just assume that there is a connection, because 'new' will fail if it cannot establish the connection, and the underlying DBI software will reconnect automatically if the connection breaks.
Queries
The methods described here operate on Class::AutoDB objects. See METHODS AND FUNCTIONS - Cursors for related methods operating on Class::AutoDB::Cursor objects.
get
Title : get
Usage : my @males=$autodb->get(collection=>'Person',name=>'Joe',sex=>'M')
-- OR --
my $males=$autodb->get(collection=>'Person',name=>'Joe',sex=>'M')
-- OR --
my @males=$autodb->get(collection=>'Person',
query=>{name=>'Joe',sex=>'M'})
-- OR --
my $males=$autodb->get(collection=>'Person',
query=>{name=>'Joe',sex=>'M'})
-- OR --
my @all_objects=$autodb->get
-- OR --
my $all_objects=$autodb->get
-- OR --
my @joe_bill=$autodb->get(sql=>qq(SELECT oid FROM Person
WHERE name='Joe' OR name='Bill'))
-- OR --
my $joe_bill=$autodb->get(sql=>qq(SELECT oid FROM Person
WHERE name='Joe' OR name='Bill'))
Function: Execute query and return results
Returns : list or ARRAY of objects satisfying query
Args : collection Name of collection being queried
query search_key=>value pairs. Each search key must be defined
for collection. Each value must be single value of correct
type for search key. For all types except 'object' or
'list(object)', value must be simple scalar (string or
integer). For 'object' or 'list(object)', value must be
persistent object
sql raw SQL that selects oids of objects to be retrieved
other args interpreted as search_key=>value pairs
Notes : With no arguments, retrieves all objects.
Okay to include both search keys and SQL
search_key=>value pairs are ANDed. For list types, the query is true if any element on list has the value. If a collection has a search key named 'collection', you must use an explicit 'query' arg to include it in a query.
The SQL argument must select a single column which is interpreted as containing the oids of objects to be retrieved. Our code adds additional SQL that uses the oids to select objects from _AutoDB.
If you supply search_key=>value pairs and a SQL statement, they are ANDed.
find
Title : find
Usage : my $cursor=$autodb->find(collection=>'Person',name=>'Joe',sex=>'M')
-- OR --
my $cursor=$autodb->find(collection=>'Person',
query=>{name=>'Joe',sex=>'M'})
-- OR --
my $cursor=$autodb->find
-- OR --
my $cursor=$autodb->find(sql=>qq(SELECT oid FROM Person
WHERE name='Joe' OR name='Bill'))
Function: Execute query.
Returns : Class::AutoDB::Cursor object which can be used to retrieve results
Args : same as 'get'
Notes : same as 'get'
count
Title : count
Usage : my $count=$autodb->count(collection=>'Person',name=>'Joe',sex=>'M')
-- OR --
my $count=$autodb->count(collection=>'Person',
query=>{name=>'Joe',sex=>'M'})
-- OR --
my $cursor=$autodb->find
-- OR --
my $cursor=$autodb->find(sql=>qq(SELECT oid FROM Person
WHERE name='Joe' OR name='Bill'))
Function: Count number of objects satisfying query
Returns : number
Args : same as 'get'
Notes : same as 'get'
oid
Title : oid
Usage : my $oid=$autodb->oid($object)
Function: Access object's oid (immutable object identifier)
Returns : oid as number, NOT Class::AutoDB::Oid object. undef if argument not
persistent
Args : persistent object, Class::AutoDB::Oid object, or
Class::AutoDB::OidDeleted object
Cursors
The methods described here operate on Class::AutoDB::Cursor objects returned by "find". See METHODS AND FUNCTIONS - Queries for related methods operating on Class::AutoDB objects.
get
Title : get
Usage : my @males=$cursor->get
-- OR --
my $males=$cursor->get
Function: Retrieve results of query associated with cursor
Returns : list or ARRAY of objects satisfying query
Args : none
It is possible to mix 'get' and 'get_next' operations. If some 'get_next' operations have been run on cursor, 'get' retrieves remaining objects
get_next
Title : get_next
Usage : my $object=$cursor->get_next
Function: Retrieve next result for cursor or undef if there are no more
Returns : object satisfying query or undef
Args : none
Notes : Allows simple while loops to iterate over results as in SYNOPSIS
count
Title : count
Usage : my $count=$cursor->count
Function: Count number of objects satisfying query associated with cursor
Returns : number
Args : none
reset
Title : reset
Usage : $cursor->reset
Function: Re-execute query associated with cursor.
Returns : nothing
Args : none
Notes : Subsequent 'get' or 'get_next' operation will start at beginning
Updates
put
Title : put
Usage : $autodb->put(@objects)
Function: Store one or more objects in database
Returns : nothing
Args : list of persistent objects, Oids, or OidDeleteds
Notes : nop (does nothing) on Oids and OidDeleteds
The difference between 'put' and 'put_objects' is that when called with no objects, 'put' does nothing, while 'put_objects' stores all persistent objects
put_objects
Title : put_objects
Usage : $autodb->put_objects
-- OR --
$autodb->put_objects(@objects)
Function: Store all persistent objects (first form) or list of persistent
objects (second form)
Returns : nothing
Args : list of persistent objects, Oids, or OidDeleteds
Notes : nop (does nothing) on Oids and OidDeleteds
The difference between 'put' and 'put_objects' is that when called with no objects, 'put' does nothing, while 'put_objects' stores all persistent objects
del
Title : del
Usage : $autodb->del(@objects)
Function: Delete one or more objects from memory and database
Returns : nothing
Args : list of persistent objects, Oids, or OidDeleteds
Notes : nop (does nothing) on OidDeleteds
Manage database schema
exists
Title : exists
Usage : my $bool=$autodb->exists
Function: Test whether AutoDB database exists
Returns : boolean
Args : none
register
Title : register
Usage : $autodb->register(class=>'Person',collection=>'Person',
keys=>qq(name string, sex string, id integer),
transients=>qq(name_prefix sex_word))
Function: Register collection with the system
Returns : nothing
Args : class whitespace- or comma-separated string of class names, or
ARRAY of class names (optional)
other args any valid form of %AUTODB
When you 'register' new collections or add search keys to existing ones, the system creates or alters database tables as needed to implement your changes. These effects are persistent, of course.
When you 'register' new associations between classes and collections, these changes are in effect for the current session only. This makes it possible to dynamically change the collections into which new objects are put.
create
Title : create
Usage : $autodb->create
Function: Create AutoDB database
Returns : nothing useful
Args : none
The system drops the existing AutoDB database, if any, and then creates the new one. Dropping the old database drops the _AutoDB table and all tables used to implement the collections that exist in the saved registry. Creating the new database creates the_AutoDB table and all tables needed to implement the collections registered in the current, in-memory registry.
The 'create' option to 'new' runs this method.
alter
Title : alter
Usage : $autodb->alter
Function: Alter AutoDB database to reflect differences between current,
in-memory registry and saved registry
Returns : nothing useful
Args : none
The only alterations that are allowed are expansions. New collections can be created and new search keys can be added to existing collections.
Users rarely need to use this method, because the system handles alterations automatically.
drop
Title : drop
Usage : $autodb->drop
Function: Drop AutoDB database
Returns : nothing useful
Args : none
The system drops the existing AutoDB database, if any. This drops the _AutoDB table and all tables used to implement the collections that exist in the saved registry.
The 'drop' option to 'new' runs this method.
Persistent objects and Oids
When you declare a class to be persistent, we arrange for it to inherit from Class::AutoDB::Object by modifying its @ISA array. This has the effect of polluting your class's namespace with inherited methods. It was a bad idea. Sorry. We hope to change this in a future release.
Two of the inherited methods are commonly used. These can be invoked on a persistent object or a Class::AutoDB::Oid object. It's usually okay to override these methods. Our code never calls them, so overriding them won't break any Class:AutoDB mechanisms.
Caveat: Class::AutoDB::Oid also defines these methods. If you override these methods, your version will not be invoked on a Class::AutoDB::Oid object. If this is a problem, you should make sure your objects are thawed before invoking the methods.
put
Title : put
Usage : $object->put
Function: Store object in database
Returns : nothing
Args : none
Notes : Deprecated. Should use $autodb->put($object)
Legal to invoke on Oid or OidDeleted but does nothing
oid
Title : oid
Usage : my $oid=$object->oid
Function: Access object's oid (immutable object identifier)
Returns : oid as number, NOT Class::AutoDB::Oid object
Args : none
Notes : Deprecated. Should use $autodb->oid($object)
Also works on Oid or OidDeleted
SEE ALSO
We rely on DBI and its MySQL driver, DBD::mysql, for access to the underlying database. See DBI for information on database handles (DBHs) and DBD::mysql, for the format of data source name (DSN) strings.
This class works closely with Class::AutoClass whose 'declare' function sets up persistent classes using the value of %AUTODB.
Many CPAN modules provide capabilities similar to those provided here. These include Alzabo, Class::DBI, DBIx::Class, DBIx::RecordSet, DBM::Deep, Fey::ORM, KiokuDB, OOPS, ORM, Pixie, SPOPS, and Tangram.
AUTHOR
Nat Goodman, <natg at shore.net>
BUGS AND CAVEATS
Please report any bugs or feature requests to bug-class-autodb at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Class-AutoDB. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
Known Bugs and Caveats
- 1. This module is old
-
We use it, and it works well for our purposes, but it's showing its age.
- 2. The current version only works with MySQL
-
The first step of the installation process, 'perl Build.PL', makes sure that MySQL is available and that the person doing the installation has sufficient privileges to run the test suite. If this check fails, Build.PL does not generate the Build script and exits with a 0 return code. This is the idiom recommended for automated CPAN testing but may be too severe for normal installs.
- 3. One AutoDB at a time
-
You can only have one active AutoDB object at a time. Grievous errors will occur if you run 'new' a second time on a different database.
- 4. No transactions
-
This is also on our to-do list, but further down, as we have no urgent need for transactions in our application.
- 5. Type mismatches
-
If data of the wrong type is stored in a search key, data conversion occurs as described in "Type mismatches".
- 6. Class attributes not stored or retrieved
-
Class attributes (as that term is used in Class::AutoClass) are not stored in the AutoDB database and are not set when objects are fetched or classes 'used'. This is related to the simple way class attributes are handled in Class::AutoClass.
- 7. Uneven error checking
-
The software does not consistently check for user errors. If you make a mistake and pass in bad data. the software sometimes responds with an intelligible error message but often does not. All too often, the mistake winds its way into the guts of the software which ultimately dies with an inscrutable message.
- 8. Pollution of user class namespace
-
The current design requires that persistent classes be subclasses of Class::AutoDB::Object; we ensure this by pushing the required class onto @ISA if it's not already there. This has the unfortunate side effect of polluting the persistent classes' namespace with every method in Class::AutoDB::Object and above. This is only a problem when user classes employ multiple inheritance.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Class::AutoClass
You can also look for information at:
RT: CPAN's request tracker
AnnoCPAN: Annotated CPAN documentation
CPAN Ratings
Search CPAN
ACKNOWLEDGEMENTS
Chris Cavnor maintained the CPAN version of the module for several years after its initial release.
COPYRIGHT & LICENSE
Copyright 2003, 2009 Nat Goodman, Institute for Systems Biology (ISB). All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.