Objectify加载Ref <! - ? - >后面的对象,即使未指定@Load也是如此

时间:2014-06-05 22:18:28

标签: java google-app-engine google-cloud-endpoints objectify

我有一个引用用户对象的帐户对象。

@Cache
@Entity
public final class Account {

    @Id Long id;
    @Index private Ref<User> user;

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }

    public User getUser() {
        return user.get();
    }
    public void setUser(User user) {
        this.user = Ref.create(user);
    }

}

我按照此处的建议隐藏了参考:http://code.google.com/p/objectify-appengine/wiki/Entities - 请注意参考是否有@Load注释。

当我从Android客户端调用我的Google Cloud Endpoint时,看起来Objectify会向嵌入式用户提供帐户对象,即使未指定@Load也是如此。

@ApiMethod(name = "account.get")
public Account getAccount(
        @Named("id") final Long id
) {
    return ofy().load().type(Account.class).id(id).now();
}

当我使用Apis Explorer直接查询帐户时,我同时获得了嵌入用户的帐户:

200 OK
{ 
"id": "5079604133888000", 
"user": {  "id": "5723348596162560",  
"version": "1402003195251",  
"firstName": "Karl" }, 
"kind": "api#accountItem", 
"etag": "\"30khohwUBSGWr00rYOZuF9f4BTE/Q31EvnQCQ6E9c5YXKEZHNsD_mlQ\""}

这提出了三个问题:

  1. Appengine是否总是原生地返回嵌入式Refs并且Objectify总是传递它已经知道的对象?
  2. @Load究竟是什么,有没有办法控制这种行为?加载组?
  3. 我错过了什么吗?为什么@Load没有服从?

1 个答案:

答案 0 :(得分:13)

在您的示例代码中,您没有指定@Load,这意味着加载帐户将无法获取User。但是,您的@ApiMethod正在将帐户序列化回客户端,因此已访问user属性,因此会发出单独的提取以加载用户对象。这就是您在调用方法时获取用户信息的原因。

不指定@Load并不意味着您无法获得User。这意味着除非您稍后特别要求,否则您不会检索User

Ref的工作原理如下:

  • 我是参考,所以默认情况下我不会获取数据。
  • 如果您要求我,那么我将首先加载数据,然后回答您。
  • 哦,如果你自己告诉我@Load,那么我最初会获取数据,并为你做好准备。

所以这在你的代码中工作得很好......但是你的@ApiMethod正在将你的Account对象序列化回客户端。序列化过程将遍历Account对象中的每个属性,包括user属性。此时,正在访问Ref<User>,因此数据将从数据存储区中获取,然后返回给客户端。

这使得您的代码效率非常低,因为Account对象在没有User信息的情况下加载,但是您以后总是访问User信息(在序列化期间),发出一个单独获取。从数据存储区域批处理gets比发布单独的gets更有效。

在您的情况下,您可以执行以下两种操作之一:

  1. @Load添加到用户属性,以便有效获取Account对象。
  2. @ApiMethod返回不具有Account属性的其他user对象(因此,如果您不需要,则可以避免抓取用户)。
  3. 上面的选项2非常有用,因为您可以从客户端看到的内容中抽象出内部数据存储区结构。你会发现自己经常使用这种模式。