NAME

Data::Model::Tutorial::JA - Data::Model::Tutorial日本語版

Data::Model とは

id:yappo さんがつくっている O/R Mapper。新興のものでチュートリアル的なものがない ので自分でかいてみることにしました。

とはいえ、Data::Model 自体がまだまだαクオリティですので、チュートリアルもてぬき です。

現在の Data::Model のドキュメントは Data::Model Track - JPerl Advent Calendar 2009 に良質なドキュメントがありますので、そちらを参考にしてください。 http://perl-users.jp/articles/advent-calendar/2009/data-model/ 近い将来、これらのドキュメントが POD に統合されます。

つかってみる

DBI を対象につかってみる

Data::Model は、Data::Model のスキーマ定義からCREATE TABLE 文を発行することができ ます。なので、まずはスキーマ定義を Perl で書きます。

# lib/Neko/DB/User.pm
package Neko::DB::User;
use strict;
use warnings;
use base 'Data::Model';
use Data::Model::Schema sugar => 'myapp';
use Neko::Columns;

install_model user => schema {
    # primary key
    key 'id';

    # カラム定義
    column 'user.id' => { auto_increment => 1 };
    utf8_column 'user.name';
};
1;

カラムの詳細な定義は、別ファイルにします。

# lib/Neko/Columns.pm
package Neko::Columns;
use strict;
use warnings;
use Data::Model::Schema sugar => 'myapp';

column_sugar 'user.id'
    => int => {
        required => 1,
        unsigned => 1,
    };
column_sugar 'user.name'
    => 'varchar' => {
        required => 1,
        size     => 255,
    };
1;

カラムの定義を別ファイルにするというところは、他の O/R Mapper とのおおきな違いで すね。これによって得られるメリットは「カラム定義の共通化」ですね。たとえば、レコー ドの作成日付を保存する created_on なんていうのは複数のテーブルで同じ定義をつかい たいものですから、共通化できると便利です。

スキーマ定義を実際につかう

これをよびだし、CREATE TABLE 文を発行するには、

# script/dump_schema.pl
use strict;
use warnings;
use Neko::DB::User;
use Data::Model::Driver::DBI;

my $dm = Neko::DB::User->new();

do {
    # ドライバ情報をつっこむ
    my $driver = Data::Model::Driver::DBI->new(
        dsn => 'dbi:SQLite:'
    );
    $dm->set_base_driver($driver);
};

for my $target ($dm->schema_names) {
    for my $sql ($dm->as_sqls($target)) {
        print "$sql\n";
    }
}

のようにします。driver を各 schema に対して発行するというあたりがポイントになる かとおもいます。わずらわしいですが、ここはぐっと我慢しましょう。

# script/crud.pl
use strict;
use warnings;
use Test::More tests => 10;
use Neko::DB::User;
use Data::Model::Driver::DBI;

my $dm = Neko::DB::User->new();

# ドライバ情報をつっこむ
{
    my $driver = Data::Model::Driver::DBI->new(
        dsn => 'dbi:SQLite:'
    );
    $dm->set_base_driver($driver);
}

# schema のセットアップ
for my $target ($dm->schema_names) {
    my $dbh = $dm->get_driver($target)->rw_handle;
    for my $sql ($dm->as_sqls($target)) {
        $dbh->do($sql);
    }
}

# INSERT 文の発行
$dm->set( 'user' => {
    name => 'yappo'
});
$dm->set('user' => {
    name => 'ukonmanaho'
});

# SELECT 文の発行
#  スカラコンテキストのときはイテレータ
{
    my $iterator = $dm->get('user' => {
        order => {'id' => 'ASC'}
    });
    my @names;
    while (my $row = $iterator->next) {
        push @names, $row->name;
    }
    is join(',', @names), 'yappo,ukonmanaho';
}

# リストコンテキストのときは配列
{
    my @users = $dm->get('user' => {order => { 'id' => 'DESC' }});
    is scalar(@users), 2;
    is $users[0]->name, 'ukonmanaho';
    is $users[1]->name, 'yappo';
}

# 条件つきで検索
{
    my @users = $dm->get('user' => {
        where => [
            name => 'yappo'
        ],
    });
    is scalar(@users), 1;
    is $users[0]->name, 'yappo';
}

# update
{
    my ($ukon, ) = $dm->get('user' => {
        where => [
            name => 'ukonmanaho'
        ],
    });
    is $ukon->name, 'ukonmanaho';
    $ukon->name('jack');
    $ukon->update;
}

# delete
{
    my $count_users = sub {
        scalar(my @users = $dm->get('user'));
    };

    is $count_users->(), 2;

    my ($jack, ) = $dm->get('user' => {
        where => [
            name => 'jack'
        ],
    });
    is $jack->name, 'jack';
    $jack->delete;

    is $count_users->(), 1;
}

に、簡単な SELECT/INSERT/UPDATE/DELETE の例をのせておきました。

Memcached をつかってみる

Data::Model ではストレージとして SQLite の他に memcached protocol によるデータの 保存にも対応しています。ここではいわゆる hash database 的なものをつかうことが想定 されています。Tokyo Tyrant などをストレージとして、Data::Model でデータをあつかえ るということです。

Memcached をつかう場合の例は下記のスクリプトにおいてあります。

# script/memcached.pl
use strict;
use warnings;
use Test::More tests => 2;
use Neko::DB::User;
use Data::Model::Driver::Memcached;
use Cache::Memcached::Fast;

my $dm = Neko::DB::User->new();

# ドライバ情報をつっこむ
{
    my $driver = Data::Model::Driver::Memcached->new(
        memcached => Cache::Memcached::Fast->new({
            servers => [
                '127.0.0.1:11211',
            ],
        }),
    );
    warn $dm->set_base_driver($driver);
}

# INSERT
warn $dm->set( 'user' => 1, {
    name => 'yappo'
});
warn $dm->set('user' => 2, {
    name => 'ukonmanaho'
});

# SELECT
{
    my ($yappo) = $dm->get('user' => 1);
    warn $yappo;
    is $yappo->name, 'yappo';
}
{
    my ($ukonmanaho) = $dm->get('user' => 2);
    warn $ukonmanaho;
    is $ukonmanaho->name, 'ukonmanaho';
}

キャッシュを使ってみる

DBI や Memcached のストレージへのアクセスするさいに Memcached などのキャッシュを 挟む事が出来ます。の、予定。

Q4M

カラム定義の詳細

クエリメソッドのオプションや絞り込み方法など

get

lookup

lookup_multi

set

replace

update

delete

Mixin について

trigger について

このドキュメントの作者

tokuhirom (original http://github.com/tokuhirom/data-model-tutorial/tree/master)

yappo (加筆修正)