DBIx::Class + Oracle でプライマリーキーを auto increment する方法

Oracle には、MySQL の auto_increment 属性に該当する機能がありません。実現するには、次のサイトで紹介されているように、SEQUENCE と TRIGGER を組み合わせるというのが常套手段のようです。

でも、Perl の DBIx::Class を使えばラクチンです!例えば、次のようなクラスを作ったとします。

MyApp/Schema/Result/Post.pm
[cc lang="perl"]
package MyApp::Schema::Result::Post;

use strict;
use warnings;

use base ‘DBIx::Class’;

__PACKAGE__->load_components( “Core” );
__PACKAGE__->table(“post”);
__PACKAGE__->add_columns(
id => {
data_type => ‘NUMBER’,
size => 11,
is_nullable => 0,
is_auto_increment => 1,
},
title => {
data_type => ‘VARCHAR2′,
size => 500,
},
);
__PACKAGE__->set_primary_key(“id”);

1;
[/cc]

15行目で is_auto_increment => 1 という指定をしています。この状態で、DBIx::Class::Schema の deploy メソッドを実行すると、次のように TABLE と同時に SEQUENCE と TRIGGER を自動的に作成してくれます。

[cc lang="sql"]
CREATE SEQUENCE sq_post_id;

CREATE TABLE post (
id number(11) NOT NULL,
title varchar2(500),
PRIMARY KEY (id)
);

CREATE OR REPLACE TRIGGER ai_post_id
BEFORE INSERT ON post
FOR EACH ROW WHEN (
new.id IS NULL OR new.id = 0
)
BEGIN
SELECT sq_post_id.nextval
INTO :new.id
FROM dual;
END;
[/cc]

実際の処理は、SQL::Translator::Producer::Oracle が担当しているようです。

ちなみに、add_columns() でカラムを定義する時に、data_type => ‘timestamp’ とすると、同様に次のような TRIGGER が作成され、レコードの追加および更新時に自動的に SYSDATE をセットしてくれます。

[cc lang="sql"]
CREATE OR REPLACE TRIGGER ts_post_updated_at
BEFORE INSERT OR UPDATE ON post
FOR EACH ROW WHEN (new.updated_at IS NULL)
BEGIN
SELECT sysdate INTO :new.updated_at FROM dual;
END;
[/cc]

仕事で Oracle を使うことになり、「えー!auto_increment ないのー!」と困っていたのですが、おかげで楽に実装できました。欲を言えば、作成する SEQUENCE の START WITH を指定できたり、レコードの追加のときだけ SYSDATE をセットしてくれる TRIGGER が自動生成できれば、もっとありがたいんだけどなぁ。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>