Solidity同一mapping键覆盖旧值后如何从区块链上检索历史数据?
问题根因
你遇到的现象是Solidity mapping类型的固有特性导致的,和区块链数据不可篡改的特性并不冲突:
- Solidity中的
mapping(uint => eventStruct)是键值对存储结构,同一个键重复赋值会直接覆盖旧值。你两次调用loggingAccess都传入了相同的_nik = 10001,第二次调用会直接把dictConfirmAccess[10001]对应的结构体整体替换为新传入的值,因此你调用checkData查询的是合约当前的最新状态,自然只能拿到第二次写入的结果。
疑问逐一解答
1. 第一次写入的数据有没有被删除?
合约当前状态里的第一次写入数据是被覆盖了,没有保留在dictConfirmAccess这个状态变量里,但不等于数据被彻底删除。
2. 为什么区块链数据永久可查,却读不到第一次的写入结果?
区块链永久存储的是所有历史交易、区块数据以及事件日志,而你调用checkData读的是合约执行完所有历史交易后的最新状态快照,不是历史交易数据。第一次写入的所有信息都保存在你第一次调用loggingAccess的那笔链上交易里,只是没有体现在当前的合约状态中。
3. 怎么检索到第一次写入的数据?
分两种场景实现:
不修改现有合约的检索方式
找到你第一次调用loggingAccess的那笔交易哈希,解码交易的input字段,就能提取到第一次传入的所有参数(时间、事件名、event_id、data_nik),所有链上交易都是永久存证不可篡改的,不会丢失。
优化合约支持直接查询历史记录
如果后续需要长期查询同一个nik的所有写入历史,可以修改合约存储结构,两种常用方案:
- 方案1:将mapping改为存储结构体数组
把mapping(uint => eventStruct) public dictConfirmAccess;改成mapping(uint => eventStruct[]) public dictConfirmAccess;,每次调用loggingAccess时,往对应_nik的数组里push新的结构体,而不是直接赋值,后续就可以通过索引读取同一个nik下的所有历史记录。 - 方案2:新增事件日志(更推荐)
在合约中定义事件,每次写入数据时触发事件,链上的事件日志支持按索引过滤查询,比查交易更方便:
// 定义事件,nik加indexed支持后续过滤查询 event AccessLogged(uint indexed nik, string event_log_time, string event_name, string event_id, uint data_nik); function loggingAccess( uint _nik, string memory _event_log_time, string memory _event_name, string memory _event_id, uint _data_nik ) public{ dictConfirmAccess[_nik] = eventStruct( event_log_time : _event_log_time, event_name : _event_name, event_id : _event_id, data_nik : _data_nik ); arrayOfData.push(_nik); // 触发事件 emit AccessLogged(_nik, _event_log_time, _event_name, _event_id, _data_nik); }
后续你只需要用nik = 10001作为过滤条件查询AccessLogged事件,就能拿到所有这个编号对应的历史写入记录。
补充说明你现有代码中arrayOfData的问题:目前你每次调用loggingAccess都会往这个数组里push一次_nik,所以你的数组现在已经存了两个10001,但这个数组只存了编号,没有对应存储当时写入的结构体数据,因此无法通过这个数组找回第一次的写入内容。
内容的提问来源于stack exchange,提问作者alvin Christianto
相关产品推荐
相关产品推荐

