单例模式 (Multi_index singleton)

October 9, 2019 · View on GitHub

支持我们,请投票给 rex.m

singleton 其实是 Multi_index DB 的一种特例,也是用于存储合约的数据状态,只是它的只能存储一条记录。

单例模式的定义

接下来以一些简单例子来说明,这里单例表中定义了两个变量:布尔类型的 is_active,以及 uint64_t 类型的 play_times,表结构如下:

struct [[eosio::table]] game {
  bool is_active;
  uint64_t play_times;
}

从结构可以看出,和通常的 multi_index相比,它少了 primary_key。其实是在 singleton.hpp 中自定义了 primary_key

   template<name::raw SingletonName, typename T>
   class singleton
   {
      public:
      constexpr static uint64_t pk_value = static_cast<uint64_t>(SingletonName);
      struct row {
         T value;
         uint64_t primary_key() const { return pk_value; }
         EOSLIB_SERIALIZE( row, (value) )
      };

pk_value 的值是定义的表结构体名的 uint64_t 类型的值,在这里也就是 "game"_n.value 的值。而且单例模式表不存在 secondary_key

实例化

实例化方式和 multi_index 相同:

typedef eosio::singleton<"game"_n, game> global;
global state(get_self(), get_self().value);

添加或更新数据

singleton 有专门的 set 方法来添加或更新数据。

添加或更新表中所有字段:

ACTION update(){
  // 实例化
  global state(get_self(), get_self().value);
  state.set({true, 2019}, get_self());
}

set 参数说明:

  • 第一个参数: 需要添加或更新的所有字段值。
  • 第二个参数: 添加或更新表的RAM支付账户。

更新表中某个字段:

ACTION update() {
  // 实例化
  global state(get_self(), get_self().value);
  auto states = state.get();
  states.is_active = false;
  state.set(states, get_self());
}

查找

其实在上文中已经有提到:

global state(get_self(), get_self().value);
auto states = state.get();
eosio::print(states.is_active);
eosio::print(states.play_times);

这就可以查询表中字段的具体数值了。

删除

remove方法用于数据的删除:

ACTION del(){
  global state(get_self(), get_self().value);
  state.remove();
}

上述完整例子的源码:

#include <eosiolib/eosio.hpp>
#include <eosiolib/singleton.hpp>

CONTRACT hello : public eosio::contract {
  using eosio::contract::contract;
 public:
  ACTION update() {
    // 实例化
    global state(get_self(), get_self().value); 
    // 添加或更新数据
    // state.set({true, 2019}, get_self());
    auto states = state.get();
    states.is_active = false;
    state.set(states, get_self());
  }
  ACTION del() {
    global states(get_self(), get_self().value);
    // 删除数据
    states.remove();
  }
  // 单例表结构
  TABLE game {
    bool is_active;
    uint64_t play_times;
  };

  typedef eosio::singleton<"game"_n, game> global;

};
EOSIO_DISPATCH(hello, (update)(del))

总结

multi_index 在存储多实例的情况下是非常实用的,但是在存储单个实例,比如一些全局的状态等,使用 multi_index 则显得有些复杂,此时 singleton 闪亮登场了。