<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xml:lang="ja">
  <channel>
    <title>DBIx::Skinny - JPerl Advent Calendar 2009</title>
    <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/</link>
    <description>DBIx::Skinnyの解説や、ちょっとした tips などを紹介していきます。</description>
    <item>
      <title>総括 #25</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/25.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちわ！こんにちわ！nekokakです！</p>
<p>アドベントカレーンダー達成しました！</p>
<p>ありがとうございます。</p>
<p>これでかんかんに焼肉をおごってもらえます！</p>

<p>本日はアドベントカレンダー最終日ということで、コードレベルの話から離れて</p>
<p>総括的な話をしてみようかと思います。</p>

<p>まず、今回のアドベントカレンダーでDBIx::Skinnyに興味を持った方は</p>
<p>ぜひ一度試してみてください！</p>
<p>わからないことはメールでもircでも可能な限り手厚くフォローします。</p>

<p>irc: dbix-skinny@irc.perl.org</p>
<p>mail: nekokak+skinny _at_ gmail.com</p>

<p>どうぞよろしくです。</p>

<p>いままでDBIx::Skinnyの話しをするときに引き合いでDBIx::Classを出すことが多かったです。</p>
<p>（DBICはパフォーマンスがでないとか）</p>
<p>DBICを比較対象に出すことが多かったのは私がもともとDBICを使い倒していたユーザで、</p>
<p>色々と不満に思うことがおおかったからです。</p>
<p>ただ、DBICは素晴らしいORMだと思っています。それはDBIx::Skinnyを作った今でも思っています。</p>

<p>DBICのドキュメントの充実ぶりや、世界のPerlHackerによるメンテナスが行われており</p>
<p>多くのユーザがいます。これはある種の安心感があります。</p>

