【问链-EOS公开课】第九课 EOS 数据库与持久化 API(二)
上次的文章詳細講解了 EOS 數據庫的架構,本文將以官方示例為基礎,詳解 EOS 數據庫的開發實戰。
基本步驟
在智能合約里與 EOS 數據庫交互,首先要定義存儲的數據:
定義對象:具體就是定義一個 C++ 類或者 C++ 結構體,數據表就由一個個對象組成。
定義主鍵:在剛才的類/結構體中,定義一個const類型的成員函數primary_key(),返回值必須為uint64_t類型,返回值即為主鍵。
定義索引:EOS 數據表不光可以按照主鍵搜索數據,還可以定義多達 16 種索引。而且索引鍵(Key)不止支持64位無符號整數,還支持 128、256位整數以及雙精度、四精度浮點數。
為每個索引定義鍵提取器(key extractor)。
存儲數據定義好之后,就可以與數據庫交互了:
建立數據表:實例化 multi_index,建立數據表。
增刪數據:使用emplace方法在表中添加數據;使用erace方法刪除數據。
修改數據:使用modify方法修改數據。
查詢數據:使用get、find方法和其他迭代器操作查詢數據。
需求分析
我們參考 EOS 的官方示例,建立一個“汽車修理店”智能合約所需要的數據庫。數據庫服務的對象是維修技師和車主。每次車輛維修保養后,維修技師都可以添加本次維修服務的信息,可以更科學地管理每位客戶的車輛維修保養服務。而且維修技師和車主都可以更新車輛目前的里程,以便技師確定車輛是否應該保養。我們需要一個數據表:維修數據表(service Table)。
建立數據對象
維修數據表中,每一條數據對象就是一次車輛維修保養的數據,包含以下成員:
主鍵:因為數據表主鍵必須是唯一的,所以無法用顧客的賬戶名作為主鍵(同一個顧客有多條維修記錄)。這里我們讓系統自動生成主鍵。
顧客賬戶:存儲每次維修服務的顧客賬戶名。
維修日期:每次維修服務的日期。
車輛里程:每次服務時,車輛的里程信息。
我們還想方便的查詢每個顧客的維修記錄,所以需要一個以顧客賬戶名為鍵(Key)的索引。
這樣我們就得到了 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的模板參數:
TableName為數據表名稱,12字符以內,只能使用小寫字母,數字1-5,小數點“.”。
T為數據對象類型,這里就是我們定義的service_rec結構體。
Indices為索引列表,最多十六個。為了降低開發難度,官方推薦使用const_mem_fun模板,大家可以模仿官方的做法:
按照需求,我們這樣設置multi_index的模板參數:
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表示了同一個數據表的不同部分,互相獨立讀寫。這兩種理解的結果是一樣的,就是唯一確定一個數據表需要三個參數:TableName,code,scope。
實例化multi_index:
service_table_type service_table( current_receiver(), mechanic );上面的code = current_receiver(),表示當前的智能合約,即“汽車維修店合約”。如果這里的code為其他合約,那么說明這個multi_index指向了其他賬戶名下的數據表,在本合約中就只能進行讀取操作了。scope = mechanic表明實例化的這個multi_index指向了細分名稱為mechanic(以維修技師賬戶命名)的數據表。
我們所建立的數據表結構如下圖所示。
操作數據
一般數據庫的基本操作是增、刪、改、查,EOS 數據庫當然也具有這些功能。
新增數據
新增數據需要用到multi_index的emplace方法:
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_name、service_date、odometer要在實際開發時使用有意義的變量。
查詢數據
由于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_customer、new_service_date、new_odometer請使用有意義的變量。也可以只修改其中部分變量。
刪除數據
在迭代器指向數據后,可以對數據進行刪除,使用erase方法:
service_table.erase( cust_itr/*<-迭代器*/ );至此,帶領大家了初步解了 EOS 數據庫開發的思路與方法,EOS 數據庫還有很多 API 可以供智能合約使用,大家可以查閱官方 Wiki:
https://github.com/EOSIO/eos/wiki/Persistence-API
總結
以上是生活随笔為你收集整理的【问链-EOS公开课】第九课 EOS 数据库与持久化 API(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【问链-EOS公开课】第八课 EOS 数
- 下一篇: 【问链-EOS公开课】第十课 EOS 错