正文
本文将使用一个简单的例子帮助理解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": ""
}
注:参考自官方开发文档。