<p>もし今あなたが、DBICを仕事でつかっており、仕事上でDBICに問題点を感じないのであれば</p>
<p>無理にDBIx::Skinnyを使う必要はないと思っています。</p>
<p>もちろん試してみては欲しいところですが、無理やり切り替えてやろうとかは考えない方が良いでしょう。</p>
<p>(DBIx::Skinnyがすごすぎてつかいたい！とか思ってる場合は別ですが）</p>
<p>DBICの問題点を感じないのにDBIx::Skinnyに切り替えるメリットはあまりありませんよ！</p>
<p>移行コストが高く付くだけです！</p>

<p>Perlには他にも素晴らしいORMがあります。</p>
<p>Data::Model Rose::DB::Object Class::DBI Data::ObjectDriver Fey::ORM etc...</p>
<p>その他の素晴らしいORMも色々使ってみてください。</p>
<p>それぞれに特徴があります。</p>
<p>その特徴を見極めた上であなたが本当に必要なORMを選択すればよいと思います。</p>
<p>ORMが必要ないと判断する人もいるでしょう。</p>

<p>ORMは必要ないけどSQLジェネレータは必要だから FeyはつかうとかRose::DBはつかうとかSQL::Abstractはつかうとか</p>
<p>そういう選択肢もありえます。</p>

<p>もっともDBI直接でいいじゃないかと言う人もいると思います。</p>
<p>DBI自体がORMだ！と言う人もいます。</p>

<p>CPANをめくればこんなに素晴らしプロダクトが一杯あります。</p>
<p>それをつまみ食いせずに眺めてるだけなんてもったいなくないですか？</p>

<p>ぜひ色々ためして、あなたが本当に必要とするプロダクトを選んでください。</p>

<p>そして、あなたがDBIx::Skinnyを選んでくれたらそれはとても素晴らしい私へのクリスマスプレゼントになるでしょう。</p>

<p>25日間ありがとうございました。</p>

<p>JPerl Advent Calendar 2009の発起人id:tokuhirom</p>
<p>JPerl Advent Calendarのコンテンツを置くサバーを用意してくれたid:yappo</p>
<p>DBIx::Skinnyのアドベントカレンダーを実行するきっかけになったのもyappoさんのおかげです。</p>
<p>また、DBIx::Skinnyのアドベントカレンダーを手伝ってくれた</p>
<p>id:walf443 id:daijirow id:studio-m(nekoya)</p>
<p>のみなさん。</p>
<p>本当にありがとうございました！</p>

<p>Merry Christmas</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <dc:creator>nekokak</dc:creator>
      <pubDate>Fri, 25 Dec 2009 00:55:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>DBIx::Skinny::InflateColumn::DateTimeで勝手にinflate/deflate #24</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/24.html</link>
      <description><![CDATA[<div class="section">
<p>昨日に続きましてnekoyaがお送りします。</p>

<p>今日もSkinny関連モジュールの紹介です。DBIx::Skinny::InflateColumn::DateTimeという名前だけで何をするかだいたい理解できそうな小物モジュールとなっております。</p>

<p>インストールは、CPANから</p>
<pre>
cpan DBIx::Skinny::InflateColumn::DateTime
</pre>
<p>するか、<a href="http://github.com/nekoya/p5-dbix-skinny-inflatecolumn-datetime">githubから</a>どうぞ。</p>

<p>これも昨日のSchema::Loaderと同じく、DBIx::Skinny本体のバージョンが古いと動きません。0.05以上にアップデートしておいてください。</p>

<p>利用方法は、Schemaクラスで</p>
<pre>
package Your::DB::Schema;
use DBIx::Skinny::Schema;
use DBIx::Skinny::InflateColumn::DateTime;
</pre>
<p>のように、useするだけです。</p>

<p>useすると、XXXX_at, XXXX_onなカラムが自動的にDateTimeオブジェクトにinflate/deflateされます。</p>

<p>実際にやっていることも、このAdvent Calenderの<a href="http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/04.html">4日目</a>でnekokakさんが紹介したサンプルとほぼ同等のごくシンプルなモジュールです。</p>

<p>と、これだけで終わると寂しいので、もう一つおまけを紹介します。</p>

<pre>
package Your::DB::Schema;
use DBIx::Skinny::Schema;
use DBIx::Skinny::InflateColumn::DateTime::Auto;
</pre>

<p>のように、DBI::Skinny::InflateColumn::DateTime::Auto（Inflate::DateTimeに同梱されています）をuseすると、insert/update時に特定のカラムに操作時刻を自動設定してくれます。</p>

<p>設定されるtriggerと対象カラムは以下になります。</p>

<table>
<tr>
<td> pre_insert: </td>
<td> created_at, created_on, updated_at, updated_on</td>
</tr>
<tr>
<td> pre_update: </td>
<td> updated_at, updated_on</td>
</tr>
</table>

<p>こうしたカラムの操作はDBの機能を使ってすることも可能ですが、DBに依存せずにアプリケーション側で処理したい場合は使ってみてください。</p>

<p>この機能は、hidekさんの</p>
<ul>
<li><a href="http://blog.hide-k.net/archives/2006/08/dbixclassauto_i.php">DBIx::Classでauto insert/update datetime - hide-k.net#blog</a></li>
</ul>
<p>にインスパイアされて盛り込みました。hidek++</p>

<p>実は、<a href="http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/04.html">4日目</a>に紹介されたcommon_triggerの機能は、このモジュールを作るのに欲しかったので追加したという経緯があります。あなたも周辺モジュールを書いていて「本体にこんな機能が欲しい」と思ったら、パッチを送ったり、IRCやブログで話を投げてみてください。</p>

<p>明日はいよいよ最終日。nakokakさんの大団円エンディングにご期待ください。</p>

<p>have a nice skinny days!:)</p>
</div>
]]></description>
      <dc:creator>nekoya</dc:creator>
      <pubDate>Thu, 24 Dec 2009 02:45:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>DBIx::Skinny::Schema::Loaderで楽々Schema設定 #23</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/23.html</link>
      <description><![CDATA[<div class="section">
<p>nekokakさんと名前が紛らわしいと評判のnekoyaです。</p>

<p>今日はSkinny本体ではなく、関連モジュールとしてDBIx::Skinny::Schema::Loaderを紹介します。</p>

<p>インストールは、ふつうにCPANから</p>
<pre>
cpan DBIx::Skinny::Schema::Loader
</pre>
<p>するか、<a href="http://github.com/nekoya/p5-dbix-skinny-schema-loader">githubからclone</a>してください。</p>

<p>DBIx::Skinny本体のバージョンが古いと動きません。CPANから0.05を入れるか、あるいは<a href="http://github.com/nekokak/p5-dbix-skinny">githubの最新版</a>を入れてください。</p>

<p>DBIx::Skinny::Schema::Loaderは名前から想像できるように、Skinnyで使用するSchemaを自動設定するモジュールです。</p>

<p>その場でinstall_tableを実行する動的生成の他に、Schemaクラスのファイルを書き出す静的生成にも対応しています。</p>

<p>対応DBはSQLite, MySQL, Postgresqlです。Skinny本体はOracleにも対応していますが、Schema::Loaderでは今のところサポートしていません。</p>
</div>
<div class="section">
<h3> ■動的生成</h3>

<p>Schemaクラスを以下のように書くと、ロード時にDBに合わせたSchema情報を設定します。</p>

<pre>
package Your::DB::Schema;
use base qw/DBIx::Skinny::Schema::Loader/;

__PACKAGE__->load_schema;

1;
</pre>

<p>通常、Schemaクラスでは use DBIx::Skinny::Schema; しますが、Schema::Loaderを使う場合はuse baseしてください。</p>

<p>load_schemaを呼ぶと、DBの中を見て、各テーブルのpkとcolumnsを設定するinstall_tableが実行されます。</p>

<p>それ以外の要素、例えばinflate/deflateやtriggerなどを設定したい場合は、</p>

<pre>
package Your::DB::Schema;
use base qw/DBIx::Skinny::Schema::Loader/;

install_table users => schema {
    trigger pre_insert => sub {
        my ($class, $args) = @_;
        $args->{ status } = 'hooked';
    };
};

__PACKAGE__->load_schema;

1;
</pre>

<p>のように書きます。install_tableは複数回に分けて書いてもいいので、ここに書いたtriggerとload_schemaが設定するpk, columnsの両方の設定が生きます。</p>

<p>load_schemaによる動的生成を使う場合の注意点として、Skinnyクラス（上の例だとYour::DB）にuse DBIx::Skinny setupでDBの接続情報を書いておくことが必要です。</p>

<p>newで接続情報を渡したりする場合でもやってやれなくはないのですが、無理矢理感があるので、そういったケースでは基本的には次の静的生成をお勧めしています。</p>
</div>
<div class="section">
<h3> ■静的生成</h3>

<p>まず、以下のようなスクリプトを書きます。publish_schema.plなど適当な名前で保存しておきましょう。</p>

<pre>
use DBIx::Skinny::Schema::Loader qw/make_schema_at/;
print make_schema_at(
  'Your::DB::Schema',
  {},
  [ 'dbi:SQLite:test.db', '', '' ]
);
</pre>

<p>そして、以下のようにすると、Your::DB::Schemaのファイルが書き出されます。</p>

<pre>
perl publich_schema.pl > lib/You/DB/Schema.pm
</pre>

<p>DBICと違ってSkinnyのSchemaは1ファイルで完結するので、直接ファイルを書き出すのではなく、標準出力に吐き出すようにしています。</p>

<p>make_schema_atは第2引数にオプションを指定して、出力するSchemaクラスのカスタマイズが出来ます。</p>

<p>例えば、先程のload_schemaのようなtrigger設定を入れるには以下のようにします。</p>

<pre>
use DBIx::Skinny::Schema::Loader qw/make_schema_at/;

my $before = << '...';
install_table users => schema {
    trigger pre_insert => sub {
        my ($class, $args) = @_;
        $args->{ status } = 'hooked';
    };
};
...

print make_schema_at(
  'Your::DB::Schema',
  {
    before_template => $before,
  },
  [ 'dbi:SQLite:test.db', '', '' ]
);
</pre>

<p>この他、install_tableブロックの後に指定の文字列を挿入するafter_templateや、install_tableブロックに任意のテンプレートを使用するtable_templateといったオプションがあります。詳細はPODをご覧ください。</p>

<p>PODには他にも動作の詳細やちょっとしたTipsを書いていますので、よかったら目を通してみてください（破滅的な英語ですが）。</p>

<p>このように、Schema::Loaderを使うとSchema定義を書く手間を軽減することが出来ます。個人的には、テーブル設計が流動的な開発初期はload_schemaによる動的生成を使い、ある程度落ち着いてきたらmake_schema_atによる静的生成に切り替えるといいんじゃないかと思ってます。</p>

<p>明日はDBIx::Skinny::InflateColumn::DateTimeを紹介します。</p>

<p>have a nice skinny days!:)</p>
</div>
]]></description>
      <dc:creator>nekoya</dc:creator>
      <pubDate>Thu, 24 Dec 2009 02:45:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>紹介しきれなかったメソッドまとめ #01</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/22.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちわ！こんにちわ！nekokakです！</p>
<p>☆４ついただきました！</p>
<p><a href="http://cpanratings.perl.org/dist/DBIx-Skinny">http://cpanratings.perl.org/dist/DBIx-Skinny</a></p>

<p>ということで、本日は久々に俺のターンということで</p>
<p>今までで説明しきれなかったDBIx::Skinnyの各種メソッドを紹介してみようと思います。</p>
</div>
<div class="section">
<h3> set_dbh</h3>

<p>DBIx::Skinnyをいままでつかっておらず、例えばDBI直接使っているコードがあり、</p>
<p>いきなり全てDBIx::Skinnyに置き換えるのが困難なケースもあるでしょう。</p>

<p>普通にDBIx::Skinnyを使う場合は、newする時の引数や、DBIx::SkinnyをuseしているModelクラスがわで</p>
<p>コネクション情報を書いておけば新規にコネクションをはりますが、</p>
<p>コネクション数を増やしたくないけど、DBIx::Skinnyを使ってみたい場合に</p>
<p>set_dbhメソッドをつかいます。</p>

