Persistence::ORM - Object-relational mapping.


Persistence-Entity documentation Contained in the Persistence-Entity distribution.

Index


Code Index:

NAME

Top

Persistence::ORM - Object-relational mapping.

SYNOPSIS

Top

    package Employee;

    use Abstract::Meta::Class ':all';
    use Persistence::ORM ':all';

    entity 'emp';
    column empno => has('$.no') ;
    column ename => has('$.name');




DESCRIPTION

Top

Object-relational mapping module.

EXPORT

Top

entity column trigger to_one one_to_many many_to_many LAZY EAGER NONE ALL ON_INSERT ON_UPDATE ON_DELETE by 'all' tag

ATTRIBUTES

class

class name

entity_name

entity name.

columns

A map between database column and object attribute

lobs

Assocation to LOB objects definition.

relationships

Assocation to objects relationship definition.

trigger

Defines tigger that will execute on one of the following event before_insert after_insert before_update after_update before_delete after_delete, on_fetch Takes event name as first parameter, and callback as secound parameter.

    $entity_manager->trigger(before_insert => sub {
        my ($self) = @_;
        #do stuff
    });

entity_manager
mop_attribute_adapter

Name of the class that is an adapter to meta object protocols. That class have to implements Persistence::Attribute interface.

object_creation_method

Returns object creation method. Allowed values: bless or new

_attributes_to_columns

Cache for the attributes_to_columns method result

_columns_to_attributes

Cache for the columns_to_attributes method result

_columns_to_storage_attributes

Cache for the columns_to_storage_attributes method result

METHODS

entity

Creates a meta entity class.

mapping_meta

Returns meta enity class. Takes optionally package name as parameter.

column

Adds mapping between column name and related attribute. Takes column name and attribute object as parameter.

    column ('column1' => has '$.attr1');

lob

Adds mapping between lob column name and related attribute.

    lob 'lob_column' => (
        attribute    => has('$.photo'),
        fetch_method => LAZY,
    );




covert_to_attributes

Converts passed in data structure to attributes

covert_to_lob_attributes

Converts passed in data structure to lob attributes

_create_meta_attribute

Creates a meta attribute

add_lob_column

Adds lob column. Takes lob column name, attribute name;

eager_fetch_lobs
lazy_fetch_lobs
attribute
deserialise

Deserialises resultset to object.

deserialise_eager_relation_attributes
deserialise_eager_lob_attributes
deserialise_lazy_relation_attributes
update_object
join_columns_values

Returns join columns values for passed in relation

unique_values

Return unique columns values

primary_key_values

Return primary key values

trigger
validate_trigger

Validates triggers types

run_event
attributes_to_columns
columns_to_attributes
columns_to_storage_attributes
attribute_to_column

Returns column name. Takes attribute name.

storage_attribute_values

Transforms column values to the hash that can be blessed as an object. Takes hash ref of column_values

attribute_values

Transforms column values to the object attribute value hash. Takes hash ref of column_values

column_values

Transforms objects attributes to column values Takes object, optionally required columns. (by default all colunms)

attribute_values_to_column_values

Returns column values. Takes attribute values hash.

SEE ALSO

Top

Abstract::Meta::Class Persistence::Entity::Manager SQL::Entity

COPYRIGHT AND LICENSE

Top

AUTHOR

Top

Adrian Witas, adrian@webapp.strefa.pl


Persistence-Entity documentation Contained in the Persistence-Entity distribution.
package Persistence::ORM;

use strict;
use warnings;

use Abstract::Meta::Class ':all';

use Persistence::Attribute::AMCAdapter;
use Persistence::Relationship ':all';
use Persistence::LOB;
use Persistence::Relationship::ToOne ':all';
use Persistence::Relationship::OneToMany ':all';
use Persistence::Relationship::ManyToMany ':all';


use vars qw(@EXPORT_OK %EXPORT_TAGS $VERSION);
use Carp 'confess';
use base 'Exporter';

$VERSION = 0.04;

@EXPORT_OK = qw(entity column trigger to_one one_to_many many_to_many lob LAZY EAGER NONE ALL ON_INSERT ON_UPDATE ON_DELETE);
%EXPORT_TAGS = (all => \@EXPORT_OK);

