FISCO BCOS CRUD小白使用攻略

CRUD是什么?

用我自己的话解释,就是`fisco-bcos`提供的区块链表格的增删改查,`CRUD`分别代表`Create`创建、`Read`读取(查询)、`Update`修改、`Delete`删除四个操作。

本文基于作者个人的坎坷摸索经验总结,有任何错误欢迎批评指正....

首先环境需要`Table.sol`合约,这里会提供一份(不要对`Table.sol`源码有任何改动)

pragma solidity ^0.4.24;

contract TableFactory {
    function openTable(string) public constant returns (Table);  // 打开表
    function createTable(string,string,string) public returns(int);  // 创建表
}

// 查询条件
contract Condition {
    //等于
    function EQ(string, int) public;
    function EQ(string, string) public;

    //不等于
    function NE(string, int) public;
    function NE(string, string)  public;

    //大于
    function GT(string, int) public;
    //大于或等于
    function GE(string, int) public;

    //小于
    function LT(string, int) public;
    //小于或等于
    function LE(string, int) public;

    //限制返回记录条数
    function limit(int) public;
    function limit(int, int) public;
}

// 单条数据记录
contract Entry {
    function getInt(string) public constant returns(int);
    function getAddress(string) public constant returns(address);
    function getBytes64(string) public constant returns(byte[64]);
    function getBytes32(string) public constant returns(bytes32);
    function getString(string) public constant returns(string);

    function set(string, int) public;
    function set(string, string) public;
    function set(string, address) public;
}

// 数据记录集
contract Entries {
    function get(int) public constant returns(Entry);
    function size() public constant returns(int);
}

// Table主类
contract Table {
    // 查询接口
    function select(string, Condition) public constant returns(Entries);
    // 插入接口
    function insert(string, Entry) public returns(int);
    // 更新接口
    function update(string, Entry, Condition) public returns(int);
    // 删除接口
    function remove(string, Condition) public returns(int);

    function newEntry() public constant returns(Entry);
    function newCondition() public constant returns(Condition);
}

TableFactory合约创建表

固定合约地址为`0x1001`。这句话的意思是,创建`TableFactory`时参数内固定写`0x1001`

    TableFactory tf; //将工厂作为全局变量,方便反复调用
    string public TABLE_NAME = "t_test4"; //表名作为全局变量,方便反复调用
    
    constructor() public {  //使用构造方法对其进行初始化
        tf = TableFactory(0x1001);  //创建表工厂对象固定使用地址0x1001
        tf.createTable(TABLE_NAME,"id","item_name, item_age"); //表名,主键名(目前只支持单个主键),表的其他字段名(字段之间以英文逗号分隔)	
    }

这个合约相当于表工厂,功能只有两个:

  • `createTable`创建表:返回错误码(int256),错误码详见下表
  • `opentTable`打开表:返回合约Table的地址,当表名不存在时返回空地址

这里需要注意,一个地址部署的合约,其表名一旦创建,再次重复部署使用相同的表名的话,会直接对原来创建过的表进行操作。

想要一个表名相同的全新表的话,就只能换个地址再部署了

我这里就是最开始建表后,如果中途修改表格,再添加表的列,键位增加。预编译是没有问题,但是再次调用新增函数就报错:`Receipt message: PrecompiledError`


Entry合约

`Entry`代表记录对象,一个`Entry`对象代表一行记录,一个表内容就是有很多行Entry对象组成的。

其接口如下:


Entries合约

`Entries`是记录集合对象,`Entries`用于存放Entry对象,查询数据后会返回这个`Entries`。

其接口如下:


Condition合约

查询、更新和删除记录时指定的过滤条件对象。这里需要注意,`Condition`条件为空表示不过滤,也可以根据需要使用条件过滤。但就算`Condition`条件为空也会有一个对主键值的过滤条件。

CRUD的主键不具备唯一性!!!

小白可以理解为类别,不管增删改查都必须带上主键值

其接口如下:


Table合约

用于对表进行增删改查操作的对象,`Condition`和`Entry`都是通过此合约创建,其接口如下:

注意看,CRUD四个操作都是有主键值作为条件的,因此需要谨记主键值不唯一!!!


案例实操:

第一步:引入Table.sol

使用`import "./Table.sol";`导入也好,直接粘贴在需要的合约文件内也罢,都是可以的。