<pre>
# もともとの処理でDBIを使っているとする
my $dbh = DBI->connect(...);
# このコネクションをDBIx::Skinnyに渡す。
Proj::DB->set_dbh($dbh);
# あとはいつも通りにDBIx::Skinnyを使う
Proj::DB->single('user', {name => 'nekokak'});
</pre>

<p>これだけで、今まで使っていたDBコネクションをつかいまわせます。</p>
</div>
<div class="section">
<h3> sql_for_unixtime</h3>

<p>いまさらですがunixtimeは何気につかいたくなることがあります。</p>
<p>DBIx::Skinnyを使っているQudoでもunixtimeを利用しています。</p>

<p>DBIx::Skinnyでunixtimeをつかうには以下のようにします。</p>

<pre>
Proj::DB->dbd->sql_for_unixtime;
Proj::DB->dbh->selectrow_array("SELECT $unixtime_sql")
</pre>

<p>こうすることで、接続しているDBDに応じたunixtimeを取得することができます。</p>

<p>本日はここまで。</p>
<p>明日はnekoya氏のターンです。</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <dc:creator>nekokak</dc:creator>
      <pubDate>Tue, 22 Dec 2009 02:06:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>Rowの拡張について2 #21</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/21.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちは。walf443の443はSSLのポート番号のことですか?とよく聞かれる<a href="http://d.hatena.ne.jp/walf443/">よしみ</a>です。</p>
<p>21日目は、<a href="http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/13.html">Rowの拡張</a>についてもうちょっとツッコんで話をしてみようと思います。</p>

<p>Proj::DBをDBIx::Skinnyのベースクラスとした場合に、Proj::DB::Row::TableNameみたいなクラスを作れば、singleやsearchなどの結果でjoinしていないときは、</p>
<p>Rowクラスが自動で適用されるようになるというのは前回お話したとおりかと思います。</p>

<p>Skinnyの思想的にはなるべくSQLをチューニングして書くことを意識しているので、CDBIや、DBICでやられているようなhas_a, has_manyみたいなものはあえて実装されていません。</p>
<p>とはいえ、アプリケーションの全ての箇所をカリカリにチューニングする必要はないと思いますし、チューニングと開発速度はトレードオフになることもあるので、</p>
<p>できるだけ早く新サービスをリリースしたいときのような開初初期段階にはお手軽なhas_aなどを使いたいといったこともあるかと思います。</p>

<p>というわけで、そういうのをRowに生やしたいときの例を示してみようかと思います。</p>

<pre>
package MyTest::Model::Schema;
use DBIx::Skinny::Schema;

install_table user => schema {
    pk 'id';
    columns qw/
        id
        name
        leader_user_id
        /;
};

1;

package MyTest::Model::Mixin::Row::HasA;
use strict;
use warnings;

sub import {
    my $pkg = caller;
    my $code_hasa = sub {
        my ($class, $method, $table, $column) = @_;

        my $code = sub {
            my $self = $_[0];
            $self->{"__cached_$column"} ||= $self->{skinny}->single($table, +{ id => $self->$column });
        };
        {
            no strict 'refs';
            no warnings 'redefine';
            *{"${pkg}::$method" } = $code; ## no critic
        }
    };

    {
        no strict 'refs';
        no warnings 'redefine';
        *{"${pkg}::has_a"} = $code_hasa; ## no critic
    }

}

1;

package MyTest::Model::Row::User;
use base qw(DBIx::Skinny::Row);
use MyTest::Model::Mixin::Row::HasA;

BEGIN {
    __PACKAGE__->has_a('leader', 'user', 'leader_user_id');
}

1;

</pre>

<p>みたいにしておくと、</p>

<pre>
use strict;
use warnings;
use Test::More;

my $db = MyTest::Model->new;

$db->create('user', {id => 1, name => 'alice'});
$db->create('user', {id => 2, name => 'bob', leader_user_id => 1 });
my $user = $db->single('user',{ id => 2});
my $leader = $user->leader;
is $leader->id, 1;
is $leader->name, 'alice';

</pre>

<p>みたいな感じにできます。</p>

<p>あまりRowにメソッドを増やしすぎると、column名と被ったりするとハマってしまいそうではありますが、</p>
<p>1プロジェクト内でRowにメソッドを増やすような場合は、テーブル名とかメソッド名やらに規約があったりして、</p>
<p>こういう仕組みを作っておくと、Row間で似たような処理を何度も書かなくてもよいよというお話でした。</p>

<p>これはあくまで例なので、お手軽に試せるように直接Rowから引けるskinnyを見にいっていますが、</p>
<p>あとからmemcachedやKVSから引くようにしたりしやすいので、Skinnyやcache処理などをwrapしたModel層を呼び出すような仕組みにしておくとよいでしょう。</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <dc:creator>walf443</dc:creator>
      <pubDate>Sun, 20 Dec 2009 16:29:02 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>Schemaファイルのメンテナンスについて #20</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/20.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちは。<a href="http://wassr.jp/">モバイルファクトリー</a>のよしみ、こと、<a href="http://d.hatena.ne.jp/walf443/">id:walf443</a>です。</p>
<p>20日目は、DBIx::SkinnyのSchemaファイルのメンテナンスについてです。</p>

<p>DBIx::Skinnyで共同開発してるときに、他の人がschemaが変わるような変更をあげたときに、手元のソースを最新にすると、</p>
<p>Schemaファイルは新しいのに、開発用のDBのSchemaが古いままということがままあります。</p>

<p>どこが変わったかは、コミットログを追えばだいたいはわかるんですが、ちゃんと直ったか確認するのはめんどうです。</p>

<p>ということでテストを書きましょう。</p>

<p>私はMySQLを使っているので、こんな感じでチェックするテストスクリプトを書いてます。</p>

<pre>
use strict;
use warnings;
use Proj::DB; # is a DBIx::Skinny.
use Data::Dumper;
use Test::More;
use YAML::XS;
use Text::Diff;

# SkinnyのSchemaとDBのSchemaの整合性をチェックするやつ

my $base = Proj::DB;
my $skinny_schema = $base->schema->schema_info;
my $skinny_table = {};
{
    for my $table ( sort keys %{ $skinny_schema } ) {
        $skinny_table->{$table} = [ sort @{ $skinny_schema->{$table}->{columns} } ];
    }
}

$base->connect;
my $dbh = $base->dbh;
my $db_table = {};
{
    for my $table ( $dbh->tables ) {
        # $table is like "table`.`column`".
        $table =~ s/^.+\.`(.+)`$/$1/;

        my $sth = $dbh->prepare(qq{ SHOW COLUMNS FROM $table });
        $sth->execute();
        my @columns;
        while ( my $row = $sth->fetchrow_hashref ) {
            push @columns, $row->{Field};
        }
        $db_table->{$table} = [ sort @columns ];
    }

}

for my $table ( keys %{ $skinny_schema }) {
    my $result = Text::Diff::diff(\YAML::XS::Dump($db_table->{$table}), \YAML::XS::Dump($skinny_table->{$table}) , {
        FILENAME_A => "db schema", FILENAME_B => "skinny schema",
    });
    ok(!$result, "$base schema $table table test" )
        or diag($result);
}

