【许晓笛】EOS 数据库与持久化 API —— 实战

简介:

EOS 数据库开发实战

上次的文章详细讲解了 EOS 数据库的架构,本文将以官方示例为基础,详解 EOS 数据库的开发实战。

基本步骤

在智能合约里与 EOS 数据库交互,首先要定义存储的数据:

  • 定义对象:具体就是定义一个 C++ 类或者 C++ 结构体,数据表就由一个个对象组成。
  • 定义主键:在刚才的类/结构体中,定义一个const类型的成员函数primary_key(),返回值必须为uint64_t类型,返回值即为主键。
  • 定义索引:EOS 数据表不光可以按照主键搜索数据,还可以定义多达 16 种索引。而且索引键(Key)不止支持64位无符号整数,还支持 128、256位整数以及双精度、四精度浮点数。
  • 为每个索引定义 键提取器(key extractor)

存储数据定义好之后,就可以与数据库交互了:

  • 建立数据表:实例化 multi_index,建立数据表。
  • 增删数据:使用emplace方法在表中添加数据;使用erace方法删除数据。
  • 修改数据:使用modify方法修改数据。
  • 查询数据:使用getfind方法和其他迭代器操作查询数据。

需求分析

我们参考 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表示了同一个数据表的不同部分,互相独立读写。这两种理解的结果是一样的,就是唯一确定一个数据表需要三个参数: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/wiki/Persistence-API


相关文章
|
10天前
|
JSON API 数据处理
【Swift开发专栏】Swift中的RESTful API集成实战
【4月更文挑战第30天】本文探讨了在Swift中集成RESTful API的方法,涉及RESTful API的基础概念,如HTTP方法和设计原则,以及Swift的网络请求技术,如`URLSession`、`Alamofire`和`SwiftyJSON`。此外,还强调了数据处理、错误管理和异步操作的重要性。通过合理利用这些工具和策略,开发者能实现高效、稳定的API集成,提升应用性能和用户体验。
|
12小时前
|
SQL Oracle 关系型数据库
数据库SQL语言实战(五)(数据库系统概念第三章练习题)
本文的SQL语言适用的是Oracle数据库与mySQL可能存在略微不同
|
13小时前
|
SQL Oracle 关系型数据库
数据库SQL语言实战(三)
本篇文章重点在于SQL中的各种删除操作
|
1天前
|
SQL Java 数据库连接
JDBC Java标准库提供的一些api(类+方法) 统一各种数据库提供的api
JDBC Java标准库提供的一些api(类+方法) 统一各种数据库提供的api
8 0
|
5天前
|
存储 NoSQL 关系型数据库
MongoDB非关系型数据库实战
【5月更文挑战第6天】MongoDB,流行的NoSQL数据库,以其灵活的数据模型和高性能备受青睐。本文介绍了MongoDB的基础,包括文档型数据库特性、安装配置、数据操作。通过电商订单管理的实战案例,展示了MongoDB在处理复杂数据结构和大规模数据时的优势,适用于电商、游戏、视频直播等场景。MongoDB的索引、全文搜索和地理空间功能进一步增强了其实用性。注意性能优化和扩展性以确保系统稳定性和可靠性。
|
10天前
|
缓存 关系型数据库 MySQL
MySQL数据库性能优化实战
【4月更文挑战第30天】本文探讨了MySQL性能优化实战技巧,包括硬件与配置优化(如使用SSD、增加内存和调整配置参数)、索引优化(创建合适索引、使用复合索引及定期维护)、查询优化(避免全表扫描、减少JOIN和使用LIMIT)、分区与分片(表分区和数据库分片),以及使用缓存、定期清理数据库和监控诊断。通过这些方法,可以提升数据库性能和响应速度。
|
10天前
|
机器学习/深度学习 算法 安全
深度学习在图像识别中的应用与挑战构建高效可扩展的RESTful API:后端开发的实战指南
【4月更文挑战第30天】 随着计算机视觉技术的飞速发展,深度学习在图像识别领域取得了显著的成果。本文将探讨深度学习技术在图像识别中的应用及其所面临的挑战。首先,我们将介绍深度学习的基本原理和关键技术,然后分析其在图像识别中的优势和应用案例。最后,我们将讨论当前深度学习在图像识别领域所面临的主要挑战和未来的发展趋势。
|
12天前
|
存储 关系型数据库 MySQL
MySQL数据库实战:从入门到精通
本文介绍了MySQL的使用和优化,适合Web开发者阅读。首先,确保安装并配置好MySQL,熟悉SQL基础。接着,通过命令行客户端连接数据库,执行创建、查询、添加、修改和删除数据等操作。学习数据类型并创建表存储数据。最后,探讨了数据库优化,包括查询优化和索引使用,以提升性能。
24 2
|
16天前
|
SQL 关系型数据库 API
从API获取数据并将其插入到PostgreSQL数据库:步骤解析
使用Python处理从API获取的数据并插入到PostgreSQL数据库:安装`psycopg2`,建立数据库连接,确保DataFrame与表结构匹配,然后使用`to_sql`方法将数据插入到已存在的表中。注意数据准备、权限设置、性能优化和安全处理。
|
17天前
|
SQL 人工智能 JSON
【AI大模型应用开发】2.2 Function Calling连接外部世界 - 【实战】查询数据库
【AI大模型应用开发】2.2 Function Calling连接外部世界 - 【实战】查询数据库
20 0