【问题标题】:JSON data and DBIx::Class - how can I store data directly?JSON 数据和 DBIx::Class - 如何直接存储数据?
【发布时间】:2021-01-30 06:59:02
【问题描述】:

我有一个带有 JSON 列的 Postgres 数据库,我想做这样的事情:

use lib './lib';
use Digest::MD5 qw(md5 md5_hex);
use Data::Dump qw/dump/;

use Foo; # isa 'DBIx::Class::Schema';

$f = Foo->connect('dbi:Pg:database=mydb');

my $i = $f->resultset('People')->create({ id => md5_hex(rand() . 'some string') });
$i->insert;
$i->data({ name => 'john', surname => 'doe', birthday => '04/07/1976'});

#--------------------------------
# this is the important bit
# assigning to the HoH directly
#--------------------------------
$i->data->{books} = ['1984', 'Wuthering heights'];
$i->update;

Foo::People 看起来像这样:

__PACKAGE__->add_columns(
  "id",
  { data_type => "varchar", is_nullable => 0, size => 32 },
  "data",
  { data_type => "json", is_nullable => 1, accessor  => '_data', },
);

__PACKAGE__->set_primary_key(qw/id/);

use JSON::XS;
use Data::Dump qw/dump/;

sub data {
    my ($self, $value) = @_;
    if(@_ > 1) {
        my $value = encode_json($value);
        $self->_data($value);
    }
    $value = decode_json($self->_data());
    return $value;
}

但这不起作用 - 唯一能做的是将哈希存储在一个临时值中,更改它,然后再次存储它,如下所示:

my $v = $i->data;
$v->{books} = ['1984', 'Wuthering heights'];
$i->data($v)    
$i->update;

有没有更简洁的方法来做到这一点?

【问题讨论】:

    标签: perl postgresql dbix-class


    【解决方案1】:

    您正在寻找的是像 https://metacpan.org/pod/DBIx::Class::InflateColumn::Serializer::JSYNC 这样的 InflateColumn。

    您也可以自己轻松编写。

    不要忘记,当您调用 add_column 时,DBIC 只会为您生成列访问器,并且它们的名称是可配置的,因此可以重命名它们并从您自己的 aceessor 方法中调用它们。

    或者你使用 Moo(se) 方法修饰符。

    【讨论】:

    • DBIx::Class::InflateColumn docs中有很好的例子
    • 感谢@abraxxa - 这解决了问题,但只是部分解决。请看下面我的回答。无论如何欣赏。你让我走上了正确的道路。
    【解决方案2】:

    像 abraxxa 的答案那样对列进行充气/放气效果很好,但我仍然必须明确更新该列,否则不会存储对取消引用字段的分配。

    我最终为该列添加了一个属性,并在更新时添加了一个 before Moose 挂钩,如下所示:

    __PACKAGE__->add_columns(
                 "id" => { data_type => "varchar", is_nullable => 0, size => 32 },
                 "data" => {
                      data_type => "json",
                      is_nullable => 1,
                      always_update => 1
                 },
    );
    

    before 'update' => sub {
        my $self = shift;
        my %c = $self->get_columns;
        for (grep { $self->result_source->column_info($_)->{always_update} } keys %c) {
            $self->make_column_dirty($_);
        }
    };
    

    这样我就不需要每次都明确地调用data 列的更新。

    我不知道这是不是一个坏主意 - 欢迎 cmets。

    【讨论】:

    • DBIC 的更新方法采用更改列值的参数是一个糟糕的设计决策,因为它绕过了列访问器方法。因此,建议不要使用此 api 并始终使用其访问器修改结果(行)对象,并确保 update 在使用 before 或 around 方法修饰符传递列值时抛出异常。
    猜你喜欢
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    • 2013-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-08
    相关资源
    最近更新 更多