【问题标题】:datastore: invalid entity type on Put数据存储区:Put 上的实体类型无效
【发布时间】:2026-02-17 05:55:01
【问题描述】:

我正在尝试为我的 Kinds 创建一个包装器,这就是我的做法:

package model

import (
    "time"
)

type Kind interface {
    Name() string
}

type Message struct {
    Text      string
    CreatedOn time.Time
    UserId    string
}

func (q Message) Name() string {
    return "MESSAGE"
}

而我介绍type Kind interface的原因是:

// Stores the given model for the the kind in data store
func Store(req *http.Request, data Kind) error {
    ctx := appengine.NewContext(req)
    key := datastore.NewKey(ctx, data.Name(), "", 0, nil)
    _, err := datastore.Put(ctx, key, &data)
    return err
}

如您所见,我使用data.Name() 来获取种类名称。

当我尝试保存数据时,它抱怨:

datastore: invalid entity type

我读到这可能是由于没有传递对datastore.Put 的引用,但我正在这样做。有什么想法吗?

我必须补充一点,当我检查数据类型时(使用reflect.TypeOf()),它也是model.Message,这也是正确的。所以它是一个具体的类型。

【问题讨论】:

    标签: google-app-engine go google-cloud-datastore


    【解决方案1】:

    datastore.Put() 期望实体数据为结构指针或实现PropertyLoadSaver 的任何值。这在其文档中明确说明:

    src必须是结构体指针或实现PropertyLoadSaver

    你传递给datastore.Put()的是一个指针值,一个指向接口的指针。接口中存储的值确实是具体类型model.Message,但它们并不相同。

    您不能使用reflect.TypeOf().String(),因为在接口的情况下,它会告诉您存储在接口中的具体类型(因此可能会产生误导)。

    查看这段代码来演示区别:

    var data Kind = Message{}
    fmt.Println(reflect.TypeOf(&data).Kind())
    fmt.Println(reflect.TypeOf(&data).Elem().Kind())
    
    var msg Message = Message{}
    fmt.Println(reflect.TypeOf(&msg).Kind())
    fmt.Println(reflect.TypeOf(&msg).Elem().Kind())
    

    输出(在Go Playground上试试):

    ptr
    interface
    ptr
    struct
    

    总而言之,&data 是一个指向接口的指针,不允许传递给datastore.Put()。你只能传递*Message,或者如果你想传递一个接口值(不是一个指向接口的指针),那么一定要实现PropertyLoadSaver

    【讨论】:

    • 感谢您对我对具体类型的困惑做出了很好的解释。我试图了解在这种情况下实施PropertyLoadSaver (cloud.google.com/appengine/docs/go/datastore/…) 可以如何帮助我。我想在这里实现的是创建一个可以存储任何种类的通用函数,它可以使用附加到种类本身的元数据来获取种类的名称。我试图通过引入Name() 来实现这一目标。你有什么解决办法吗?所以调用者不需要一直指定种类名称。
    • @MaxZooZoo Name() 是一个过于通用的方法名称,我会选择更具体的名称(例如EntityName())。但我不会走这条路,实体名称是一个元信息,而不是具体实现应该决定的东西。最好向结构添加一个新字段,使用struct tags 将其排除,但标签值可以保存实体名称的信息。或者更好的选择是建立一个名称注册表,一个从 Go 类型到实体名称的 map[Type]string 映射。
    • 这正是我的观点。我喜欢标记方法,但我想知道是否有办法标记结构本身,因为元数据将链接到整个结构而不是结构中的特定字段。如果您熟悉 Java 中的注解,那将是一个类注解。我们在 Go 中有这样的东西吗?
    • @MaxZooZoo 不,只有字段可以“标记”。要“标记”类型,请使用上述注册表:map[Type]string