done_testing();

</pre>

<p>他には、<a href="http://github.com/nekoya/p5-dbix-skinny-schema-loader">DBIx::Skinny::Schema::Loader</a>というやつを使う方法もありますが、それはまた別の回があるそうです。</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <dc:creator>walf443</dc:creator>
      <pubDate>Sun, 20 Dec 2009 00:20:02 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>データのページングについて #19</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/19.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちわ！nekokakです！</p>
<p>十九日目はデータのページングについてです。</p>

<p>webの開発をしていると一度に大量のデータを表示させるのではなく、</p>
<p>１０件ずつデータを表示させ、画面には次のデータへアクセスするためのリンクを用意する</p>
<p>いわゆるページングの処理を入れることが良くあります。</p>

<p>例えば、DBICを使っている場合は、</p>

<pre>
my $itr = $rs->search(...);
my $pager = $itr->pager;
</pre>

<p>のように、DBIC側でData::Pageのオブジェクトを生成して、</p>
<p>データのページングをしやすいように提供してくれます。</p>

<p>DBICで開発している方は良く使われていることでしょう。</p>

<p>ただ、DBIx::Skinnyではこのような機能を提供していません。</p>

<p>理由としては、Data::Pageのオブジェクトを作成する為には、</p>
<p>検索対象となるデータが全部で何件存在するかを知る必要があるからです。</p>

<p>DBICの場合は、pagerメソッドを呼び出すと内部でcountクエリを発行し、</p>
<p>検索対象の全体件数を取得しています。</p>

<p>しかし、DBIx::Skinnyはsearch_by_sqlのように生のSQLを自分で書く事を前提としたORMなので</p>
<p>利用者が書いた任意のSQLを解析して同じ条件のcountクエリを発行することは</p>
<p>とても難しいことです。</p>

<p>なので、実装していません。</p>
<p>また、個人的にはDBICが内部で勝手にcountクエリを発行するのは嫌です。</p>

<p>というのも、あまり意識せずに使っていると想定しているよりも多くSQLが実行されてしまい</p>
<p>パフォーマンスに影響が出てくることが多いからです。</p>

<p>普段DBICを使って開発されている方は、DBICのクエリログ機能を使って、</p>
<p>発行しているSQLを一度全部見てみることをおすすめします。</p>

<p>もしかすると、想定異常にcountメソッドを発行しているかもしれませんよ。</p>
<p>（最近のDBICでは改善されているかもしれませんが）</p>

<p>じゃぁDBIx::Skinnyでデータのページングはどうすんだよ！</p>
<p>って思う人もいるとおもいますが、</p>
<p><a href="http://d.hatena.ne.jp/nekokak/20090924/1253761408">http://d.hatena.ne.jp/nekokak/20090924/1253761408</a></p>
<p>こちらに以前まとめた記事があるので、参考にしてみてください。</p>


<p>今日はここまで。</p>
<p>明日はid:walf443先生が書いてくれます。</p>
<p>お楽しみに。</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <pubDate>Sat, 19 Dec 2009 13:38:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>テーブルを動的に扱う方法について #18</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/18.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちは。モバイルファクトリーの千葉と申します。</p>
<p>今回DBIx::Skinnyを仕事で使わせてもらっている縁でエントリを担当させていただくことになりました。</p>

<p>18日目はDBIx::Skinnyでテーブルを動的に扱う方法について説明します。</p>


<p>DBの分割をする際などに、テーブルの構成は固定されているが、テーブル名は動的に指定したいという場合があると思います。</p>

<p>しかしDBIx::Skinnyでは使用するテーブルの名前や構成をあらかじめスキーマで定義しておく必要があるため、標準では未定義のテーブルに対して問い合わせをすることはできません（search_by_sqlについてはその限りではありませんが）。</p>

<p>そんなときは共通のスキーマをあらかじめ定義しておいて、実行時にそれを関連付けるという方法をとることができます。</p>

<pre>
package Proj::DB;
use DBIx::Skinny setup => +{
   dsn => 'dbi:SQLite:',
   username => '',
   password => '',
};

package Proj::DB::Schema;
use DBIx::Skinny::Schema;

# 共通スキーマの定義
install_table 'access_log' => schema {
   pk 'id';
   columns qw/id domain remote_host request status created_at/;
};

package main;

my $db = Proj::DB->new;

# テーブル作成
$db->do(q{ CREATE TABLE IF NOT EXISTS access_log_20091218 LIKE access_log });

# 作成したテーブルに共通スキーマの適用
$db->schema->schema_info->{access_log_20091218} = $db->schema->schema_info->{access_log};

# 通常のテーブルと同じように扱うことができる
$db->create('access_log_20091218', { id => 1, domain => 'example.com',
remote_host => '127.0.0.1', request => '/', status => 200 });
$db->update('access_log_20091218', { status => 500 }, { id => 1 });
$db->search('access_log_20091218', { id => 1 });
$db->delete('access_log_20091218', { id => 1 });
</pre>

<p>さらにこのテーブルのRowクラスも指定したいという場合はこのようにすることができます。</p>

<pre>
my $db = Proj::DB->new;

# あらかじめ定義しておいた共通Rowクラスと作成したテーブルをマッピング
$db->{row_class_map}->{access_log_20091218} = 'Proj::DB::Row::AccessLog';

my $row = $db->search('access_log_20091218', { id => 1 })->first;
print ref $row #=> 'Proj::DB::Row::AccessLog'
</pre>

<p>現在これらの処理をまとめたDBIx::Skinny::Mixin::ProxyTableというモジュールを開発中ですのでよろしくお願いします。</p>
<p><a href="http://github.com/daijirow/p5-dbix-skinny-mixin-proxy_table">http://github.com/daijirow/p5-dbix-skinny-mixin-proxy_table</a></p>

<p>以上、簡単ですがDBIx::Skinnyでテーブルを動的に扱う方法について説明しました。</p>


<p>ありがとうございました。</p>

</div>
]]></description>
      <pubDate>Fri, 18 Dec 2009 08:00:02 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>DB Shardingについて2 #17</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/17.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちわ！秘密主義のnekokakです！</p>
<p>十七日目は昨日のDBIx::SkinnyでのDB Shardingについての続きです。</p>

<p>昨日はミドルウェアでShardingして、アプリケーション側では楽をしようと言うお話でした。</p>

<p>しかし、私はミドルウェア屋さんではないので、ミドルウェアの細かいところまで面倒みれません。</p>
<p>面倒みれないので、ミドルウェアに頼らずに、アプリケーション側で何とかしようと考えてしまいます。</p>

<p>そこで今日は私が今まで試したことのあるDB Shardingの方法を紹介してみたいと思います。</p>

<p>関連記事としては、</p>
<p><a href="http://d.hatena.ne.jp/nekokak/20090917/1253119002">http://d.hatena.ne.jp/nekokak/20090917/1253119002</a></p>
<p>こちらを見ていただけると良いかと思います。</p>
<p>この記事で紹介した方法は、考察であって実際に運用で使ったことはありません。</p>