has '$.class' => (
    required  => 1,
    on_change => sub {
        my ($self, $attribute, $scope, $value_ref) = @_;
        mapping_meta($$value_ref, $self);
    }
);


has '$.entity_name' => (required => 1);


has '%.columns' => (
    item_accessor    => '_column',
    associated_class => 'Persistence::Attribute',
    index_by         => 'column_name',
    on_validate      => sub {
        my ($self, $attribute, $scope, $value_ref) = @_;
        my $values = $$value_ref;
        if (ref($values) eq 'HASH') {
            my $class = $self->class;
            foreach my $k (keys %$values) {
                my $value =  $values->{$k};
                $values->{$k} = $self->_create_meta_attribute($value, $class, $k)
                    if(ref($value) eq 'HASH')
            }
        }
    }
);


has '%.lobs' => (item_accessor => '_lob', associated_class => 'Persistence::LOB', the_other_end => 'orm');


has '%.relationships' => (item_accessor => '_relationship', associated_class => 'Persistence::Relationship', index_by => 'attribute_name', the_other_end => 'orm');


{

    has '%.triggers' => (
        item_accessor => '_trigger',
        on_change => sub {
            my ($self, $attribute, $scope, $value, $key) = @_;
            if($scope eq 'mutator') {
                my $hash = $$value;
                foreach my $k (keys %$hash) {
                    $self->validate_trigger($k. $hash->{$k});
                }
            } else {
                $self->validate_trigger($key, $$value);
            }
            $self;
        },
    );
}


has '$.entity_manager' => (transistent => 1);


has '$.mop_attribute_adapter' => (
    default => 'Persistence::Attribute::AMCAdapter',
);


has '$.object_creation_method' => (
    default => 'bless',
    on_change => sub {
        my ($self, $attribute, $scope, $value) = @_;
        confess "invalid value for " . __PACKAGE__ . "::object_creation_method - allowed values(bless | new)"
            if ($$value ne 'bless' && $$value ne 'new');
        $self;
    }
);


has '$._attributes_to_columns';


has '$._columns_to_attributes';


has '$._columns_to_storage_attributes';


sub entity {
    my ($name, $package) = @_;
    $package ||= caller();
    __PACKAGE__->new(entity_name => $name, class => $package);
}


{
    my %meta;

    sub mapping_meta {
        my ($package, $value) = @_;
        $package ||= caller();
        $meta{$package} = $value if defined $value;
        $meta{$package};
    }
}

sub column {
    my ($name, $attribute) = @_;
    my $attr_class = 'Persistence::Attribute';
    my $package = caller();
    my $self = mapping_meta($package) or confess "no entity defined for class $package";
    my $attribute_class = $self->mop_attribute_adapter;
    $attribute  =  $attribute_class->new(attribute => $attribute, column_name => $name)
        unless $attribute->isa('Persistence::Attribute');
    $self->add_columns($attribute);
}


sub lob {
    my ($name, %args) = @_;
    my $attribute = $args{attribute};
    my $attr_class = 'Persistence::Attribute';
    my $package = caller();
    my $self = mapping_meta($package) or confess "no entity defined for class $package";
    my $attribute_class = $self->mop_attribute_adapter;
    $args{attribute} =  $attribute_class->new(attribute => $attribute, column_name => $name)
      unless $attribute->isa('Persistence::Attribute');
    $self->add_lobs(Persistence::LOB->new(%args));
}


sub covert_to_attributes {
    my ($self, $columns) = @_;
    my $class = $self->class;
    my $attribute_class = $self->mop_attribute_adapter;
    my $result = {};
    for my $column(keys %$columns) {
        my $meta_attribute = $columns->{$column};
        my $attribute = $attribute_class->find_attribute($class, $meta_attribute->{name});
        unless ($attribute) {
            $attribute = $self->_create_meta_attribute($meta_attribute, $class, $column);
        } else {
            $attribute = $attribute_class->new(attribute => $attribute, column_name => $column);
        }
        $result->{$column} = $attribute;
    }
    $result;
}


