トランザクション

my $txn = sub {
    my $img = $schema->resultset('Image')->find_or_create({
        image_id      => $image{image_id},
        extension     => $image{extension},
        width         => $image{width},
        height        => $image{height},
        filesize      => $image{filesize},
        original_name => $image{original_name},
        post          => $image{post},
    });
    foreach my $tag (@tags) {
        from_to($tag, $charset => 'UTF-8');
        my $t = $schema->resultset('Tag')->find_or_create({tag => $tag});
        $schema->resultset('ImageTag')->find_or_create({
            image_id => $img->image_id,
            tag_id   => $t->tag_id,
        });
    }
};
eval { $schema->txn_do($txn) };

あとはevalブロックの中でファイルの移動とかもやるべきかな。で、$@をチェックしてやればいいはず。ということは、File::Copy::moveして、成功したらunlinkしてやるのがいいかな。
あー、O/Rマッピングって便利。このレイヤをはさむことで、SQL自体にはほとんど触れなくてすむからね。DBIのレイヤでRDBMS*1へのアクセスを抽象化して、さらにDBICのレイヤでSQLそのものを抽象化して扱える。

*1:CSVとかRDBじゃないのもあるけどさ。