<p>では本題。</p>
<p>今回紹介するshardingの方法は以前WEB+DBの記事で紹介されたMixiさんでつかわれているというshardingの方法を参考にしたやり方です。</p>

<p>sharding管理用のmaster tableを作成します。</p>
<pre>
CREATE TABLE db_node (
  id     int(10) unsigned NOT NULL AUTO_INCREMENT,
  number int(10) unsigned NOT NULL、
  host   varchar(20) NOT NULL COMMENT 'DB接続先Host名',
  role   enum('master','slave') NOT NULL COMMENT 'nodeの用途',
  status enum('ok','down','wait') NOT NULL COMMENT 'nodeの状態',
  type   varchar(20) NOT NULL COMMENT '何に使うnodeなのかex)message',
  timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  KEY db_node_idx (type,role,status)
) ENGINE=InnoDB COMMENT='DB接続先マスター情報';

CREATE TABLE db_node_map (
  id     int(10) unsigned NOT NULL AUTO_INCREMENT,
  db_node_id int(10) unsigned NOT NULL,
  type_key varchar(30) NOT NULL,
  timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  UNIQUE KEY db_node_map (db_node_id,type_key),
  KEY db_node_id (db_node_id)
) ENGINE=InnoDB COMMENT='DB接続先管理テーブル';
</pre>

<p>db_nodeテーブルには、接続対象とできる、hostの管理をしています。</p>
<p>db_node_mapテーブルでは、db_nodeを任意のキーで割り振るマッピングをしています。</p>

<p>参考レコードとしては</p>
<pre>
> select id, number, host, role, status, type from db_node;
+----+--------+--------------+--------+--------+---------+
| id | number | host         | role   | status | type    |
+----+--------+-----------------+-----+--------+---------+
|  1 |      1 | 192.168.1.10 | master | ok     | message |
|  2 |      2 | 192.168.1.10 | master | ok     | message |
|  3 |      3 | 192.168.1.10 | master | ok     | message |
|  4 |      4 | 192.168.1.10 | master | ok     | message |
|  5 |      5 | 192.168.1.10 | master | ok     | message |
+----+--------+-----------------+-----+--------+---------+
</pre>

<p>このようになります。</p>
<p>また、db_node_mapの参考レコードとしては</p>

<pre>
> select id, db_node_id, type_key from db_node_map;
+----+------------+------------+
| id | db_node_id | type_key   |
+----+------------+------------+
|  1 |          1 | nekokak    |
|  2 |          2 | yappo      |
|  3 |          3 | nekoya     |
|  4 |          4 | walf443    |
|  5 |          5 | kan        |
+----+------------+------------+
</pre>

<p>このようになります。</p>
<p>db_node_mapは必要に応じて自動でレコードが追加されるようにしますが、</p>
<p>db_nodeは事前にレコードを作成しておく必要があります。</p>

<p>さて、このデータをどのようにDBIx::Skinnyで使うかです。</p>

<p>db_node/db_node_mapのデータを管理利用するmodelを作成します。</p>
<p>このmodelではuserテーブルのnameをキーに使ってどのdb_node/db_node_mapを使うかを決めます。</p>

<pre>
package Proj::Model::DBShardManager;
use strict;
use warnings;
use Proj::DB;

sub new {
    my $class = shift;
    bless {
        # db_node/db_node_mapの入っているdatabaseに接続する
        db => Proj::DB->new(....),
    }, $class;
}

# db_node_mapに$user_nameに対応するレコードを登録する
sub setup_handler_setting {
    my ($self, $user_name) = @_;
    die "user_name is not defined!" unless $user_name;

    # db_node_mapにすでに登録されている場合はスルー
    return if $self->{db}->single('db_node_map',{type_key => $user_name});

    # 使える全masterノード取得
    my @db_node_ids = map { $_->id } $self->{db}->search_by_sql(
        q{
            SELECT id
            FROM   db_node
            WHERE  type   = 'message'
            AND    role   = 'master'
            AND    status = 'ok'
        }, [], 'db_node'
    );
    die 'undefined db_node!!!' unless @db_node_ids;

    # 既に使用されているノードを取得
    my %list = map { $_->db_node_id => $_->count } $self->{db}->search_by_sql(
        q{
            SELECT db_node_map.db_node_id, count(db_node_map.db_node_id) AS count
            FROM db_node, db_node_map
            WHERE db_node.id   = db_node_map.db_node_id
            AND   db_node.type = 'message'
            GROUP BY db_node_map.db_node_id
            ORDER BY count ASC
        }, [], 'db_node_map'
    );

    my $use_db_node = +{};
    for my $db_node_id (@db_node_ids) {
        next if defined $use_db_node->{count} && ($use_db_node->{count} <= ($list{$db_node_id}||0));
        $use_db_node = +{
            count      => $list{$db_node_id}||0,
            db_node_id => $db_node_id,
        };
    }

    $self->{db}->insert('db_node_map',
        {
            db_node_id => $use_db_node->{db_node_id},
            type_key   => $user_name,
        }
    );
}

my $datasource = +{
    dsn      => '',
    username => 'user',
    password => 'password',
};
sub handler_for {
    my ($self, $user_name) = @_;
    die "user_name is not defined!" unless $user_name;

    my $row = $self->{db}->search_by_sql(
        q{
            SELECT db_node.id, db_node.number, db_node.host, db_node.type
            FROM   db_node, db_node_map
            WHERE  db_node.id = db_node_map.db_node_id
            AND    db_node.type         = 'message'
            AND    db_node.role         = 'master'
            AND    db_node_map.type_key = ?
            LIMIT 1
        }, [$user_name], 'db_node'
    )->first;

    if ($row) {
        $datasource->{dsn} = $row->gen_dsn;
    } else {
        $datasource->{dsn} = $self->setup_handler_setting($user_name)->gen_dsn;
    }

    my $db = Proj::DB->new($datasource);
    # 必要であればset namesを打つ
    $db->do(q{SET NAMES utf8});
    $db;
}
</pre>

<p>このProj::Model::DBShardManagerをつかうことで、</p>
<p>dbの接続先を変えたDBIx::Skinnyのインスタンスを取得することができるようになります。</p>

<pre>
use strict;
use warnings;
use Proj::Model::DBShardManager;

my $sm = Proj::Model::DBShardManager->new;
# user_name: nekokakをキーにしたDBIx::Skinnyのインスタンスを取得
my $db = $sm->handler_for('nekokak');
# DBIx::Skinnyのインスタンス取得後は普通のDBIx::Skinnyの操作を行うのみ
</pre>

<p>このようになります。</p>
<p>Proj::Model::DBShardManagerをもう少し工夫すれば、</p>
<p>slaveへの接続をとりだしたりすることもできますね。</p>

<p>駆け足の説明なので、わかりにくいこともあるかと思いますが</p>
<p>わからないことはどんどんircで質問してみてください。</p>