import "./Table.sol";

第二步:创建表

在TableTest.sol合约文件中,创建表的核心代码如下:

contract TableDemo001 {
    TableFactory tf; //将工厂作为全局变量,方便反复调用
    string public TABLE_NAME = "t_test4"; //表名作为全局变量,方便反复调用
    
    constructor() public {  //使用构造方法对其进行初始化
        tf = TableFactory(0x1001);  //创建表工厂对象固定使用地址0x1001
        tf.createTable(TABLE_NAME,"id","item_name, item_age"); //表名,主键名(目前只支持单个主键),表的其他字段名(字段之间以英文逗号分隔)	
    }
}

createTable执行原理:createTable执行成功之后,将会在区块链系统表_sys_tables_(区块链启动会自动创建该表,专门记录区块链中所有表的信息)中插入t_test的表信息,即表名,主键名和其他字段名,但并没有正式创建该表。当对t_test表进行增删改查操作时,会首先判断t_test表是否存在,若不存在,则会查询_sys_tables_表获取t_test表的信息,如果查询有t_test表信息,则创建该表,否则执行失败。t_test表存在,则继续执行增删改查操作。

这一步是可选操作:比如新合约只是读写旧合约创建的表,则不需创建表这步操作。

第三步:针对表进行CRUD操作

在TableTest.sol合约文件中,插入记录核心代码如下:

    function insert(string memory id, string memory item_name, int item_age) public returns(int) {
        Table table = tf.openTable(TABLE_NAME); //通过表名打开表
        require(table != address(0),"table name not exist");    //检查表是否存在
        
        Entry entry = table.newEntry(); //创建Entry对象,相当于插入一行数据
        // 设置Entry对象,即设置记录的字段值
        entry.set("id", id);    
        entry.set("item_name",item_name);
        entry.set("item_age",item_age);
        
        // 调用Table的insert方法插入记录
        // 返回插入影响的记录数,值为1则插入成功,否则插入失败
        int count = table.insert(id, entry);
        return count;
    }

查询记录核心代码如下:

    // 因为无论如何都必须带上主键值,所以最普通的查询方法都相当于ById了
    // 使用""空字符串或"*"星号通配符作为主键值,都无法实现查询所有
    function selectById(string memory id) public returns(int[],string[],int[]) {
        Table table = tf.openTable(TABLE_NAME);
        require(table != address(0), "table name not exist");
        
        //创建Condition对象,条件为空表示不过滤,也可以根据需要使用条件过滤
        Condition condition = table.newCondition();
        Entries entries = table.select(id, condition);
        
        int size = entries.size(); // 获取记录集合的大小
        string[] memory item_name_list = new string[](uint256(size));
        int[] memory item_age_list = new int[](uint256(size));
        int[] memory id_list = new int[](uint256(size));
        
        // 遍历记录集合
        // 将记录的三个字段值分别存放到三个数组中,方便方法返回查询的数据
        for(int i = 0; i < size; ++i){
            // 根据索引获取记录集合中的记录(获取每一行的数据)
            Entry entry = entries.get(i);
            // 根据记录的字段名查询字段值
            // 注意字段值的类型不同需要选择相应的get方法
            id_list[uint256(i)] = entry.getInt("id");
            item_name_list[uint256(i)] = entry.getString("item_name");
            item_age_list[uint256(i)] = entry.getInt("item_age");
        }
        return (id_list,item_name_list,item_age_list);
    }

更新记录核心代码如下:

    function updateByAge(string memory item_age, string memory id) public returns(int) {
        Table table = tf.openTable(TABLE_NAME);
        require(table != address(0),"table name not exist");
        
        Entry entry = table.newEntry();
        entry.set("item_age",item_age); // 设置需要修改的属性
        
        Condition condition = table.newCondition(); // id = 1 && age >=10 && age <=15
        condition.GE("item_age",10);
        condition.LE("item_age",15);
        
        // 调用Table的update方法更新记录
        // 返回更新影响的记录数,值大于0则更新成功,否则更新失败
        int count = table.update(id, entry, condition);
        require(count > 0, "update count <= 0");
        return count;
    }

