?
存证溯源案例2.0概览
本文分享如何使用
1、K-V数据库的ID一致性
Hyperledger fabric默认采用LevelDB 作为peer 节点的状态数据库。因此,我们在链码中进行数据存储时,基于K-V数据库来进行设计。
在本存证溯源案例中,我们采用LevelDB数据库来作为存储溯源记录的组件,但作为键值数据库,存储的Key多为字符串,因此就产生了如何实现ID主键自增的问题。
我们设计了一个全局自增变量
type AssetGlobal struct { IdNum int `json:"idNum"` }
对初始化方法进行相应调整
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error { key := "assetGlobal" exist, err := ctx.GetStub().GetState(key) if err != nil { return fmt.Errorf("can not find assetGlobal record, error :%v", err) } if exist != nil { return fmt.Errorf("assetGlobal exist, can not initLedger again.") } assetGlobalInstance := &AssetGlobal{ IdNum: 0, } assetGlobalInstanceBytes, err := json.Marshal(assetGlobalInstance) if err != nil { return fmt.Errorf("json assetGlobalInstance error :%v", err) } err = ctx.GetStub().PutState(assetGlobalName, assetGlobalInstanceBytes) if err != nil { return fmt.Errorf("PutState assetGlobalInstance error: %v", err) } return nil }
相应的增删改方法也做出调整
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, color string, size int, owner string, appraisedValue int) error { assetGlobalInstance, err := s.ReadAssetGlobal(ctx) if err != nil { return fmt.Errorf("get assetGlobalInstance error: %s", err) } var nextAssetID = assetGlobalInstance.IdNum + 1 assetGlobalNewInstance := &AssetGlobal{ IdNum: nextAssetID, } // id补零 nextAssetIDStr := transIDToStr(nextAssetID) exists, err := s.AssetExists(ctx, nextAssetIDStr) if err != nil { return fmt.Errorf("failed to get asset: %v", err) } if exists { return fmt.Errorf("asset already exists: %s", nextAssetIDStr) } asset := &Asset{ DocType: "asset", ID: nextAssetIDStr, Color: color, Size: size, Owner: owner, AppraisedValue: appraisedValue, } assetBytes, err := json.Marshal(asset) if err != nil { return err } err = ctx.GetStub().PutState(nextAssetIDStr, assetBytes) if err != nil { return err } return nil } func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, assetID int, color string, size int, owner string, appraisedValue int) error { exists, err := s.AssetExists(ctx, transIDToStr(assetID)) if err != nil { return fmt.Errorf("failed to get asset: %v", err) } if !exists { return fmt.Errorf("asset not exists: %d", assetID) } asset, err := s.ReadAsset(ctx, assetID) if err != nil { return err asset.Color = color asset.Size = size asset.Owner = owner asset.AppraisedValue = appraisedValue assetJSON, err := json.Marshal(asset) if err != nil { return fmt.Errorf("asset json marshal error:%s", err) } return ctx.GetStub().PutState(transIDToStr(assetID), assetJSON) } func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, assetID int) error { asset, err := s.ReadAsset(ctx, assetID) if err != nil { return fmt.Errorf("failed to get asset: %v", err) } if asset != nil { return fmt.Errorf("asset not exists: %d", assetID) } err = ctx.GetStub().DelState(transIDToStr(assetID)) if err != nil { return fmt.Errorf("failed to delete asset %d: %v", assetID, err) } }
2、范围查询的使用方法
增加了范围查询
func (s *SmartContract) GetAssetsByRangeLatest(ctx contractapi.TransactionContextInterface, pageSize, pageIndex int) ([]*Asset, error) { assetGlobalInstance, err := s.ReadAssetGlobal(ctx) if err != nil { return nil, fmt.Errorf("get assetGlobalInstance error: %s", err) } latestKey := assetGlobalInstance.IdNum var endKey int = latestKey - (pageIndex-1)*pageSize + 1 var startKey int = endKey - pageSize startKeyStr := transIDToStr(startKey) endKeyStr := transIDToStr(endKey) resultsIterator, _, err := ctx.GetStub().GetStateByRangeWithPagination(startKeyStr, endKeyStr, int32(pageSize), "") if err != nil { return nil, err } defer resultsIterator.Close() return constructQueryResponseFromIterator(resultsIterator) }
3、K-V数据库范围查询的弊端与解决方案
在实际调用主键ID作为Key来范围查询时,我们发现当,id作为自增的
这与我们设想的ID自增排序不同,因此我们需对原有ID进行补零操作
func transIDToStr(assetID int) string { assetIDStr := strconv.Itoa(assetID) if len(assetIDStr) < idStringLong { size := idStringLong - len(assetIDStr) for i := 0; i < size; i++ { assetIDStr = "0" + assetIDStr } } return assetIDStr }
?