<p>他にもshardingの方法はあるので、また別の機会にでも。</p>

<p>というかこの方法は特別DBIx::Skinnyに特化しているわけではないので</p>
<p>何かの参考にしていただければと思います。</p>

<p>今日はここまで。</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <pubDate>Thu, 17 Dec 2009 05:26:02 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>DB Shardingについて1 #16</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/16.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちわ！nekokakです！</p>
<p>十六日目はDB ShardingとShardingしたDBにどのようにDBIx::Skinnyからアクセスするかについてです。</p>


<p>DBのShardingの方法は色々あります。</p>
<p>RDBMS側で提供される機能をつかうのも良いでしょう。</p>
<p>アプリケーションがわで任意の方法でShardingするのも良いでしょう。</p>

<p>ご自分の得意分野を活かしてShardingするといいと思います。</p>
<p>私は普段MySQLしか使わないので、今回はMySQLに限ったお話になりますが、参考にしていただければと思います。</p>

</div>
<div class="section">
<h3> テーブルパーティショニング</h3>

<p>MySQL5.1から使えるようになったテーブルパーティショニングの場合、</p>
<p>MySQLが内部でデータファイルを分割しており、アプリケーショ側は特に意識する必要はありません。</p>

<p>MySQL5.1のテーブルパーティショニングについては</p>
<p><a href="http://itpro.nikkeibp.co.jp/article/COLUMN/20080710/310540/">http://itpro.nikkeibp.co.jp/article/COLUMN/20080710/310540/</a></p>
<p>こちらを参考にするとよいでしょう。</p>
</div>
<div class="section">
<h3> Spider</h3>

<p>他には、斯波さんが作られている、SpiderというMySQLのストレージエンジンを使えば</p>
<p>MySQL5.1のテーブルパーティショニングとは異なり、</p>
<p>物理的に異なるサーバをつかってshardingすることが可能です。</p>

<p>この場合でも、アプリケーション側は特に意識する必要がありません。</p>

<p>Spiderについては</p>
<p><a href="http://wild-growth-ja.blogspot.com/">http://wild-growth-ja.blogspot.com/</a></p>
<p><a href="http://labs.unoh.net/2009/07/mysqlspider.html">http://labs.unoh.net/2009/07/mysqlspider.html</a></p>
<p>を参考にするとよいと思います。</p>


<p>上記２つはアプリケーション側では特に意識しなくてもRDBMSがわ（MySQL側）で勝手にshardingしてくれて、</p>
<p>データへのアクセスも特に意識することなく利用することができます。</p>

<p>このようなミドルウェアを使いこなせる方は試してみると良いと思います。</p>
</div>
<div class="section">
<h3> Incline</h3>

<p>次に奥一穂さんが作られているInclineというMySQLのプラグインを使う手もあります。</p>

<p>Inclineについては</p>
<p><a href="http://www.slideshare.net/kazuho/ss-2620503">http://www.slideshare.net/kazuho/ss-2620503</a></p>
<p>を参考にすると良いと思います。</p>

<p>InclineはMySQLのテーブルパーティショニングやSpiderとはことなり、</p>
<p>データへのアクセスはアプリケーション側で管理する必要があります。</p>

<p>InclineでshardingしたDBへのアクセス管理は</p>
<p>奥さんが作られているDBIx::ShardManagerをつかうとよいでしょう。</p>
<p><a href="http://svn.coderepos.org/share/lang/perl/DBIx-ShardManager/">http://svn.coderepos.org/share/lang/perl/DBIx-ShardManager/</a></p>

<p>今日は特別DBIx::Skinnyの話はでてきませんでしたが、</p>
<p>ミドルウェアを有効に使ってアプリケーション側で楽をすることで、</p>
<p>DBIx::Skinny側で特別なにかしなくても良い方法を考えるもの一つの手でしょう。</p>

<p>明日はミドルウェアに頼らないDB Shardingについてやろうとおもいます。</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <pubDate>Wed, 16 Dec 2009 02:25:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>DB以外でDBIx::Skinny::Iteratorを使う #15</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/15.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちわ！nekokakです！</p>
<p>十伍日目はDBIx::SkinnyでDB意外のデータを使う方法についてです。</p>

<p>DBIx::SkinnyはDBIxとついているように、DBIを拡張するモジュールです。</p>
<p>しかし、データのイテレーションはDBのデータ意外でも使いたい場合があると思います。</p>
<p>そこで、今日は、DBのデータ意外でのIteratorの使い方についてです。</p>

<p>と、その前に、DBIx::Skinny::Iteratorの使い方をざっくり説明しましょう。</p>

<p>DBIx::Skinny::IteratorはDB検索結果のレコードを取りまとめるオブジェクトとなります。</p>
<p>複数のレコードを取りまとめて、データをイテレーションさせるためのクラスです。</p>

<p>itaratorではnext/first/reset/count/allというメソッドがあります。</p>

<p>nextメソッドを使うと、１行ずつデータを取得して、処理を行うことができます。</p>
<pre>
my $itr = $db->search('user');
while (my $row = $itr->next) {
    # some process....
}
</pre>

<p>allメソッドを使うと、一気に配列に行データを格納することができます。</p>

<pre>
my $itr = $db->search('user');
my @users = $itr->all;
</pre>

<p>firstメソッドを使うと、イテレータから１行だけデータを取得することができます。</p>

<pre>
my $itr = $db->search('user');
my $user = $itr->first;
</pre>

<p>countメソッドを使うと、現在イテレータが保持しているデータのレコード数を取得することができます。</p>

<pre>
my $itr = $db->search('user');
my $count = $itr->count;
</pre>

<p>resetメソッドを使うと、途中まで進めたイテレータのポジションをはじめに戻すことができます。</p>
<p>下の例の場合、resetメソッドを呼び出さないと、allメソッドでは２行目以降のデータしか取得できません。</p>

<pre>
my $itr = $db->search('user');
# １行目を取得（イテレータのポジションが一個進む）
my $user = $itr->first;
# イテレータのポジションをリセットする
$itr->reset;
# 全行を取得する
my @users = $itr->all;
</pre>

<p>no_cacheメソッドというのがgithubの最新バージョンに存在します。</p>
<p>これはDBIx::Skinny::IteratorがDBから取り出した行データをインスタンスにキャッシュしているのをやめるものです。</p>
<p>resetメソッドを組み合わせて、同じSQLの実行結果を使いまわす場合は、</p>
<p>インスタンスにキャッシュされたデータをつかいまわすと、DBに余計なクエリを発行擦る必要がなく、</p>
<p>負荷軽減になるのですが、同じSQLの実行結果を使いまわさない場合で、</p>
<p>１クエリで大量のレコードが取得される場合、インスタンスにキャッシュしてしまうと</p>
<p>メモリの無駄遣いになるので、そういった場合に使用します。</p>

<pre>
my $itr = $db->search('user');
# itratorインスタンスにrowデータをキャッシュしないことを指示
$itr->no_cache;
my @users = $itr->all;
</pre>

