NAME

Basset::Object::Persistent - subclass of Basset::Object that allows objects to be easily stored into a relational database. Presently only supports MySQL, but that may change in the future.

AUTHOR

Jim Thomason, jim@jimandkoka.com

SYNOPSIS

(no synopsis, this is an abstract super class that should never be instantiated directly, it should be subclassed for all persistent objects and used through them)

DESCRIPTION

Basset::Object is the uber module in my Perl world. All objects should decend from Basset::Object. It handles defining attributes, error handling, construction, destruction, and generic initialization. It also talks to Basset::Object::Conf to allow conf file use.

But, some objects cannot simply be recreated constantly every time a script runs. Sometimes you need to store the data in an object between uses so that you can recreate an object in the same form the last time you left it. Storing user information, for instance.

Basset::Object::Persistent allows you to do that transparently and easily. Persistent objects need to define several pieces of additional information to allow them to commit to the database, including their table definitions. Once these items are defined, you'll have access to the load and commit methods to allow you to load and store the objects in a database.

It is assumed that an object is stored in the database in a primary table. The primary table contains a set of columns named the same as object attributes. The attributes are stored in those columns.

Some::Package->add_attr('foo');
my $obj = Some::Package->new();
$obj->foo('bar');
$obj->commit();

in the database, the 'foo' column will be set to 'bar'.

ATTRIBUTES

loaded

boolean flag 1/0.

This flag tells you whether or not the objects you are operating on has been loaded from a database or initially created at this time and not loaded. This flag is set internally, and you should only read it.

loading

read only boolean flag 1/0.

This flag is usually used internally, it keeps track of whether or not the object is currently in the process of loading from the database. It will always be zero unless the object is loading. This flag is set internally, and you should only read it.

committing

read only boolean flag 1/0.

This flag is usually used internally, it keeps track of whether or not the object is currently in the process of committing to the database. It will always be zero unless the object is committing. This flag is set internally, and you should only read it.

committed

Flag, N/0.

This flag tells you whether this object has been committed during this instantiation. It will not keep track of whether an object has been committed before this instantiation. The value is either 0 (no commits during this instantiation) or N, where N is a positive integer number containing the number of times that this object has been committed during this instantiation. This flag is set internally, and you should only read it.

$object->commit();
if ($object->committed){
 print "Yay, committed!";
}
else {
 print "Could not commit : " . $object->errstring . "\n";
};
deleting

read only boolean flag 1/0.

This flag is usually used internally, it keeps track of whether or not the object is currently in the process of being deleted from the database. It will always be zero unless the object is deleting. This flag is set internally, and you should only read it.

deleted

Boolean flag, 1/0.

When an object is deleted via the ->delete method, this flag is set to 1. Otherwise, it is 0. This is the only change that is made to an object when it is deleted, so this is the way to determine if your delete was successful. This flag is set internally, and you should only read it.

$object->delete();
if ($object->deleted){
 print "Yay, deleted!";
}
else {
 print "Could not delete : " . $object->errstring . "\n";
};
arbitrary_selectables

This should be set in the conf file. This is a regular expression that specifies which queries arbitary_sql should expect to return data. A good value for MySQL is: (show|select|desc|set)

force_insert

Boolean flag. 1/0. Trickles to subclasses.

Your objects may be transactional in nature such that you always want to keep a record of them no matter how often they've changed. In that case, you can specify the force_insert flag.

Care must be taken with this flag to ensure you never violate primary key constraints. Also, you may not use auto generated ids, for obvious reasons.