删除记录核心代码如下:

    function removeByNameAndAge(string memory id, string memory item_name, int item_age) public returns(string memory) {
        Table table = tf.openTable(TABLE_NAME);
        require(table != address(0), "table name not exist");
        
        Condition condition = table.newCondition(); // id = 1 && item_name ==10 && item_age ==15
        condition.EQ("item_name", item_name);
        condition.EQ("item_age",item_age);
        
        int count = table.remove(id,condition);
        if(count > 0){
            return "yes";
        }else {
            return "no";
            require(count <= 0, "remove count <= 0");
        }
    }

完整代码如下:

// SPDX-License-Identifier: MIT
pragma solidity ^0.4.24;
// 启用 ABI 编码器 V2
pragma experimental ABIEncoderV2;

// import "./Table.sol";

contract TableDemo001 {
    TableFactory tf; //将工厂作为全局变量,方便反复调用
    string public TABLE_NAME = "t_test4"; //表名作为全局变量,方便反复调用
    
    constructor() public {  //使用构造方法对其进行初始化
        tf = TableFactory(0x1001);  //创建表工厂对象固定使用地址0x1001
        tf.createTable(TABLE_NAME,"id","item_name, item_age"); //表名,主键名(目前只支持单个主键),表的其他字段名(字段之间以英文逗号分隔)	
    }
    
    function insert(string memory id, string memory item_name, int item_age) public returns(int) {
        Table table = tf.openTable(TABLE_NAME); //通过表名打开表
        require(table != address(0),"table name not exist");    //检查表是否存在
        
        Entry entry = table.newEntry(); //创建Entry对象,相当于插入一行数据
        // 设置Entry对象,即设置记录的字段值
        entry.set("id", id);    
        entry.set("item_name",item_name);
        entry.set("item_age",item_age);
        
        // 调用Table的insert方法插入记录
        // 返回插入影响的记录数,值为1则插入成功,否则插入失败
        int count = table.insert(id, entry);
        return count;
    }
    
    // 因为无论如何都必须带上主键值,所以最普通的查询方法都相当于ById了
    // 使用""空字符串或"*"星号通配符作为主键值,都无法实现查询所有
    function selectById(string memory id) public returns(int[],string[],int[]) {
        Table table = tf.openTable(TABLE_NAME);
        require(table != address(0), "table name not exist");
        
        //创建Condition对象,条件为空表示不过滤,也可以根据需要使用条件过滤
        Condition condition = table.newCondition();
        Entries entries = table.select(id, condition);
        
        int size = entries.size(); // 获取记录集合的大小
        string[] memory item_name_list = new string[](uint256(size));
        int[] memory item_age_list = new int[](uint256(size));
        int[] memory id_list = new int[](uint256(size));
        
        // 遍历记录集合
        // 将记录的三个字段值分别存放到三个数组中,方便方法返回查询的数据
        for(int i = 0; i < size; ++i){
            // 根据索引获取记录集合中的记录(获取每一行的数据)
            Entry entry = entries.get(i);
            // 根据记录的字段名查询字段值
            // 注意字段值的类型不同需要选择相应的get方法
            id_list[uint256(i)] = entry.getInt("id");
            item_name_list[uint256(i)] = entry.getString("item_name");
            item_age_list[uint256(i)] = entry.getInt("item_age");
        }
        return (id_list,item_name_list,item_age_list);
    }
    
    function updateByAge(string memory item_age, string memory id) public returns(int) {
        Table table = tf.openTable(TABLE_NAME);
        require(table != address(0),"table name not exist");
        
        Entry entry = table.newEntry();
        entry.set("item_age",item_age); // 设置需要修改的属性
        
        Condition condition = table.newCondition(); // id = 1 && age >=10 && age <=15
        condition.GE("item_age",10);
        condition.LE("item_age",15);
        
        // 调用Table的update方法更新记录
        // 返回更新影响的记录数,值大于0则更新成功,否则更新失败
        int count = table.update(id, entry, condition);
        require(count > 0, "update count <= 0");
        return count;
    }
    
    function removeByNameAndAge(string memory id, string memory item_name, int item_age) public returns(string memory) {
        Table table = tf.openTable(TABLE_NAME);
        require(table != address(0), "table name not exist");
        
        Condition condition = table.newCondition(); // id = 1 && item_name ==10 && item_age ==15
        condition.EQ("item_name", item_name);
        condition.EQ("item_age",item_age);
        
        int count = table.remove(id,condition);
        if(count > 0){
            return "yes";
        }else {
            return "no";
            require(count <= 0, "remove count <= 0");
        }
    }
    
    
    // 查询所有的方法,使用""空字符串或"*"星号通配符作为主键值,都无法实现查询所有
    
    // function selectAll() public returns(int[],string[],int[]) {
    //     Table table = tf.openTable(TABLE_NAME);
    //     require(table != address(0), "table name not exist");
        
    //     Condition condition = table.newCondition();
    //     Entries entries = table.select("*", condition);
    //     int size = entries.size();
    //     string[] memory item_name_list = new string[](uint256(size));
    //     int[] memory item_age_list = new int[](uint256(size));
    //     int[] memory id_list = new int[](uint256(size));
    //     for(int i = 0; i < size; ++i){
    //         Entry entry = entries.get(i);
    //         id_list[uint256(i)] = entry.getInt("id");
    //         item_name_list[uint256(i)] = entry.getString("item_name");
    //         item_age_list[uint256(i)] = entry.getInt("item_age");
    //     }
    //     return (id_list,item_name_list,item_age_list);
    // }
}





