PerlのO/RマッパーTengの簡単なリファレンス

今回は Perl の O/Rマッパー Teng を触ってみます。特に触れない限りMySQLを利用しているということにして、コードのサンプルは DB名 teng_test / 利用するテーブルは users / そのテーブルレイアウトは

mysql> SHOW FIELDS FROM users;
+---------------+------------------+------+-----+---------+----------------+
| Field         | Type             | Null | Key | Default | Extra          |
+---------------+------------------+------+-----+---------+----------------+
| id            | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name          | varchar(50)      | NO   |     | NULL    |                |
| birth_date    | date             | NO   |     | NULL    |                |
| created_date  | datetime         | YES  |     | NULL    |                |
| modified_date | datetime         | YES  |     | NULL    |                |
+---------------+------------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

に接続する例、ということにしておきます。

まず最初に、インストールは他の CPANモジュール同様 cpanm で。(入ってなければ) 合わせて DBD::mysql (ここは利用するDBに合わせて任意で)も入れます。cpanm を利用している場合は

$ cpanm Teng DBD::mysql

でインストールが完了します。

次に簡単な使い方を。以下は id が1 のレコードを1件取得して name の値を出力する例。

use strict;
use warnings;
use utf8;
use Teng;
use Teng::Schema::Loader;

my $dsn    = 'dbi:mysql:teng_test';
my $user   = 'db_user';
my $passwd = 'db_password';

my $dbh = DBI->connect($dsn, $user, $passwd, {
    'mysql_enable_utf8' => 1,
});

my $teng = Teng::Schema::Loader->load(
    'dbh'       => $dbh,
    'namespace' => 'MyApp::DB',
);

my $row = $teng->single('users', {'id' => 1});
print $row->name;

1;

Tengクラスのインスタンスを作る部分を分離して書くこともできます。

次のコードを teng_instance.pl として……

use strict;
use warnings;
use utf8;
use Teng;
use Teng::Schema::Loader;

my $dsn    = 'dbi:mysql:teng_test';
my $user   = 'db_user';
my $passwd = 'db_password';

my $dbh = DBI->connect($dsn, $user, $passwd, {
    'mysql_enable_utf8' => 1,
});

my $teng = Teng::Schema::Loader->load(
    'dbh'       => $dbh,
    'namespace' => 'MyApp::DB',
);
1;

以下のように、インクルードすることもできるらしいです。

use strict;
use warnings;
use utf8;

my $teng = do('teng_instance.pl') or die $@;

my $row = $teng->single('users', {'id' => 1});
print $row->name;

1;

レコードの取得

1件のレコードを取得する

前のサンプルコードでも挙げたように、single メソッドを利用します。

my $row = $teng->single('users', {'id' => 1, 'name' => 'user01'});

この場合、条件は id = 1 AND name = ‘user01’ となります。$row には Row オブジェクトが返ってきて、

print $row->birth_date;

という具合でカラムに対応したメソッドを叩くと値を返してくれます。

複数件のレコードを取得する

以下のコードは id IN(1,2) の条件で検索する場合です。

my $iter = $teng->search('users', { 'id' => [1, 2] });

while ( my $row = $iter->next ) {
    print $row->name;
}

#08 search – Perl Advent Calendar Japan 2011 Teng Trackによると

TengのsearchメソッドはSQL::Makerのselectメソッドのラッパーになっていますので

詳細はそちらを参照していただくほうがよいでしょう。

ということです。なので、seachメソッドの第2引数はSQL::Maker::Condition – condition object for SQL::Maker – metacpan.org の CONDITION CHEAT SHEETがまるまる利用できるようです。

その結果、先ほどのサンプルは以下のようにも書けます。

my $iter = $teng->search('users', [ 'id', [1, 2] ]);

そのほか、簡単な例。

or検索
my $iter = $teng->search('users', [
    'name', [
        {'=' => 'username01'},
        {'=' => 'username02'},
    ]
]);
検索条件は (`name` = ‘username01’) OR (`name` = ‘username02’)
もう少し書きようがあったかも……
like検索
my $iter = $teng->search('users',  [ 'name', {'like' => 'user%'}  ]);
検索条件は (`name` LIKE ‘user%’)

なお、以下のように配列で受けると検索結果全てのRowオブジェクトを配列で取得できます。

my @rows = $teng->search('users',  [ 'name', {'like' => 'user%'}  ]);
for my $row (@rows) {
    print $row->name, "\n";
}

レコードの追加

my $row = $teng->insert('users' => {
    'name'         => 'user01',
    'birth_date'   => '2001-01-01',
    'created_date' => \'NOW()',
});

$row には INSERT した内容に伴う Rowオブジェクトを受け取ります。DBIx::QueryLogを通すと

INSERT INTO `users` (`name`, `created_date`, `birth_date`) VALUES ('user01', NOW(), '2001-01-01')
SELECT `modified_date`, `name`, `created_date`, `id`, `birth_date` FROM `users` WHERE (`id` = '3') LIMIT 1

と レコードの作成直後にそのレコードの検索を行なっていて、その結果をRowオブジェクトに収めて返してます(実際にはINSERTの前にも色々SQLを発行してます)。

Rowオプションが不要な場合は

my $id = $teng->fast_insert('users' => {
    'name'         => 'user01',
    'birth_date'   => '2001-01-01',
    'created_date' => \'NOW()',
});

を利用します。この場合の返り値はINSERTしたレコードに割当てられたidとなります。

レコードの更新

my $count = $teng->update(
    'users' => {
        'modified_date' => \'NOW()',
    }, {
        'modified_date' => undef,
    }
);

発行されるSQLは

UPDATE `users` SET `modified_date` = NOW() WHERE (`modified_date` IS NULL)

となります。返り値は実際に更新したレコード数です。

レコードの削除

my $count = $teng->delete('users' => {'id' => 3});

発行されるSQLは

DELETE FROM `users` WHERE (`id` = '3')

となります。返り値は実際に削除したレコード数です。

任意のSQLを実行する

doメソッドで任意のSQLが実行可能です。最初のテーブル作成をこんな感じでTengから実行できます。

my $result = $teng->do(q{
  CREATE TABLE IF NOT EXISTS `users` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(50) NOT NULL,
    `birth_date` date NOT NULL,
    `created_date` datetime DEFAULT NULL,
    `modified_date` datetime DEFAULT NULL,
    PRIMARY KEY (`id`)
  ) ENGINE=InnoDB  DEFAULT CHARSET=utf8
});

返り値は対象になったレコード件数か、そうでない場合は 0E0 が返ります。またSQLが失敗した際には例外が発生して終了します。

参照URL