sub covert_to_lob_attributes {
    my ($self, $lobs) = @_;
    my $class = $self->class;
    my $attribute_class = $self->mop_attribute_adapter;
    my $result = {};
    for my $lob (@$lobs) {
        my $column = $lob->{name};
        my $fetch_method = $lob->{fetch_method};
        my $attribute_name = $lob->{attribute};
        
        my $attribute = $attribute_class->find_attribute($class, $attribute_name);
        unless ($attribute) {
            $attribute = $self->_create_meta_attribute({name => $attribute_name}, $class, $column);
        } else {
            $attribute = $attribute_class->new(attribute => $attribute, column_name => $column);
        }
        $result->{$column} =  Persistence::LOB->new(
            attribute => $attribute,
            ($fetch_method ? (fetch_method => Persistence::LOB->$fetch_method) :())
        );
    }
    $result;
}


sub _create_meta_attribute {
    my ($clazz, $meta_attribute, $class, $column_name) = @_;
    my $self = mapping_meta($class) or confess "no entity defined for class $class";
    my $attribute_class = $self->mop_attribute_adapter;
    $attribute_class->create_meta_attribute($meta_attribute, $class, $column_name);
}


sub add_lob_column {
    my ($self, $column, $attribute_name, $fetch_method) = @_;
    $self->add_lobs(
        Persistence::LOB->new(
            name         => 'column',
            attribute    => $self->attribute($attribute_name),
            ($fetch_method ? (fetch_method => Persistence::LOB->$fetch_method) :()),
        )
    );
}


sub eager_fetch_lobs {
    my ($self) = @_;
    my $lobs = $self->lobs;
    Persistence::LOB->eager_fetch_filter($lobs);
}


sub lazy_fetch_lobs {
    my ($self) = @_;
    my $lobs = $self->lobs;
    Persistence::LOB->lazy_fetch_filter($lobs);
}


sub attribute {
    my ($self, $attribute_name) = @_;
    my $meta = Abstract::Meta::Class::meta_class($self->class)
        or confess "cant find meta class defintion (Abstract::Meta::Class) for " . $self->class;
    my $attribute = $meta->attribute($attribute_name)
        or confess "cant find attribute ${attribute_name} for class " . $self->class;
    $attribute;
}


sub deserialise {
    my ($self, $args, $entity_manager) = @_;
    my $object_creation_method = $self->object_creation_method;
    my $columns_to_attributes = $self->columns_to_attributes;
    my $result = $object_creation_method eq 'bless'
        ? bless ({
        $self->storage_attribute_values($args)
    }, $self->class)
        : $self->class->new(map { $args->{$_} } keys %$columns_to_attributes);

    $entity_manager->initialise_operation($self->entity_name, $result);
    $self->deserialise_eager_relation_attributes($result, $entity_manager);
    $self->deserialise_eager_lob_attributes($result, $entity_manager);
    $entity_manager->complete_operation($self->entity_name);
    $self->run_event('on_fetch', $result);
    $result;
}


sub deserialise_eager_relation_attributes {
    my ($self, $object, $entity_manager) = @_;
    my @relations = Persistence::Relationship->eager_fetch_relations(ref($object));
    foreach my $relation (@relations) {
        $relation->deserialise_attribute($object, $entity_manager, $self);
    }
}


sub deserialise_eager_lob_attributes {
    my ($self, $object, $entity_manager) = @_;
    my @lobs = $self->eager_fetch_lobs;
    foreach my $lob (@lobs) {
        $lob->deserialise_attribute($object, $entity_manager, $self);
    }
}


sub deserialise_lazy_relation_attributes {
    my ($self, $object, $entity_manager) = @_;
    my @relations = Persistence::Relationship->lazy_fetch_relations(ref($object));
    foreach my $relation (@relations) {
        my $name = $relation->attribute->name;
        $object->$name;
    }
}


sub update_object {
    my ($self, $object, $column_values, $columns_to_update) = @_;
    my $columns = $self->columns;
    $columns_to_update ||= [keys %$column_values];
    for my $column_name (@$columns_to_update) {
        my $attribute = $columns->{$column_name} or next;
        $attribute->set_value($object, $column_values->{$column_name});
    }
}


sub join_columns_values {
    my ($self, $entity, $relation_name, $object) = @_;
    my $relation = $entity->to_many_relationship($relation_name);
    my $pk_values = $self->column_values($object, $entity->primary_key);
    unless ($entity->has_primary_key_values($pk_values)) {
        my $values = $self->unique_values($object, $entity);
        $pk_values = $self->retrive_primary_key_values($values);
    }
    $entity->_join_columns_values($relation, $pk_values);
}