<p>DBIx::Skinny::Itaratorの使い方としてはこんな感じです。</p>
<p>DBIx::Classを普段使っている人は特に違和感なく使えると思います。</p>

<p>さて、本日の本題である、DB意外のデータをDBIx::Skinny::Itaratorで扱う方法ですが、</p>
<p>例えば、</p>

<pre>
my @users = (
    {name => 'nekokak'},
    {name => 'yappo'},
    {name => 'nekoya'},
);
my $itr = Proj::DB->data2itr('user', \@users);
while (my $row = $itr->next) {
    warn $row->name;
}
</pre>

<p>このような使い方が可能です。</p>
<p>data2itrメソッドを使うと、ある、hashrefが格納されてるarrayrefをわたすと、</p>
<p>DBIx::Skinny::Iteratorのオブジェクトにしてくれます。</p>
<p>#14で説明した、memcachedから取得したデータをitaratorに戻すのと同じです。</p>

<p>itaratorに戻した後に取得される１データはDBIx::SkinnyのRowクラスでオブジェクト化されていますので、</p>
<p>あたかもDBから取得したデータのように扱うことが可能です。</p>

<p>本題がめっちゃ短いですが、simple is bestということで。</p>

<p>明日はDBIx::Skinnyを使った場合のdb shardingについて書いてみようかと思います。</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <pubDate>Tue, 15 Dec 2009 03:44:32 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>データをどのようにキャッシュするか #14</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/14.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちわ！nekokakです！</p>
<p>十四日目はDBIx::Skinnyでどのようにデータをキャッシュし、</p>
<p>キャッシュしたデータをどう扱うかについてです。</p>

<p>アプリケーションの負荷軽減策の一つとしてデータをmemcachedにキャッシュしてしまい、</p>
<p>DBアクセスをすくなくすることで、レスポンスを早くする手法はみなさん良く使われていると思います。</p>


<p>DBIx::SkinnyではSkinny自体の機能として、データを透過キャッシュするなどの便利機能は存在していません。</p>
<p>そこで、いろいろ工夫擦る必要があります。</p>

<p>なぜ、DBIx::Skinnyにデータの透過キャッシュする便利機能がないんだと思う人もいるかも知れません。</p>
<p>しかしDBIx::Skinnyの思想としてはDBIへのシンプルなwrapperなので、それ以上のことがしたければ、</p>
<p>DBIx::Skinny自体をラップしてくくださいと言う方針です。</p>

<p>また、データのキャッシュについても、DBIx::Skinny側でキャッシュ方法を明確に決めてしまわず、</p>
<p>ユーザの必要なケースに応じてキャッシュしてもらった方が効率が良いと考えたからです。</p>

<p>では、早速データのキャッシュ方法についてみていきましょう。</p>


<pre>
# キャッシュしたいユーザ情報を取得
my $user = $db->single('user', {name => 'nekokak'});
my $row = $user->get_columns; # get_columnsメソッドでデータをhashref化
my $cache = Cache::Memcached::Fast->new(....); # memcachedに接続
$cache->set('nekokak' => $row); # データをmemcachedにset
</pre>

<p>これだけです。</p>
<p>これでnekokakというキーでuserデータがmemcachedにキャッシュされました。</p>
<p>簡単ですよね。</p>

<p>次に、memcachedにキャッシュしたデータをどのように扱うかです。</p>


<pre>
my $cache = Cache::Memcached::Fast->new(....); # memcachedに接続
my $row = $cache->get('nekokak'); # memcachedからデータを取得
# memcachedから取得したデータをDBIx::SkinnyのRowオブジェクトにもどす
my $user = $db->data2itr('user', [$row])->first;
</pre>

<p>memcachedに入っているデータをDBIx::SkinnyのRowにもどしてしまえば、</p>
<p>あとはそのデータがDBから取得したものなのか、DBから取得したものなのかを気にする必要は全くありません。</p>

<p>簡単ですね。</p>

<p>明日は、今回でてきたdata2itrメソッドについて掘り下げていこうかと思います。</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <pubDate>Mon, 14 Dec 2009 00:52:02 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>Rowクラスの拡張について #13</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/13.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちわ！nekokakです！</p>
<p>十三日目はDBIx::SkinnyのRowクラスの拡張についてです。</p>

<p>Rowクラスとは、DB検索結果のレコードをオブジェクト化するクラスになります。</p>

<p>DBIx::Skinnyでは基本的にユーザがRowクラスを定義する必要がありません。</p>
<p>Rowクラスは自動で生成されるからです。</p>

<p>たとえば、</p>

<pre>
my $row = $db->singe('user', {name => 'nekokak'});
</pre>

<p>このような処理を行った場合、Rowクラスが自動で生成されますが、</p>
<p>Rowクラスのパッケージ名は、</p>

<pre>
DBIx::Skinny::Row::Cdb3ee45d06b8098ef489e14b9bbb801741e549c8
</pre>

<p>という名前になっています。</p>
<p>これは発行するSQLに応じたRowクラスを生成するため、</p>
<p>発行するSQLをsha1hashした値をベースにクラスを作り利用しています。</p>

<p>基本RowクラスにはSELECTしたカラム名をアクセサにしたメソッドのみが設定されていますので</p>

<pre>
$row->name; # nekokak
</pre>

<p>といった形でデータにアクセスすることが可能です。</p>


<p>基本的には取得したカラムにアクセスできれば十分だとはおもいますが、</p>
<p>Rowクラスを拡張したくなることもあるでしょう。</p>


<p>例えば、userテーブルにはfirst_name / last_nameというカラムが別々で存在している場合に、</p>
<p>いちいち</p>

<pre>
prit $row->first_name . ' ' . $row->last_name;
</pre>

<p>というようなことをあちこち書いて回るのは面倒ですよね？</p>
<p>そのような場合に、</p>

<pre>
print $row->full_name;
</pre>

<p>と、アクセスできれば良いようにしたいとします。</p>
<p>このような場合に、Rowクラスを拡張します。</p>


<p>今回の場合、userテーブルに対して検索をおこなっているので、</p>
<p>Proj::DB::Row::UserというRowクラスをつくりましょう。</p>

<pre>
package Proj::DB::Row::User;
use strict;
use warnings;
use base 'DBIx::Skinny::Row';
sub full_name {
    my $self = shift;
    return $self->first_name . ' ' . $self->last_name;
}
1;
</pre>

<p>このようにuserテーブルに対応するRowクラスを作成し、</p>
<p>必要なメソッドを定義するだけです。</p>

<p>簡単ですね！</p>


<p>おっと、はじめに、RowクラスはSQLをsha1hashした値をつかって自動で作ったクラスをつかうって言ったじゃないかといわれそうですが、</p>
<p>DBIx::Skinnyでは対応するRowクラスが存在する場合は、そのRowクラスを使います。</p>
<p>対応するRowクラスがない場合や、search_by_sqlメソッドなどをつかって、このクエリはどのRowクラスに対応させればよいか分からない場合に、</p>
<p>自動で生成したRowクラスをつかうようになります。</p>


