Gorm存储*hedera.ContractID报错:需定义外键或实现Valuer接口
解决Gorm存储Hedera ContractID的错误问题
错误原因
Gorm无法直接处理*hedera.ContractID类型,因为该类型(以及其内部嵌套的AccountID中的AliasKey字段)没有实现Gorm要求的Valuer和Scanner接口——这两个接口负责将自定义类型转换为数据库可存储的格式,以及从数据库读取数据转换回自定义类型。
解决方案
方案1:转成字符串存储(最简单直接)
直接将模型中的ContractId字段改为字符串类型,存储时调用ContractID的String()方法转为字符串,读取时再解析回原类型。
修改模型
package models import ( "time" "gorm.io/gorm" ) type Contract struct { gorm.Model Id string ContractId string // 改为string类型 GasUsed uint64 TransactionId string Timestamp time.Time ChargeFee string PayerAccount string Status string }
修改存入逻辑
// Create db.Create(&model.Contract{ Id: id.String(), ContractId: contract.Receipt.ContractID.String(), // 转成字符串存储 GasUsed: contract.CallResult.GasUsed, TransactionId: fmt.Sprint(contract.TransactionID), Timestamp: contract.ConsensusTimestamp, ChargeFee: fmt.Sprint(contract.TransactionFee), PayerAccount: fmt.Sprint(contract.TransactionID.AccountID), Status: fmt.Sprint(contract.Receipt.Status), })
读取时还原(如果需要)
var contract model.Contract db.First(&contract) // 将字符串解析回*hedera.ContractID contractID, err := hedera.ContractIDFromString(contract.ContractId) if err != nil { // 处理解析错误 }
方案2:给自定义类型实现Valuer/Scanner接口(更优雅,保留类型语义)
通过包装hedera.ContractID类型,实现Gorm所需的接口,让Gorm直接支持该类型的存储和读取。
定义包装类型并实现接口
package models import ( "database/sql/driver" "errors" "time" "github.com/hashgraph/hedera-sdk-go/v2" "gorm.io/gorm" ) // 包装hedera.ContractID类型 type HederaContractID hedera.ContractID // 实现Valuer接口:将类型转为数据库可存储的字符串 func (c HederaContractID) Value() (driver.Value, error) { return hedera.ContractID(c).String(), nil } // 实现Scanner接口:从数据库读取字符串并转为HederaContractID func (c *HederaContractID) Scan(value interface{}) error { strVal, ok := value.(string) if !ok { return errors.New("无法将非字符串类型转换为HederaContractID") } contractID, err := hedera.ContractIDFromString(strVal) if err != nil { return err } *c = HederaContractID(*contractID) return nil } // 修改模型字段类型为自定义包装类型的指针 type Contract struct { gorm.Model Id string ContractId *HederaContractID // 使用自定义包装类型 GasUsed uint64 TransactionId string Timestamp time.Time ChargeFee string PayerAccount string Status string }
修改存入逻辑
将*hedera.ContractID类型转换为自定义的*HederaContractID类型:
// Create db.Create(&model.Contract{ Id: id.String(), ContractId: (*model.HederaContractID)(contract.Receipt.ContractID), // 类型转换 GasUsed: contract.CallResult.GasUsed, TransactionId: fmt.Sprint(contract.TransactionID), Timestamp: contract.ConsensusTimestamp, ChargeFee: fmt.Sprint(contract.TransactionFee), PayerAccount: fmt.Sprint(contract.TransactionID.AccountID), Status: fmt.Sprint(contract.Receipt.Status), })
这样Gorm就能自动处理ContractId字段的存储和读取,无需手动转换字符串。
内容的提问来源于stack exchange,提问作者mev
相关产品推荐
相关产品推荐

