The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Persistence::Manual::Introduction - Introduction to Persistence::Entity.

INTRODUCTION

We'll take a thorough look at the process of developing entities, perl classes and mapping between them to a relational database.

ENTITY

Entity represents logical unit of data in the database. It doesn't need to be just a simple mapping to a table, but it may represent comlex SQL epressions, with subqueries, joins, etc, this is especailly important to limit database calls for all those information to one call, rather then using separtes cursors with many fetch executions.

Examples of entities:

    my $dept_entity = Persistence::Entity->new(
        name    => 'dept',
        alias   => 'dt',
        primary_key => ['deptno'],
        columns => [
            sql_column(name => 'deptno'),
            sql_column(name => 'dname'),
        ]
    );

    my $emp_entity = Persistence::Entity->new(
        name    => 'emp',
        alias   => 'ur',
        primary_key => ['empno'],
        columns => [
            sql_column(name => 'empno'),
            sql_column(expression => 'SYSDATE - dob', alias => 'age'),
            sql_column(name => 'ename', unique => 1),
            sql_column(name => 'job'),
            sql_column(name => 'deptno'),
        ],
        subquery_columns => [
            $dept_entity->column('dname'),
        ]
    );

    $entity_manager->add_entities($dept_entity, $emp_entity);

OBJECT TO RELATIONAL DATABASE MAPPING

The purpose of an object relational mapping is to provide programmers with a simpler mechanism for accessing and changing data using plain perl classes, so when creating a new object or changing existing one it must be inserted into the database or synchronized accordingly. The process of coordinating the data represented by a object instance with the database is called persistence.

You have to delcare mapping between you class and entity, then you can use your classes like any other perl classes.

Class itself shouldn't be aware about database mapping and database operation underneath, thus you interact with the entity manager to persist, update, remove, locate, and query for objects. The entity manager is responsible for automatically managing the object' state. It takes care of managing the object in transactions and persisting its state to the database.

    $entity_manager->begin_work;
    eval {
        my $emp = Employee->new(id => 100, name => 'Scott');
        $entity_manager->insert($emp);

        #you can delcare mapping between many object to the same entity (for different inheritance strategy)
        #so that find takes entity_name, class_name as two first parameters.

        my @emp = $entity_manager->find(emp => 'Employee', deptnp => 10);
        for my $emp (@emp) {
            $emp->set_sallary($emp->salary + $emp->salary * 0.05)  ;
            $entity_manager->update($emp);
        }
        $entity_manager->commit;
    }
    $entity_manager->rollback if $@;

The Programming Model

Classes defined with Meta Object Protocol

This module suppports adapters to different MOP classes so that it may be easily extended by adding adapter class that implements/extends Persistence::Attribute interface.

Persistence::Attribute::AMCAdapter is default adapter class that supports Abstract::Meta::Class.

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

    column empno => has('$.id') ;
    column ename => has('$.name') ;
    column job => has('$.job') ;
Plain perl classes
    use Persistence::ORM;

    Persistence::ORM->new(
        mop_attribute_adapter => 'Persistence::Attribute::AMCAdapter',
        entity_name           => 'emp',
        class                 => 'Employee',
        columns               => {
            empno => {name => 'id'},
            ename => {name => 'name'},
            job   => {name => 'job'},
        }
    );

    package Employee;

    sub new {
        my $class = shift;
        bless {@_}, $class;
    }


    sub id {
        shift()->{id};
    }

    sub set_id {
        my ($self, $value) = @_;
        $self->{id} = $value;
        $self;
    }

    sub name {
        shift()->{name};
    }

    sub set_name {
        my ($self, $value) = @_;
        $self->{name} = $value;
        $self;
    }

    #or setter/getter in one approach

    sub job {
        my ($self, $value) = @_;
        $self->{job} = $value if (@_ > 1);
        $self->{job};
    }
XML Mapping File

If you do not want to interact directly with ORM or Entity meta protocal to declare map between your class and entity, or entity and database you can alternatively use an XML mapping file to declare this metadata.

Perl class

    package Employee;
    use Abstract::Meta::Class ':all';
    has '$.id';
    has '$.name';
    has '$.salary';
    has $.job';

XML injection of the persistence metadata.

    use Persistence::Meta::XML;
    my $meta = Persistence::Meta::XML->new(persistence_dir => 'meta/');
    $meta->inject('persistence.xml');

XML definitions:

    persistence.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence name="test"  connection_name="test" >
        <entities>
            <entity_file  file="emp.xml"  />
        </entities>
        <mapping_rules>
            <orm_file file="Employee.xml" />
        </mapping_rules>
    </persistence>

    emp.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <entity name="emp" alias="e">
        <primary_key>empno</primary_key>
        <columns>
            <column name="empno" />
            <column name="ename" unique="1" />
            <column name="sal" />
            <column name="job" />
            <column name="deptno" />
        </columns>
    </entity>


    Employee.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <orm entity="emp"  class="Employee" >
        <column name="empno"  attribute="id" />
        <column name="ename"  attribute="name" />
        <column name="job"    attribute="job" />
        <column name="sal"    attribute="salary" />
    </orm>