METHODS

    _keyed_accessor

    This is an accessor designed to be specified with add_attr. For example,

    Basset::User->add_attr(['user_group', '_keyed_accessor'], 'Basset::Group');

    That would specify that if you have a user object, you can only specify values to your user_group attribute that would successfully load into a Basset::Group object.

    You can shut off the key validation if you're positive your value is valid

    $user->user_group($group_id);				#validates
    $user->user_group($group_id, 'valid');		#does not validate

    Also note that the validation does not occur when the object is loading. It is assumed that if the key made it into the database, it's valid.

    add_primarytable

    add_primarytable is a class method that takes a hash as an argument, which is used as a constructor call for a Basset::DB::Table object (or whatever you've specified as your table type object)

    __PACKAGE__->add_primarytable(
    	'name'				=> 'transaction',
    	'primary_column'	=> 'id',
    	'autogenerated'		=> 1,
    	'definition'		=> {
    		'id'				=> 'SQL_INTEGER',
    		'account'			=> 'SQL_INTEGER',
    		'paidby'			=> 'SQL_INTEGER',
    		'category'			=> 'SQL_INTEGER',
    		'day'				=> 'SQL_DATE',
    		'amount'			=> 'SQL_DECIMAL',
    		'description'		=> 'SQL_VARCHAR',
    	}
    );

    See Basset::DB::Table for more information. This table is the primary table where the object's data is stored.

    This method is a wrapper around add_tables with a single table ->factory call on the 'table' type, but it also explicitly wipes out the tables list before setting the primary table.

    add_tables

    add_tables is a class method that takes a list of tables as its arguments, which are the tables associated with this object when it is stored to the database.

     __PACKAGE__->add_primarytable(
     	__PACKAGE__->factory(
     		'type'			=> 'table',
    		'name'				=> 'transaction',
    		'primary_column'	=> 'id',
    		'autogenerated'		=> 1,
    		'definition'		=> {
    			'id'				=> 'SQL_INTEGER',
    			'account'			=> 'SQL_INTEGER',
    			'paidby'			=> 'SQL_INTEGER',
    			'category'			=> 'SQL_INTEGER',
    			'day'				=> 'SQL_DATE',
    			'amount'			=> 'SQL_DECIMAL',
    			'description'		=> 'SQL_VARCHAR',
    		}
    	)
     );

    See Basset::DB::Table for more information.

    primary_table

    Returns the first table associated with the given object.

    relationships

    This is a class attribute that internally stores the relationships used by this class. Specify new relationships with has_a or has_many.

    should_be_deleted

    This is used to flag an object that has been auto-vivified and is tied to a parent object. You should rarely need to set, access, or worry about this flag directly.

    should_be_committed

    This is used to flag an object that has been auto-vivified and is tied to a parent object. You should rarely need to set, access, or worry about this flag directly.

    instantiated_relationships

    Internal hash that keeps track of which relationships for a given object have been instantiated. Check for instantiation via the is_instantiated method instead.

    cental_load_cache

    if the use_central_load_cache parameter is set in the conf file, then objects will use a centralized loading cache, stored here. This is internal only.

    _deleted_relationships

    Internal method. Keeps track of instantiated associated objects that were subsequently deleted. No looky, no touchy.

    is_instantiated

    Boolean operator. Given an attribute, returns true if it is an associated attribute and has been instantiated, false if it has not been.

    instantiate

    In the abstract, this is simple. Takes an attribute and an optional set of clauses, then instantiates that object.

    $obj->instantiate('foo');

    Now $obj->foo will contain whatever the instantiated list of information is, as defined when it was set up with the has_a or has_many call. Alternatively, you can pass in a set of clauses to restrict the objects loaded.

    $obj->instantiate('foo', {
    	'where' => 'status_id = 1'
    });

    Will instantiate the 'foo' attribute only with the objects that have a status_id of 1, anything else will simply not be loaded. A useful clauses flag to pass is "temporary" - this will instantiate the relationship according to the clauses, but not populate the attribute.

    Note that you should only instantiate an attribute that is defined has having an instantiating parameter of 'manual' (as opposed to 'lazy' ) and this is due to encapsulation reasons.

    Lazy objects are not instantiated until the attribute holding them is accessed, but then they are instantiated automatically.

    Manual objects are the ones that you want to worry about. In those cases, the instantiate method is basically a shortcut to insulate you from needing to take extra steps and know the class involved.

    Say that a user has_many classes. You could do this:

    use Some::Class;
    use Some::User;
    
    my $user = Some::User->load(1);
    my $classes = Some::Class->load_where('user_id' => $user->id);

    or this

    use Some::User;
    
    my $user = Some::User->load(1);
    my $classes = $user->instantiate('classes');
    uninstantiate
    has_a

    has_a defines relationship between objects. "An object 'has_a' different object". The has_a method is simply a wrapper around has_many, passing in a key of undef and setting the singleton flag to 1.

    _instantiating_accessor

    If a relationship is defined in has_many with instantiating -> lazy, then the associated objects will be populated automagically, but not until the attribute is accessed. _instantiating_accessor internally handles all of that.

    has_many

    All right, now we finally get to define relationships. The has_many parameter needs two values, the attribute and its class.

    Some::Class->has_many(
     'wibbles' => 'Some::Other::Class'
    );

    That will create an accessor for 'wibbles' and associate it with "Some::Other::Class". You could then instantiate it from an object:

    $someObject->instantiate('wibbles');

    And populate all of your wibbles data.

    has_many takes an optional (but recommended!) 3rd argument, the options hash. Several options are supported.

    key

    If loading up multiple associated objects (a cat "has_many" paws), then they will by default appear in an arbitrarily ordered arrayref containing all of the data. But, there are times when you want to load up all of the data and quickly associate objects associated with particular attributes. In that case, pass in the key parameter.

    Some::Class->has_many(
     'wibbles' => 'Some::Other::Class',
     {
     	'key' => 'foo'
     }
    );

    Then your data will be populated into a hashref, with the associated objects' "foo" attributes serving as their keys.

    instantiating

    This item should be one of 2 values - 'manual' or 'lazy'

    Some::Class->has_many(
     'wibbles' => 'Some::Other::Class',
     {
     	'instantiating' => 'manual'
     }
    );
    lazy

    lazily instantiated objects will automatically come into being when the associated attribute of the owning object is accessed for the first time. This is the default.

    manual

    manually instantiated objects will never automatically come into being. you will have to explicitly call 'instantiate' yourself.

    singleton

    If the singleton flag is set, then it is known that this attribute is associated with a single other object, and consequently will just hold a reference to that object itself (not in an arrayref or hashref)

    Some::Class->has_many(
     'wibbles' => 'Some::Other::Class',
     {
     	'singleton' => 1
     }
    );
    clauses

    the clauses hashref is the same sort of clauses hashref to be handed into the loader. in fact, it is handed into the loader when the associated objects are instantiated.

    Some::Class->has_many(
     'wibbles' => 'Some::Other::Class',
     {
     	'clauses' => {
     		'where' => 'status_id = 1'
     	}
     }
    );
    accessibility

    This governs encapsulation. Associating objects with other objects is good, but you don't always want the user of the class to know that other objects are involved. You should set the accessibility flag to 'private' if the associated object will never be accessed outside of the class that defines it. These classes should probably be inlined (or at least privatedly declared inside another package)

    Some::Class->has_many(
     'wibbles' => 'Some::Other::Class',
     {
     	'accessibility' => 'private'
     }
    );

    Default value is 'public';

    Making an associated object private shuts off its ability to commit or delete itself. its changes only go in when its parent object is committed or deleted.

    relationship_key

    Sometimes, you may have an object that references two objects in a different table. You may know that every Car has a primary_driver and a secondary_driver. So you define your relationship:

    Car->primary_table->references(
    	{
    		'primary_driver' => 'driver.id',
    		'secondary_driver' => 'driver.id'
    	}
    );

    But you wouldn't be able to establith relationships for those items, since instantiate would try to load an object using both of those values.

    Car->has_a(
    	'primary_driver' => 'Driver'
    );

    Would try to load where driver.id = car.primary_driver_id and driver.id = car.secondary_driver_id. So it would only work in the edge case when they're the same driver, which is not your intent.

    The solution is to explicitly define which key you'd like to join on.

    Car->has_a(
    	'primary_driver' => 'Driver',
    	{
    		'relationship_key' => 'primary_driver'
    	}
    );
    
     Car->has_a(
    	'secondary_driver' => 'Driver',
    	{
    		'relationship_key' => 'secondary_driver'
    	}
    );
    transform

    See the 'transform' flag in the load_all method for info.

    foreign_has_a

    If you have a has_many relationship, then presumably your foreign class has a has_a relationship with you. You can declare that relationship here. This has two advantages.

    1) It allows you to autmatically populate the foreign object's has_a property with yourself upon setting the has_many.

    2) If the foreign class references you with multiple columns (say, obj_id_1 and obj_id_2), then the foreign has_a has defined the relationship key to use. Specifying the foreign_has_a here uses those same relationship keys.

    create_isa_to_method

    Mainly used internally when setting up has_many relationships. When you create a has_many relationship, you automatically get an add_to* method.

    Some::Store->has_many(
    	'bagels' => 'Some::Bagel::Class'
    );
    
    my $store->add_to_bagels(
    	'type' => 'chocolate chip',
    	'id' => '17738'
    );

    Is equivalent to:

    my $bagel = Some::Bagel::Class->new(
    	'type' => 'chocolate chip',
    	'id' => '17738',
    	'store_id' => $store->id,
    );
    commit_relationships

    Used internally to commit all associated objects for a given object, only used for private objects

    $obj->commit_relationships
    delete_relationships

    Used internally to delete all associated objects for a given object.

    $obj->delete_relationships

    only used for private objects

    is_relationship

    Given an attribute, returns true if it is a relationship, false if not.

    if ($obj->is_relationship("some_attribute")) {
    	#do interesting thing
    }
    relationship_columns

    Takes a relationship as an argument, returns a list of two arrayrefs - the referencing columns (yours) and the foreign columns (columns in the foreign table)

    my ($referencing, $foreign) = $self->relationship_columns($relationship);

    I can't think of a reason you'd ever want to call this directly.

    primary_identifier

    Returns the single, unique primary identifier of the object.

    my $id = $obj->primary_identifier;

    If an object has composite keys, this method will return an error by default. You can pass the 'composite' flag to get back an arrayref of all primary keys.

    my $idref = $obj->primary_identifier('composite');

    If you simply want a string identifier to identify the object, pass in the "string" flag.

    my $string = $obj->primary_identifier('string');
    copy

    copy is overridden in Basset::Object::Persistent. When you copy a persistent object, it automatically wipes out the object's primary keys, and breaks all flags listing it as being in the database, so you get a fresh insert. Explicitly call Basset::Object's copy to key primary key values.

    my $o2 = $o->copy;					#loses primary keys
    my $o2 = $o->Basset::Object::Copy;	#keeps primary keys
    commit

    There is a lot of internal magic here which I'll decline to get into at the moment. Suffice to say, that ->commit() will store your object in the database, and that all of the Right Things will happen during the commit.

    $object->commit();
    if ($object->committed){
     print "Success!\n";
    } else {
     print "Failure : " . $object->errstring . "\n";
    };
    writable_method

    Given a method name, returns true if the value of this method will be written out to disk on the next commit, and false if it will not be written out.

    my $output = $object->writable_method('id');
    if ($output) {
    	print "object will store id\n";
    } else {
    	print "object will not store id\n";
    }
    load

    the load method loads an object from the database. The arguments passed must be the primary_column specified in your primary table, in that order.

    __PACKAGE__->add_primarytable(
    	.
    	.
    	.
    	'primary_column' => 'id'
    );
    
    my $obj = Some::Package->load($id);
    
    __PACKAGE__->add_primarytable(
    	.
    	.
    	.
    	'primary_column' => [qw(foo bar baz)]
    );
    
    my $obj = Some::Package->load($foo, $bar, $baz);

    The arguments passed must be in the same order they were defined.

    Returns an error if no object found that matches

    load_or_new

    Does what it sounds like, it tries to load an object, and if it fails, it creates a new empty object instead. Basically, this allows some lazy object creation for things like stateless applications (such as cgis) that don't know in advance what they're operating on, and don't really care. So you can try to load an object if values were passed back to you, and if they weren't then you create an automatically create a new one for yourself.

    load_many

    Convenience method. If you have a class that only uses one primary column (a unique ID, for instance) and you want to load certain objects with given IDs, you can use load_many.

    my $objects = $self->load_many(1,2,3,4,5);
    load_next
    create

    Convenience method. Instantiates a brand new object and then immediately commits it to the database.

    load_all

    load_all loads all objects of a given package and returns them in an arrayref.

    my $objects = Some::Package->load_all();

    load_all optionally takes an arbitrary number of arguments, where the first is a hashref that defines a set of constraints and the rest are column values to bind to those constraints.

    my $objects = Some::Package->load_all(
    	{
    		'where'		=> 'name = ? and company = ?',
    		'order by'	=> 'id'
    	},
    	'Jim', 'FooFram'
    );

    Will return an arrayref containing all objects with a name of "Jim" and a company of "FooFram"

    A list of all valid constraints is provided in the Basset::DB::Table object.

    Note that load_all is faster than loading objects individually, since it combines its SQL to minimize the number of queries. However, all queries dones internally to auto-instantiated relationships will still be performed one at a time, and not in aggregate.

    NOTE - with load_all, you are required to pass in actually column names, not aliases attribute names. You would pass in aliased attribute names to load_where.

    Returns an empty arrayref if no objects found.

    The loader can also accept various 'flag' attributes passed in the constraints hash. The flags will not be passed onto the SQL generator.

    iterator

    The iterator flag allows you to load up objects in sequence using load_next.

    my $objs = Some::Class->load_all();
    foreach my $o (@$objs) {
    	$o->do_something;
    };

    is equivalent to:

    Some::Class->load_all({'iterator' => 1});
    while (my $o = Some::Class->load_nex) {
    	$o->do_something;
    };

    The advantage is that you won't have all of the objects in memory at one time. Note that if you subsequently call a load* method in the same class that you will wipe out the current iterator.

    constructor

    A hashref of constructor args. As data is loaded from the database, objects will be created and initialized with the data loaded. But sometimes you need to load objects and populate in new values or override existing values with new ones. That's where the constructor comes in. It will override the values of those attributes in the database with new ones.

    my $objs = Some::Class->load_all(
    	{
    		'constructor' => {
    			'foo' => 'bar'
    		}
    	}
    );

    Now all objects in $objs will have their foo attribute set to 'bar'

    singleton

    Sometimes, you build up a complicated query but know that you'll only get back one object. If you pass in the 'singleton' flag, then you'll only get back a single object instead of an arrayref containing a single object.

    transform

    Will transform the loaded object into one of its related objects declared via a has_a or has_many relationship.

    Some::User->has_a('pelican' => 'Some::Pelican');
    
    my $pelican = Some::User->load_all({'where' => 'user_id = ?', 'transform' => 'pelican'});

    Directly using this as a loader flag is dubious at best, it is most useful with relationships.

    force_arrayref

    There are several flags that will return the resutls of load_all in a different format (key or singleton, for example), but this makes subclassing difficult. You can't easily override the load_all method, since you don't know what SUPER's implementation will return to you. So you can pass the force_arrayref flag. That will return a list with the actual original arrayref first, and the value to return to the user second. Along these lines:

    package Some::Subclass;
    
    sub load_all {
    	#not quite right...this wipes out the existing clauses hashref.
    	my ($values, $return) = shift->SUPER::load_all({'force_arrayref' => 1}, @_);
    	
    	foreach my $value (@$values) {
    		#do interesting thing;
    	}
    	
    	return $return;
    }
    exists

    Query to quickly determine if a given object (or set of objects) exists in the database. The objects will not be loaded. Returns a count of the number of objects that exist.

    my $itsthere = Basset::User->exists(1); #user id 1 exists in the database
    delete

    This will delete an object from the database

    $object->delete();

    The object itself will not be affected, except for the fact that its deleted flag will be set.

    load_where

    Simple wrapper around load_all. Takes key/value pairs.

    my $users = Some::Class->load_where(
    	'user' 		=> 3,
    	'location'	=> 'mountains',
    	'weather'	=> 'sunny'
    );

    This is exactly equivalent to:

    my $users = Some::Class->load_all(
    	{
    		'where' => 'user = ? and location = ? and weather = ?'
    	},
    	3, 'mountains', 'sunny'
    );

    It just looks prettier and hides more of the SQL.

    Even better, you can also stick in an array for multiple value loads.

    my $users = Some::Class->load_where(
    	'state'		=> 'PA',
    	'last_name' => [qw(Smith Jones Johnson)]
    );

    Is exactly the same as:

    my $users = Some::Class->load_all(
    	{
    		'where' => 'last_name in (?,?,?) and state = ?'
    	},
    	qw(Smith Jones Johnson), 'PA',
    );

    There is an alternative syntax, you may pass in one arrayref and one hashref. The arrayref becomes your where clause, the second contains additional loader args (such as 'order by', 'limit', etc.)

    my $users = Some::Class->load_where(
    	#where array
    	[
    		'state'		=> 'PA',
    		'last_name' => [qw(Smith Jones Johnson)]
    	],
    	#extra loader hash
    	{
    		'order by' => 'state desc',
    	},
    );

    Is exactly the same as:

    my $users = Some::Class->load_all(
    	{
    		'where' => 'last_name in (?,?,?) and state = ?',
    		'order by' => state desc',
    	},
    	qw(Smith Jones Johnson), 'PA',
    );
    load_one_where

    convenience method. Simply wrappers a load_where call while passing the singleton parameter

    arbitrary_sql

    The arbitrary_sql method does what it sounds like, it executes arbitrary sql code. You're expected to pass at least one parameter:

    query => 'some sql query'; #such as select col1, col2 from table1

    If you want to bind any variables to the query, put them in the vars parameter:

    query => 'select count(*) from table where id = ?',
    vars => '7'

    Normally, you'd pass in an arrayref to vars, but if it's just one, you can skip it

    vars => '7'
    or
    vars => ['7']
    
    query => 'select count(*) from table where id = ? and type = ?',
    vars => ['7', 'animal']

    Binding is done without SQL types, unless you pass in a Basset::DB::Table object and the columns as well, which contains the column types:

    my $t = Basset::DB::Table->new( {table definitions} );
    table => $t
    cols => ['id', 'type']

    Insertion queries (insert, update, etc.) will return 1 upon success

    If you're running a select, show, set, or desc query, then you end up loading data. It will always be returned in an arrayref containing the rows. Normally, each row is a hashref, loaded with the ->fetchrow_hashref method from DBI. You can also choose to load into an array, then pass in into:

    'into' => 'array'

    If you pass anything other than 'into' => 'array', then 'into' => 'hash' is assumed.

    my $data = $class->arbitrary_sql(
    	'query' => 'select id, name from names where id in (?, ?) and name in (?, ?)',
    	'vars'	=> [qw(7 8 Jim Koka)],
    );
    
    foreach my $h (@$data){
    	print {$_->{id} . " : " . $_->name . "\n"} sort keys %$h;
    };

    Alternatively, if you're memory conscious, you can pass in the 'iterator' flag. This will return the actual executed statement handle, so you can call fetchrow_array, fetchrow_hashref, etc. on it yourself.

    my $sth = $class->arbitrary_sql(
    	'query'		=> 'select id, name from names where id in (?, ?) and name in (?, ?)',
    	'vars'		=> [qw(7 8 Jim Koka)],
    	'iterator'	=> 1,
    );

    Another example:

    my $rc = $class->arbitrary_sql(
    	'query'	=> 'insert into names (id, name) values (?,?)',
    	'vars'	=> ['18', 'Jim 3'],
    	'table	=> $names_table,
    	'cols'	=> [qw(id name)]
    );
    
    # $rc == 1
    driver

    The driver method is just a shortcut wrapper for Basset::DB->new(); Only give it the same arguments in the same format as you would give to Basset::DB->new() itself. The driver object returned will be cached here for all time, unless you explicitly wipe it out or set it to something else.

    If the driver hasn't been accessed in the last 5 minutes, then it pings the database handle before returning the driver to ensure that it's still live. If the ping fails and the driver has no transaction stack, then you transparently just get back a new driver.

    But if the ping fails AND the driver had an active transaction stack, then you get back an error. Calling ->driver again will create a new handle, but you would presumably have an error condition to deal with.

    local_driver

    Normally, you're always talking to one database with all of your objects in all of your classes. And in a perfect world, that would always be the case. However, you may need to speak to more than one database at a time, and that's where local_driver comes in. Much like ->error, this is a method that may be called on either an object or a class to specify a localized driver for that class or object.

    To make all Sub::Class objects talk to a different database:

    Sub::Class->local_driver(
    	Sub::Class->factory(
    		'type' => 'driver',
    		'dsn' => 'dbi:Pg:dbname=otherdatabase'
    	)
    );

    To make just one talk to a different database:

    my $obj = Sub::Class->new(
    	'local_driver' => Sub::Class->factory(
    		'type' => 'driver',
    		'dsn' => 'dbi:Pg:dbname=otherdatabase'
    	)
    );

    Please note that you are expected to maintain a local driver yourself - it will not be pinged, cleaned up, removed, or anything. You, the programmer, are inserting in a special case and are expected to pick up after yourself.

    begin

    Database transactions are stack based. ->begin adds onto the stack, ->end removes from the stack. See Basset::DB for more info.

    You may now begin and end your transaction as normal. Please be aware of the fact that in the current implementation, beginning a transaction locks the database driver for ALL objects in the system.

    You don't need to begin if you're only committing a single object - individual classes are expected to do their own locking, stack handling, unlocking, etc. as necessary. You will need to begin and end if you're doing multiple commits of different objects (or if you're writing your own module). For example,

    my $user  = Basset::User->load(1);
    my $user2 = Basset::User->load(2);
    
    $user->begin(); 		#start up a transaction stack
    
    $user->name('Jim');		#set user's name, doesn't need to be in the transaction
    $user2->name('Koka');	#set user's name, doesn't need to be in the transaction
    
    $user->commit(); #doesn't actually commit to the database, it's in a transaction
    $user2->commit(); #doesn't actually commit to the database, it's in a transaction
    
    $user->end();			#closes the transaction stack, now commits

    See Basset::DB for more information about begin, end, fail, etc.

    end

    Database transactions are stack based. ->begin adds onto the stack, ->end removes from the stack. See Basset::DB for more info.

    fail

    Database transactions are stack based. ->fail is a shortcut to shutdown and rollback your transaction

    finish

    Database transactions are stack based. ->finish is a shortcut to immediately finish your transaction

    wipe

    Database transactions are stack based. ->wipe clears out your transaction stack.

    fatalerror

    Setting a fatalerror message causes your transaction to fail. Note that you must explicitly pass a defined value for the transaction stack to be wiped.

    If you need to unfail a failed transaction (say, you know how to recover from the error), then you should call unfail on the driver and continue.

    $driver->unfail();
    # interesting things
    setup

    The setup method is called immediately after the object is loaded and initialized in load_all. Basset::Object::Persistent's method is empty and does nothing. It's designed to be used in subclasses in locations where you need to alter something in an object after it's loaded from the database and set up properly. Say if you do further initialization or load in from an object or something.

    cleanup

    The cleanup method is called immediately before the object is committed in commit. Basset::Object::Persistent's method is empty and does nothing. It's designed to be used in subclasses in locations where you need to alter something in an object immediately before it's committed to the database.

1 POD Error

The following errors were encountered while parsing the POD:

Around line 381:

=over without closing =back