<p>なれるまではこの独自Rowクラスと自動Rowクラスの使い分けが難しいこともあるかもしれませんが、</p>
<p>なれれば、必要に応じてRowクラスを拡張できるようになるでしょう。</p>

<p>明日はDBIx::Skinnyで取得したデータのキャッシュ方法などについてです。</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <pubDate>Sun, 13 Dec 2009 11:15:03 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>Mixinについて #12</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/12.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちわ！nekokakです！</p>
<p>十二日目はDBIx::SkinnyでのMixinについてです。</p>

<p>DBIx::Skinnyにはsearch/create/update/delete...などの基本的なメソッドが提供されています。</p>
<p>特定のプロジェクトでDBIx::Skinnyが提供していないようなメソッドを定義したい場合もあるかとおもいます。</p>

<p>その場合は以下のように書くことでメソッドを定義できます。</p>

<pre>
package Proj::DB;
use DBIx::Skinny;
sub some_method { 'somesome' }
1;
</pre>

<p>このようにDBIx::Skinnyをuseしているモジュール内でメソッドを定義するだけです。</p>

<p>そのプロジェクトでしか使わないようなメソッドであればこれでもいいとおもいますが、</p>
<p>よく使うようなメソッドを毎回このように定義するのはだるいですよね。</p>
<p>コピペはよくありません。</p>

<p>そこでDBIx::Skinny::Mixinの登場です。</p>
<p>DBIx::Skinny::Mixinをつかえば、よく使うメソッドを別パッケージで定義しておき、</p>
<p>必要に応じてmixinさせることが可能になります。</p>

<p>たとえば、よく使うsome_methodというメソッドでためしてみましょう。</p>

<pre>
package Your::Mixin;
use strict;
use warnings;
sub register_method {
    +{
        some_method => sub { 'somesome' },
    };
}
</pre>

<p>Your::Mixinというパッケージを用意しました。</p>
<p>Your::Mixinの中でregister_methodを定義します。</p>

<p>register_method は HASH リファレンスを返して、 </p>
<p>key が生やしたいメソッド名 value は、そのメソッドのコードリファレンスを渡します。</p>

<p>mixin で追加したメソッドの第一引数にはDBIx::Skinnyのオブジェクトが渡され、</p>
<p>第二引数以降はユーザによって渡された引数がそのまま入ります。</p>

<p>mixinを作ったらそれをProj::DBで使えるようにしてみましょう。</p>

<pre>
package Proj::DB;
use DBIx::Skinny;
use DBIx::Skinny::Mixin modules => ['+Your::Mixin'];
1;
</pre>

<p>このようにDBIx::Skinny::Mixinの１行を追加するだけです。</p>
<p>こうすることで今回指定したYour::Mixinのregister_methodで定義されたmethodが</p>
<p>Proj::DBでつかえるようになります。</p>

<p>Mixin機能を使えばDBIx::Skinnyを簡単に拡張することができますので</p>
<p>つかってみてください。</p>

<p>明日はRowクラスの拡張について書きます。</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <pubDate>Sat, 12 Dec 2009 11:44:04 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>プロファイリングについて #11</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/11.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちわ！nekokakです！</p>
<p>十一日目はDBIx::SkinnyでのSQLプロファイリングにつての考察です。</p>

<p>DBIx::Skinnyではsearch_by_sqlメソッドなどで複雑なSQLは生で書くことを推奨しています。</p>
<p>ORMの機能で複雑なhashrefをくみたてたり、</p>
<p>いろいろメソッドを呼び出して値を設定していく方法よりも、</p>
<p>生でSQLが書かれている方が可読性が上がるからです。</p>
<p>私はそう考えます。</p>

<p>ORMの利点としてはバックエンドのRDBMSを意識しなくていいというのがあげられますが、</p>
<p>ぶっちゃけそんなことたーどうでもいいです。</p>
<p>MySQLを使ってる人が急にポスグレでいくぜ！となって、</p>
<p>MySQL依存なクエリを生で書いてるから動かねーとかないですよね？</p>
<p>そんな酔っぱらいはどうでもいいです。</p>

<p>という冗談はさておき、search_by_sqlで生SQLを各場合は、クエリの最適化を行うのは用意ですね。</p>

<pre>
$db->search_by_sql('SELECT * FROM user');
</pre>

<p>と、SQLがみえてるので、ちゃちゃっとEXPLAINなどをかければいいと思います。</p>

<p>問題はormで生成したSQLをどのようにプロファイリングするかチューニングするかです。</p>
<p>まぁ、チューニングが必要なクエリはormで発行しないように。</p>
<p>で終わりでもいいんですが、１つの処理でどれだけのクエリが発行されるかのクエリ発行回数はみたかったりするでしょう。</p>
<p>またresultsetメソッドを使う場合なんかはインクリメンタルにクエリをくみたてるのであとで実際にどのようなクエリを実行したかをみたくなるでしょう。</p>

<p>DBIx::Skinnyでは発行したクエリを後から参照することができますので</p>
<p>その機能をつかうといいでしょう。</p>

<pre>
# 実験なので実際にファイルをつくらずメモリ上で試す
# DBコネクションの作成
my $db = Proj::DB->new({dsn => 'dbi:SQLite:'});
# 実験用テーブルを作成
$db->do(q{
    create table user (
        id       INTEGER PRIMARY KEY AUTOINCREMENT,
        name     TEXT    NOT NULL,
        birth_on DATE
    )
});
my $row = $db->create('user',{name => 'nekokak'});
$db->update('user',{name => 'yappo'}, {id => $row->id});
$row = $db->search('user', {name => 'yappo'})->first;
$row = $db->single('user', {id => 1});
$db->delete('user',{id =>1});

# query log を出力する
use Data::Dumper;
warn Dumper $db->profiler->{query_log};
</pre>

<p>このようなrun.plというプログラムがあるとします。これを、</p>

<pre>
SKINNY_PROFILE=1 perl ./run.pl
</pre>

<p>と実行するだけです。</p>
<p>すると以下のように出力されます。</p>

<pre>
$VAR1 = [
          'create table user ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, birth_on DATE )',
          'INSERT INTO user (`name`) VALUES (?) :binds nekokak',
          'SELECT id, name, birth_on FROM user WHERE (id = ?) :binds 1',
          'UPDATE user SET `name` = ? WHERE (id = ?) :binds yappo, 1',
          'SELECT id, name, birth_on FROM user WHERE (name = ?) :binds yappo',
          'SELECT id, name, birth_on FROM user WHERE (id = ?) LIMIT 1 :binds 1',
          'DELETE FROM user WHERE (id = ?) :binds 1'
        ];
</pre>

<p>発行したSQLとbindした値が一緒にでます。</p>
<p>こうすればどのようなSQLを実際に実行したのかわかるようになるので、</p>
<p>無駄をさがしやすいですね！</p>

<p>明日はDBIx:Skinny::Mixinについてやろうとおもいます。</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <pubDate>Sat, 12 Dec 2009 11:44:04 GMT</pubDate>
      <category></category>
    </item>
  </channel>
</rss>
