EOS智能合约开发系列(七): 多索引table

2018-08-19  本文已影响0人  鹏飞_3870

本文介绍eosio提供的一个可持久化状态的类:多索引table。可以理解为这是一组操作数据库的方法所组成的类。在写智能合约的时候,势必要持久化数据,要持久化数据就势必用到多索引table。

多索引table就是我们上一篇中遇到的这个类了:

eosio::multi_index

它提供了一组持久化数据的操作。它提供的功能类似于DB的功能,所以有人把它也称之为多索引DB。 不过,因为这个类更相当于db中的一个table(每次写入的数据都相当于的table中的一条record),所以我有时候会把这个类称之为table了。

为什么需要持久化存储

EOSIO合约是通过action来触发执行的。action在每次执行的时候都会有一个自己的上下文环境。 每个上下文会在每次action触发前,为其开辟一块干净的内存。所以在内存中的数据状态是无法在action之间传递的,也就是说,如果你在一个action中设置了某个变量,在另一个action中是无法获取到你对应的值的。在action之间传递状态的唯一方法,是将其持久化并从EOSIO数据库中检索它。

你可能会说,我修改类的字段应该是可以在action之间传递的吧?总不能全局变量也不行吧?是的,都不行。验证也很简单,比如我们修改一下我们之前写的hello合约的hello.cpp,像下面这样:

#include <eosiolib/eosio.hpp>
using namespace eosio;

int b = 2;

class hello : public eosio::contract {
  public:
      using contract::contract;

      /// @abi action 
      void hi( account_name user ) {
         a++;
         b++;
         require_auth( user );
         print( "Hi, ", name{user} , ";a=", a, ";b=", a);
      }

      void say( account_name user ) {
         a += 10;
         require_auth( user );
         print( "Say, ", name{user} , ";a=", a);
      }

  private: 
      int a = 0;
};

EOSIO_ABI( hello, (hi)(say) )

部署完之后,我们多次触发 hi和say,你会发现,每次输出的ab的结果都是一样的。

也就是说,持久化是在action之间传递状态的唯一方式。而table是我们目前持久化的唯一方法。

Multi-Index table 的特点

如何创建table

如何使用

举例说明

创建一个结构(要存储的数据结构类型)

      /// @abi table
      struct mystruct 
      {
         uint64_t     key; 
         uint64_t     secondid;
         std::string  name; 
         std::string  account; 

         uint64_t primary_key() const { return key; } // getter for primary key
         uint64_t by_id() const {return secondid; } // getter for additional key
      };

上面代码中,我们把想要存储的数据类型定义出来,并定义一些方法获取我们想要生成的索引的值。记住,我们必须要有一个primary_key方法,它返回我们表的主键。另外还可以定义最多16个其他的索引,每个所有都有一个getter方法对应,比如上面的by_id,就是一个secondidgetter

还有两点需要注意:

table的索引

typedef eosio::multi_index<N(mystruct), mystruct> datastore;

这个是C++的语法,typedef一个新的类型datastore,这样之后就可以用datastore声明变量,不需要eosio::multi_index<N(mystruct), mystruct>这么长了。第一个模板参数N(mystruct)代表table的名字,N的作用是可以把mystruct字符串转化为一个uint64_t类型的整数。第二个参数mystruct代表要存储的记录的类型。

如何指定二级索引呢?可以用indexed_by模板参数,像下面这样:

typedef eosio::multi_index<N(mystruct), mystruct, indexed_by<N(secondid), const_mem_fun<mystruct, uint64_t, &mystruct::by_id>>> datastore;

其中
indexed_by<N(secondid), const_mem_fun<mystruct, uint64_t, &mystruct::by_id>>
的意思是:

如果有更多的索引,可以像下面的代码这样:

      /// @abi table
      struct mystruct 
      {
         uint64_t     key; 
         uint64_t     secondid;
         uint64_t           anotherid;
         std::string  name; 
         std::string  account; 

         uint64_t primary_key() const { return key; }
         uint64_t by_id() const {return secondid; }
         uint64_t by_anotherid() const {return anotherid; }
      };
      
typedef eosio::multi_index<N(mystruct), mystruct, indexed_by<N(secondid), const_mem_fun<mystruct, uint64_t, &mystruct::by_id>>, indexed_by<N(anotherid), const_mem_fun<mystruct, uint64_t, &mystruct::by_anotherid>>> datastore;

注意:

这个记录类型mystruct必须与table的名字保持一致。也就是第一个模板参数必须是这种形式的N(mystruct);如果记录类型是tablename,那么第一个模板参数必须是N(tablename),按此类推。
如果你想让你的table对于外界是可见的,也就是可以通过cleos get table获取,那么table的名字必须小于12个字符,并且都是小写的;而且在记录类型声明的时候,一定要加上//@abi table字样。

一个完整的例子

using namespace eosio;
//所有智能合约继承contract类
class truelove : public eosio::contract {
  public:
      // 初始化lovertable, scope和contract都是自己
      truelove(account_name s):eosio::contract(s), lovetable(s, s)
      {}


      // @abi action
      void transfer(account_name sender, account_name receiver, asset quanity, string memo) {
        
        lovetable.emplace( _self, [&]( auto& s ) {
          //获取下一个可用的主键值,这样可以实现id的自动增长
          s.id = lovetable.available_primary_key();
          s.sender = sender;
        });
        
      }

  private:
      // @abi table 
      struct lover{
        uint64_t        id; //auto increment id
        account_name    sender;
        
        account_name primary_key()const { return id; }
        
        EOSLIB_SERIALIZE( lover, (id)(amount)(sender)(txHash)(letter) )
      };
      
      typedef eosio::multi_index<N(lover), lover> records;
      records lovetable;
};

虽然table的内部实现很复杂,不过用起来还是蛮简单的。这个例子只用到了emplace这个方法,其他方法也都是类似的,我就不一一举例展示了。后面需要用到这些方法的时候,我再强调一下。
我们在后面的智能合约的学习中,会经常用table类。

今天就到这里了,明天见。
简介:不羁,一名程序员;专研EOS技术,玩转EOS智能合约开发。
微信公众号:know_it_well
知识星球地址:https://t.zsxq.com/QvbuzFM

上一篇 下一篇

猜你喜欢

热点阅读