【許曉笛】EOS 數據庫與持久化 API —— 實戰

EOS 數據庫開發實戰

上次的文章詳細講解了 EOS 數據庫的架構,本文將以官方示例爲基礎,詳解 EOS 數據庫的開發實戰。git

基本步驟

在智能合約裏與 EOS 數據庫交互,首先要定義存儲的數據:github

  • 定義對象:具體就是定義一個 C++ 類或者 C++ 結構體,數據表就由一個個對象組成。
  • 定義主鍵:在剛纔的類/結構體中,定義一個const類型的成員函數primary_key(),返回值必須爲uint64_t類型,返回值即爲主鍵。
  • 定義索引:EOS 數據表不光能夠按照主鍵搜索數據,還能夠定義多達 16 種索引。並且索引鍵(Key)不止支持64位無符號整數,還支持 12八、256位整數以及雙精度、四精度浮點數。
  • 爲每一個索引定義 鍵提取器(key extractor)

存儲數據定義好以後,就能夠與數據庫交互了:數據庫

  • 創建數據表:實例化 multi_index,創建數據表。
  • 增刪數據:使用emplace方法在表中添加數據;使用erace方法刪除數據。
  • 修改數據:使用modify方法修改數據。
  • 查詢數據:使用getfind方法和其餘迭代器操做查詢數據。

需求分析

咱們參考 EOS 的官方示例,創建一個「汽車修理店」智能合約所須要的數據庫。數據庫服務的對象是維修技師和車主。每次車輛維修保養後,維修技師均可以添加本次維修服務的信息,能夠更科學地管理每位客戶的車輛維修保養服務。並且維修技師和車主均可以更新車輛目前的里程,以便技師肯定車輛是否應該保養。咱們須要一個數據表:維修數據表(service Table)。segmentfault

創建數據對象

維修數據表中,每一條數據對象就是一次車輛維修保養的數據,包含如下成員:架構

  • 主鍵:由於數據表主鍵必須是惟一的,因此沒法用顧客的帳戶名做爲主鍵(同一個顧客有多條維修記錄)。這裏咱們讓系統自動生成主鍵。
  • 顧客帳戶:存儲每次維修服務的顧客帳戶名。
  • 維修日期:每次維修服務的日期。
  • 車輛里程:每次服務時,車輛的里程信息。

咱們還想方便的查詢每一個顧客的維修記錄,因此須要一個以顧客帳戶名爲鍵(Key)的索引。dom

這樣咱們就獲得了 service_rec 結構體:函數

struct service_rec {
    uint64_t        pkey;           // 主鍵
    account_name    customer;       // 顧客帳戶
    uint32_t        service_date;    // 維修日期
    uint32_t        odometer;        // 車輛里程
    
    //設置主鍵
    auto            primary_key()const { return pkey; }
    //設置索引
    account_name    get_customer()const { return customer; }

    //SERIALIZE 宏能夠幫助提升編譯速度
    EOSLIB_SERIALIZE( service_rec, (pkey)(customer)(service_date)(odometer) )
};

創建數據表

下面就能夠創建數據表了,首先,multi_index是個模板類:(對 C++ 模板不熟悉的能夠百度一下)區塊鏈

eosio::multi_index <uint64_t TableName, typename T, typename... Indices>

咱們須要填入如下multi_index的模板參數:ui

  • TableName爲數據表名稱,12字符之內,只能使用小寫字母,數字1-5,小數點「.」。
  • T爲數據對象類型,這裏就是咱們定義的service_rec結構體。
  • Indices爲索引列表,最多十六個。爲了下降開發難度,官方推薦使用const_mem_fun模板,你們能夠模仿官方的作法:

按照需求,咱們這樣設置multi_index的模板參數:spa

using service_table_type = multi_index<service/*<-數據表名稱*/, service_rec,/*<-數據對象類型*/
    /*設置索引->*/indexed_by< N(bycustomer), const_mem_fun<service_rec, account_name, &service_rec::get_customer> >
>;

這裏並無實例化multi_index,只是將填入相應模板參數的multi_index設置了一個別名:service_table_type。依然,對這裏的作法不熟悉的能夠看一下 C++ 模板類以及 C++ 的 using 關鍵字。

下面咱們實例化multi_index,構造函數須要兩個參數:

multi_index( uint64_t code, uint64_t scope )