sub unique_values {
    my ($self, $object, $entity) = @_;
    my @unique_columns = map { $_->name }  $entity->unique_columns;;
    $self->column_values($object, $entity->primary_key, @unique_columns);
}


sub primary_key_values {
    my ($self, $object, $entity) = @_;
    $self->column_values($object, $entity->primary_key);
}


sub trigger {
    my ($event_name, $code_ref) = @_;
    my $attr_class = 'Abstract::Meta::Attribute';
    my $package = caller();
    my $mapping_meta = mapping_meta($package) or confess "no entity defined for class $package";
    $mapping_meta->_trigger($event_name, $code_ref);
}


{
    my @triggers = qw(before_insert after_insert before_update after_update before_delete after_delete on_fetch);
    sub validate_trigger {
        my ($self, $name, $value) = @_;
        confess "invalid trigger name: $name , must be one of " . join(",", @triggers)
            unless (grep {$name eq $_} @triggers);
        confess "secound parameter must be a callback"
            unless ref($value) eq 'CODE';
    }
}


sub run_event {
    my ($self, $name, @args) = @_;
    my $event = $self->_trigger($name);
    $event->($self, @args) if $event;
}


sub attributes_to_columns {
    my ($self) = @_;
    my $attributes_to_columns = $self->_attributes_to_columns;
    return $attributes_to_columns if $attributes_to_columns;
    my $columns = $self->columns;
    my $result = {};
    foreach my $k (keys %$columns) {
        $result->{$columns->{$k}->name} = $k;
    }
    $self->_attributes_to_columns($result);
    return $result;
}


sub columns_to_attributes {
    my ($self) = @_;
    my $columns_to_attributes = $self->_columns_to_attributes;
    return $columns_to_attributes if $columns_to_attributes;
    my $columns = $self->columns;
    my $result = {};
    foreach my $k (keys %$columns) {
        $result->{$k} = $columns->{$k}->name;
    }
    my $lobs = $self->lobs;
    foreach my $k (keys %$lobs) {
        my $attribute = $lobs->{$k}->attribute;
        $result->{$attribute->column_name} = $attribute->name;
    }

    $self->_columns_to_attributes($result);
    return $result;
}



sub columns_to_storage_attributes {
    my ($self) = @_;
    my $columns_to_storage_attributes = $self->_columns_to_storage_attributes;
    return $columns_to_storage_attributes if $columns_to_storage_attributes;
    my $columns = $self->columns;
    my $result = {};
    foreach my $k (keys %$columns) {
        $result->{$k} = $columns->{$k}->storage_key;
    }
    $self->_columns_to_storage_attributes($result);
    return $result;
}


sub attribute_to_column {
    my ($self, $attribute_name) = @_;
    my $attributes_to_columns = $self->attributes_to_columns;
    $attributes_to_columns->{$attribute_name};
}


sub storage_attribute_values {
    my ($self, $column_values) = @_;
    my $columns = $self->columns;
    my $columns_to_storage_attributes = $self->columns_to_storage_attributes;
    my %result = map {
        ($columns_to_storage_attributes->{$_},  $column_values->{$_})} keys %$columns;
    wantarray ? (%result) : \%result;
}


sub attribute_values {
    my ($self,  $column_values) = @_;
    my $columns = $self->columns;
    my $columns_to_attributes = $self->columns_to_attributes;
    my %result = map {
        ($columns_to_attributes->{$_}, $column_values->{$_} )} keys %$columns;
    wantarray ? (%result) : \%result;
}


sub column_values {
    my ($self, $obj, @columns) = @_;
    my $columns_to_attributes = $self->columns_to_attributes;
    my $lobs = $self->lobs;
    @columns = (keys %$columns_to_attributes)
        unless @columns;
    my %result = map {
        my $accessor = $columns_to_attributes->{$_};
        ($_, $obj->$accessor)} @columns;
    wantarray ? (%result) : \%result;
}


sub attribute_values_to_column_values {
    my ($self, %args) = @_;
    my $attributes_to_columns = $self->attributes_to_columns;
    my %result;
    for my $k(keys %args) {
        my $column = $attributes_to_columns->{$k} || $k;
        $result{$column} = $args{$k};
    }
    (%result);
}



1;

__END__

1;