pragma solidity ^0.4.24;

contract TableFactory {
    function openTable(string) public constant returns (Table);  // 打开表
    function createTable(string,string,string) public returns(int);  // 创建表
}

// 查询条件
contract Condition {
    //等于
    function EQ(string, int) public;
    function EQ(string, string) public;

    //不等于
    function NE(string, int) public;
    function NE(string, string)  public;

    //大于
    function GT(string, int) public;
    //大于或等于
    function GE(string, int) public;

    //小于
    function LT(string, int) public;
    //小于或等于
    function LE(string, int) public;

    //限制返回记录条数
    function limit(int) public;
    function limit(int, int) public;
}

// 单条数据记录
contract Entry {
    function getInt(string) public constant returns(int);
    function getAddress(string) public constant returns(address);
    function getBytes64(string) public constant returns(byte[64]);
    function getBytes32(string) public constant returns(bytes32);
    function getString(string) public constant returns(string);

    function set(string, int) public;
    function set(string, string) public;
    function set(string, address) public;
}

// 数据记录集
contract Entries {
    function get(int) public constant returns(Entry);
    function size() public constant returns(int);
}

// Table主类
contract Table {
    // 查询接口
    function select(string, Condition) public constant returns(Entries);
    // 插入接口
    function insert(string, Entry) public returns(int);
    // 更新接口
    function update(string, Entry, Condition) public returns(int);
    // 删除接口
    function remove(string, Condition) public returns(int);

    function newEntry() public constant returns(Entry);
    function newCondition() public constant returns(Condition);
}

  • 11
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FISCO BCOS是一个基于联盟链技术的企业级区块链平台。通过FISCO BCOS,可以搭建单一群组的FISCO BCOS链联盟。 首先,要搭建FISCO BCOS链联盟,需要进行以下几个步骤。 第一步,准备环境。搭建FISCO BCOS链联盟需要一台可用的服务器,可以选择云服务器或物理服务器。同时,需要安装和配置必要的软件和环境,包括操作系统、Java开发环境、MySQL数据库等。 第二步,下载FISCO BCOS源代码。可以从FISCO BCOS官方网站下载最新版的FISCO BCOS源代码,并解压到服务器上的指定目录。 第三步,配置节点参数。进入解压后的FISCO BCOS源代码目录,修改config目录下的节点配置文件,配置节点的IP地址、端口号、节点类型等信息。 第四步,生成节点证书。在源代码目录下的nodes目录中,使用FISCO BCOS提供的脚本工具生成节点的证书和私钥。 第五步,初始化创世块。在源代码目录下,使用FISCO BCOS提供的脚本工具初始化创世块,并设置相关的参数,如创世节点的账户余额、链 ID、共识算法等。 第六步,启动节点。进入源代码目录下的script目录,执行启动节点的脚本命令,启动FISCO BCOS链联盟的节点。 通过以上步骤,就可以成功搭建单一群组的FISCO BCOS链联盟。在链联盟中,各个节点可以进行交易和通信,实现数据的共享和验证。需要注意的是,在搭建和操作FISCO BCOS链联盟时,要确保网络安全和相应的权限控制,保护链上数据的安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值