其中,code爲數據表的擁有者,scope爲數據表的細分名稱。這裏有兩種理解,一種理解是不一樣的 scope 就是不一樣的數據表,也就是說,在同一個帳戶下,存在着TableName相同的多個數據表,他們的scope互不相同;另外一種理解:scope表示了同一個數據表的不一樣部分,互相獨立讀寫。這兩種理解的結果是同樣的,就是惟一肯定一個數據表須要三個參數:TableNamecodescope

實例化multi_index

service_table_type service_table( current_receiver(), mechanic );

上面的code = current_receiver(),表示當前的智能合約,即「汽車維修店合約」。若是這裏的code爲其餘合約,那麼說明這個multi_index指向了其餘帳戶名下的數據表,在本合約中就只能進行讀取操做了。scope = mechanic代表實例化的這個multi_index指向了細分名稱爲mechanic(以維修技師帳戶命名)的數據表。

咱們所創建的數據表結構以下圖所示。

![](pic1)

操做數據

通常數據庫的基本操做是增、刪、改、查,EOS 數據庫固然也具備這些功能。

新增數據

新增數據須要用到multi_indexemplace方法:

const_iterator emplace( unit64_t payer, Lambda&& constructor )

其中的payer參數位儲存空間支付帳戶,也就是由誰來提供新加入的這個數據對象的存儲空間,這裏填入維修技師mechanic帳戶。constructor是個 Lambda 表達式,也叫匿名函數,是向emplace方法傳入了一個構造函數,用來構造這個新的數據對象。

service_table.emplace(mechanic,/*<-儲存空間支付帳戶*/ [&]( auto& s_rec )/*<-匿名函數*/ {
    s_rec.pkey = service_table.available_primary_key(); /*<-系統生成可用主鍵*/ //匿名函數體
    s_rec.customer = eosio::chain::string_to_name(customer_name);             //匿名函數體
    s_rec.service_date = service_date;                                        //匿名函數體
    s_rec.odometer = odometer;                                                //匿名函數體
});

其中的customer_nameservice_dateodometer要在實際開發時使用有意義的變量。

查詢數據

因爲service_table數據表的主鍵是沒有意義的,因此咱們須要使用bycustomer索引來根據顧客帳戶名(customer)查詢數據。

auto customer_index = service_table.template get_index<N(bycustomer)>();

這樣咱們就獲得了bycustomer索引,咱們可使用索引的find方法來按照索引查找特定customer的數據對象。

//創建要查找的帳戶,注意這裏的customer_name要使用有意義的字符串
account_name customer_acct = eosio::chain::string_to_name(customer_name);
//使用`find`方法查找數據,使cust_itr(迭代器)指向所需數據
auto cust_itr = customer_index.find(customer_acct);

若是沒有查找到,cust_itr(迭代器)就是service_table.end(),也就是搜索到最後也沒有找到對應的數據。若是查找成功,cust_itr(迭代器)就會指向所需的數據對象。

以後,可使用下面的代碼能夠遍歷數據表中全部咱們所需的條目。(由於顧客帳戶名不是惟一的,用find方法會找到符合條件的第一條數據)

while (cust_itr != service_table.end() /*<-判斷迭代器位置*/&& cust_itr->customer == customer_acct/*<-判斷數據是否符合*/) {

    // 業務邏輯,對數據進行處理
   
    cust_itr++;//迭代器自增,指向下一條數據
}

修改數據

在迭代器指向數據後,能夠對數據進行修改,使用modify方法:

service_table.modify(cust_itr,/*<-迭代器*/, mechanic, /*<-儲存空間支付帳戶*/ [&]( auto& s_rec )/*<-匿名函數*/ {
    
    s_rec.customer = new_customer;             //匿名函數體
    s_rec.service_date = new_service_date;     //匿名函數體
    s_rec.odometer = new_odometer;             //匿名函數體
});

匿名函數中的new_customernew_service_datenew_odometer請使用有意義的變量。也能夠只修改其中部分變量。

刪除數據

在迭代器指向數據後,能夠對數據進行刪除,使用erase方法:

service_table.erase( cust_itr/*<-迭代器*/ );

至此,帶領你們了初步解了 EOS 數據庫開發的思路與方法,EOS 數據庫還有不少 API 能夠供智能合約使用,你們能夠查閱官方 Wiki:
https://github.com/EOSIO/eos/...


相關文章和視頻推薦

【許曉笛】EOS 數據庫與持久化 API —— 架構

圓方圓學院聚集大批區塊鏈名師,打造精品的區塊鏈技術課程。 在各大平臺都長期有優質免費公開課,歡迎報名收看。
公開課地址:https://ke.qq.com/course/345101     

相關文章
相關標籤/搜索