本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回はカヤックの村瀬大輔さんで、
DBIx::Classとは
DBIx::ClassはPerlのO/
DBIx::ClassはPerlのORMとしては現在世界で一番使われているモジュールです。日本では最近データベース操作モジュールとしてより軽量なDBIx::SkinnyやData::Modelなどの注目が高まってきていますが、
DBIx::Classも登場時にはいろいろな記事になりましたが、
サンプルDBスキーマ
本稿ではリスト1の定義のデータベースをサンプルとして使用します。このサンプルデータベースはTwitterのデータ構造を模したものになっていて、
- user:ユーザ
- tweet:つぶやき
- following_
map:フォロー関係 - user_
profile:ユーザ詳細プロフィール
CREATE TABLE user (
id INTEGER NOT NULL PRIMARY KEY,
username VARCHAR(255) NOT NULL
);
CREATE TABLE user_profile (
id INTEGER NOT NULL PRIMARY KEY, -- user.id
full_name VARCHAR(255) NOT NULL,
bio VARCHAR(255)
);
CREATE TABLE following_map (
id INTEGER NOT NULL PRIMARY KEY,
user INTEGER NOT NULL, -- user.id
target INTEGER NOT NULL -- user.id
);
CREATE TABLE tweet (
id INTEGER NOT NULL PRIMARY KEY,
user INTEGER NOT NULL, -- user.id
body VARCHAR(255) NOT NULL,
created_date DATETIME NOT NULL,
modified_date DATETIME NOT NULL
);
基本的な使い方
まずはDBIx::Classの基本的な使い方を見てみましょう。
データベースクラス定義
DBIx::Classを使うには、
- Schemaクラス
- Resultクラス
- ResultSetクラス
●Schemaクラス
SchemaクラスはDBIx::Classを使ううえでベースとなるクラスで、
package My::Schema;
use strict;
use warnings;
use base 'DBIx::Class::Schema';
__PACKAGE__->load_namespaces;
1;
この例ではMy::Schemaというクラス名でSchemaクラスを作成しています。
SchemaクラスはDBIx::Class::Schemaを継承して作成し、load_
を使用するのが一般的です。
load_
は、
- My::Schema::Result::*にあるResultクラス
- My::Schema::ResultSet::*にあるResultSetクラス
を自動的にロードするメソッドです。
●Resultクラス
Resultクラスはデータベースのテーブルを表すクラスで、
Resultクラスは次のようにDBIx::Class::Coreを継承して作成します。
package My::Schema::Result::User;
use strict;
use warnings;
use base 'DBIx::Class::Core';
そしてこのResultクラスがデータベースのどのテーブルに紐付いているかを定義します。
__PACKAGE__->table('user');
続いてそのテーブルがどのようなカラムを持つのかも定義します。
__PACKAGE__->add_columns(qw/id username/);
カラム名だけでなく、
__PACKAGE__->add_columns(
id => {
data_type => 'INTEGER',
is_nullable => 0,
is_auto_increment => 1,
},
username => {
data_type => 'VARCHAR',
size => 255,
is_nullable => 0,
},
);
DBIx::Classのコアではこの詳細情報は使われないので必ずしも詳細に定義する必要はありませんが、CREATETABLE
を発行したり、
そして、
__PACKAGE__->set_primary_key('id');
ここまでが、
●ResultSetクラス
ResultSetクラスはResultクラスの集合を表すクラスです。このクラスの定義は必須ではありません。定義しなかった場合はデフォルトのDBIx::Class::ResultSetがそのまま使用されます。
定義したクラスを使用する
●データベース接続情報定義
定義したスキーマクラスを使用するには、
my $schema =
My::Schema->connect('dbi:SQLite:/path/to/my.db');
connectメソッドに渡す引数はDBIのそれとほとんど同じで、 データベーステーブルを操作するにはそのテーブルに対するResultSet オブジェクトを取得します。ResultSet オブジェクトはSchema オブジェクトのresultsetメソッドで取得できます。 このResultSetオブジェクトが持つ各種メソッドを使用することで、 特定のレコードに対する操作はResultオブジェクトを使用して行います このようにレコードに対する操作はResultオブジェクトで、 DBIx::Classでは複数テーブルの関係性をResultクラスに定義することで、 ユーザ DBIx::Class で1:多を定義するには、 と関係しているオブジェクトを直感的に取得できるようになります。 また、 1:1のリレーションとは、 DBIx::Classで1:1のリレーションを定義するには、 今回のサンプルではUserとUserProfileの関係が1:1にあたります。 多:多のリレーションとは今回の例で言うと、 DBIx::Classで多:多を定義にするには、 まず、 そしてこの1:多のリレーションを使用して多:多のリレーションを使用できるよう定義します。 以上の定義をすることで さて、 基本事項はこのくらいにして、My::Schema->connect( $dsn, $user, $password, $attrs );
●テーブルの操作
my $user_rs = $schema->resultset('User');
# プライマリキーによる取得
my $user = $user_rs->find(14);
# 検索
my $rs = $user_rs->search({
username => { -like => 'daisuke%' },
});
while (my $user = $rs->next) {
print $user->username, "\n";
}
# ユーザ作成
my $new_user = $user_rs->create({ username => 'daisuke' });
●レコードの操作
# 値の取得
print $user->username, "\n";
# データの更新
$user->username('daisuke');
$user->update;
# データの削除
$user->delete;
リレーションの定義
●1:多──belongs_
belongs_
)、has_
)belongs_
、has_
を用います。# in My::Schema::Result::User
__PACKAGE__->has_many(
tweets => 'My::Schema::Result::Tweet', 'user',
);
# in My::Schema::Result::Tweet
__PACKAGE__->belongs_to(
user => 'My::Schema::Result::User',
);
has_
メソッドの第3引数ではTweet側のユーザIDを格納しているカラム名を指定します。このようにすることで、my $tweet_rs = $user->tweets
my $user = $tweet->user;
# 特定のユーザのつぶやきを検索
my $tweet_rs =
$user->tweets({ body => { -like => 'Hello %' }});
# 特定のユーザにつぶやきを追加
$user->add_to_tweets({ body => 'Hello World!' });
●1:1──has_
has_
メソッド、might_
メソッドの2つがあります。has_
とmight_
の違いは、has_
はリレーション先が必ず存在する場合にしか使えないのに対し、might_
はリレーション先が存在しても存在しなくても定義できるということと、has_
はINNERJOIN
を使用するのに対し、might_
はLEFT JOIN
を使用するということです。# in My::Schema::Result::User
__PACKAGE__->has_one(
profile => 'My::Schema::Result::UserProfile',
);
●多:多──many_
many_
メソッドを用います。# Result::User
__PACKAGE__->has_many(
following_maps =>
'My::Schema::Result::FollowingMap', 'user'
);
__PACKAGE__->has_many(
follower_maps =>
'My::Schema::Result::FollowingMap', 'target'
);
# Result::FollowingMap
__PACKAGE__->belongs_to(
user => 'My::Schema::Result::User',
);
__PACKAGE__->belongs_to(
target => 'My::Schema::Result::User',
);
# Result::User
__PACKAGE__->many_to_many(
followings => following_maps => 'target' );
__PACKAGE__->many_to_many(
followers => follower_maps => 'user' );
$user->followers
という直感的なコードでユーザのフォロワー一覧のオブジェクトを取得できるようになります。●カスケーディングデリート
# これだけでユーザが持つつぶやきデータも同時に削除される
$user->delete;