You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 04:30:51