正文
本文將使用一個簡單的例子幫助理解EOSIO中的數據持久性。
1. 在你的contract目錄下創建addressbook文件夾,並在其中創建addressbook.cpp文件。
cd /Users/yourUserName/Documents/EOS/contracts
mkdir addressbook
cd addressbook
touch addressbook.cpp
2. 在cpp文件中編寫一個繼承自eosio::contract的addressbook類。
同時,在public方法中繼承eosio::contract的構造函數。
#include <eosio/eosio.hpp>
using namespace eosio;
class [[eosio::contract("addressbook")]] addressbook : public eosio::contract {
public:
using contract::contract;
private:
};
3. 添加person表結構。
使用name類型來定義一個叫做key的變量,同時,編寫一個叫做primary_key()的方法(返回key的值),於是key就是該結構的主鍵。
struct person {
name key;
std::string first_name;
std::string last_name;
std::string street;
std::string city;
std::string state;
uint64_t primary_key() const { return key.value;}
};
4. 配置multi_index表。
typedef eosio::multi_index<"people"_n, person> address_index;
“people”是該表的名稱(_n表示爲name類型),person是前面定義的結構體,address_index是該類型的類型名(後面將使用其實例化表)。
5. 編寫添加修改記錄的方法。
[[eosio::action]]
void upsert(name user, std::string first_name, std::string last_name, std::string street, std::string city, std::string state) {
require_auth( user ); //只能操作自己的記錄,所以需要驗證操作的記錄是不是自己的。
//multi_index的實例化需要兩個參數,code和scope。
//code是擁有該合約的賬戶,scope是用於邏輯上的劃分(目前沒什麼作用)
address_index addresses(get_self(), get_first_receiver().value);
//創建一個查詢,使用find()方法查詢user並返回一個迭代器。
auto iterator = addresses.find(user.value);
//end()相當於null
if( iterator == addresses.end() )
{
//使用multi_index的emplace方法來添加一條記錄,需要兩個參數:user和callback function。
//user爲payer,這裏爲添加修改記錄的人;回調函數必須使用lamba函數創建引用。
addresses.emplace(user, [&]( auto& row ) {
row.key = user;
row.first_name = first_name;
row.last_name = last_name;
row.street = street;
row.city = city;
row.state = state;
});
}
else {
//使用multi_index的modify方法來修改記錄。
//回調函數將修改該用戶的記錄。
addresses.modify(iterator, user, [&]( auto& row ) {
row.key = user;
row.first_name = first_name;
row.last_name = last_name;
row.street = street;
row.city = city;
row.state = state;
});
}
}
6. 添加刪除記錄的方法
[[eosio::action]]
void erase(name user) {
require_auth(user);
address_index addresses(get_self(), get_first_receiver().value);
auto iterator = addresses.find(user.value);
//若結果爲fail,會給出後半部分給出的文字提醒。
check(iterator != addresses.end(), "Record does not exist");
//將記錄刪除。
addresses.erase(iterator);
}
完整代碼如下。
#include <eosio/eosio.hpp>
using namespace eosio;
class [[eosio::contract("addressbook")]] addressbook : public eosio::contract {
public:
// addressbook(name receiver, name code, datastream<const char*> ds): contract(receiver, code, ds) {}
using contract::contract;
[[eosio::action]]
void upsert(name user, std::string first_name, std::string last_name, std::string street, std::string city, std::string state) {
require_auth( user );
address_index addresses( get_self(), get_first_receiver().value );
// print("hi,",get_self());
print("hello,",get_first_receiver());
auto iterator = addresses.find(user.value);
if( iterator == addresses.end() )
{
addresses.emplace(user, [&]( auto& row ) {
row.key = user;
row.first_name = first_name;
row.last_name = last_name;
row.street = street;
row.city = city;
row.state = state;
});
}
else {
addresses.modify(iterator, user, [&]( auto& row ) {
row.key = user;
row.first_name = first_name;
row.last_name = last_name;
row.street = street;
row.city = city;
row.state = state;
});
}
}
[[eosio::action]]
void erase(name user) {
require_auth(user);
address_index addresses( get_self(), get_first_receiver().value);
auto iterator = addresses.find(user.value);
check(iterator != addresses.end(), "Record does not exist");
addresses.erase(iterator);
}
private:
struct [[eosio::table]] person {
name key;
std::string first_name;
std::string last_name;
std::string street;
std::string city;
std::string state;
uint64_t primary_key() const { return key.value; }
};
typedef eosio::multi_index<"people"_n, person> address_index;
};
7. 編譯並部署到addressbook賬戶
編譯。
eosio-cpp addressbook.cpp -o addressbook.wasm
創建一個叫addressbook的賬戶。
cleos create account eosio addressbook EOS6GwYrUANGhouVRR97W8ukiCPX49Z74toqMTTxGH8mViYiPuuPi -p eosio@active
將合約部署到該賬戶。
cleos set contract addressbook /Users/yourUserName/Documents/EOS/contracts/addressbook -p addressbook@active
若提示沒有解鎖的錢包,需要先解鎖一下(cleos wallet unlock解鎖默認錢包)。
8. 測試合約
(1) alice爲自己添加記錄。
cleos push action addressbook upsert '["alice", "alice", "liddell", "123 drink me way", "wonderland", "amsterdam"]' -p alice@active
結果如下。
executed transaction: 003f787824c7823b2cc8210f34daed592c2cfa66cbbfd4b904308b0dfeb0c811 152 bytes 692 us
# addressbook <= addressbook::upsert {"user":"alice","first_name":"alice","last_name":"liddell","street":"123 drink me way","city":"wonde...
(2) 查詢一下alice的記錄。
cleos get table addressbook addressbook people --lower alice --limit 1
結果如下。
{
"rows": [{
"key": "alice",
"first_name": "alice",
"last_name": "liddell",
"street": "123 drink me way",
"city": "wonderland",
"state": "amsterdam"
}
],
"more": false,
"next_key": ""
}
(3) 如果alice想爲bob添加記錄,測試一下。
cleos push action addressbook upsert '["bob", "bob", "is a loser", "doesnt exist", "somewhere", "someplace"]' -p alice@active
結果是失敗的,顯示如下。
Error 3090004: Missing required authority
Ensure that you have the related authority inside your transaction!;
If you are currently using 'cleos push action' command, try to add the relevant authority using -p option.
Error Details:
missing authority of bob
(4) alice刪除自己的記錄。
cleos push action addressbook erase '["alice"]' -p alice@active
顯示如下。
executed transaction: 0a690e21f259bb4e37242cdb57d768a49a95e39a83749a02bced652ac4b3f4ed 104 bytes 1623 us
# addressbook <= addressbook::erase {"user":"alice"}
warning: transaction executed locally, but may not be confirmed by the network yet ]
再次查詢。
cleos get table addressbook addressbook people --lower alice --limit 1
結果如下。
{
"rows": [],
"more": false,
"next_key": ""
}
注:參考自官方開發文檔。