вторник, 2 августа 2011 г.

Баг ОРМ'а

Вкратце, баг касается переиспользования объекта, созданного с использованием параметра obj_new =1. Актуально при маппинге на таблицы с первичным ключом без auto_inctement. В этом случае после использования $obj->save(), значение ключа "убивается" и $obj->getID() выдает значение 0. Соответственно, при следующей попытке сохранения, в базе создается новая запись, соответствующая данному значению ключа (id=0), вместо ожидаемого апдейта существующей.

Ниже приведен пример, позволяющий убедиться в данной неприятности.

1) Таблица в БД:

CREATE TABLE `test` (
`id` int(11) NOT NULL,
`some_value` varchar(255),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=koi8r;

2) ОРМ:

package Rutube::Obj::Test;

use warnings;
use strict;

use Rutube::Object;
use base qw[Rutube::Object];


mapper( '-table' => 'test', '-key' => 'id' );
set_property_type('db');

property('id');
property('some_value');


1;

__END__

3) Тестовый скрипт:

#!/usr/bin/perl -w

use strict;
use warnings;

use lib qw|../lib|;
use Rutube::Config;
use Rutube::ObjectRegistry;

use Rutube::Obj::Track;

my $conf = Rutube::Config->new('../conf/.config');
Rutube::ObjectRegistry->setNamespace('offline');

use Rutube::Obj::Test;

my $obj = Rutube::Obj::Test->new({id => 123, some_value => 'asdf'}, {obj_new => 1});
printf "[id=%3d|some_value=%s]\n", $obj->getID(), $obj->getSomeValue();
# [id=123|some_value=asdf]

$obj->save();
printf "[id=%3d|some_value=%s]\n", $obj->getID(), $obj->getSomeValue();
# [id= 0|some_value=asdf]

$obj->setSomeValue("qwerty");
$obj->save();
printf "[id=%3d|some_value=%s]\n", $obj->getID(), $obj->getSomeValue();
# [id= 0|some_value=qwerty]

exit 1;

4) Имеем в результате:

idsome_value
0qwerty
123asdf

4 комментария:

  1. Да, не приятно и не понятно, что делать.

    М.б. добавить опцию force_id - ?

    ОтветитьУдалить
  2. Index: StoreFactory.pm
    ===================================================================
    --- StoreFactory.pm (revision 20062)
    +++ StoreFactory.pm (working copy)
    @@ -70,6 +70,9 @@
    $dbh->do( "INSERT INTO `$table` ($keys_str) values ($val_str)", undef, @values );
    my $id = $dbh->{mysql_insertid};
    $obj->setID($id);
    +
    + delete $obj->{obj_new};
    +
    return;
    } else {
    unless ( $obj->_isUpdated() ) {

    ОтветитьУдалить
  3. Помогло следующее:

    --- lib/Rutube/Object/StoreFactory.pm (revision 20352)
    +++ lib/Rutube/Object/StoreFactory.pm (working copy)
    @@ -68,8 +68,10 @@
    my $val_str = join( ', ', ('?') x ( scalar keys %$hash ) );
    my @values = map { $hash->{$_} } sort keys %$hash;
    $dbh->do( "INSERT INTO `$table` ($keys_str) values ($val_str)", undef, @values );
    - my $id = $dbh->{mysql_insertid};
    - $obj->setID($id);
    +
    + $obj->setID( $dbh->{mysql_insertid} ) unless $obj->{obj_new};
    + delete $obj->{obj_new};
    +
    return;
    } else {
    unless ( $obj->_isUpdated() ) {



    trunk:

    Sending lib/Rutube/Object/StoreFactory.pm
    Transmitting file data .
    Committed revision 20353.

    ОтветитьУдалить
  4. Красивая конструкция:

    if ( !delete $obj->{obj_new} ) {
    $obj->setID( $dbh->{mysql_insertid} )
    }

    Но слегка выворачивает мозг :)

    ОтветитьУдалить