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

如何通过Objectify在Datastore中保存递归对象

Got it, let's figure out how to save your recursive Item/Option structure using Objectify on Google Cloud Datastore. There are two solid approaches here, depending on your app's access patterns—let's walk through both.

方案1:嵌入嵌套Option(Embedded Entities)

This is perfect if your Options are tightly coupled to their parent Item (or parent Option for deeper recursion) and you rarely need to query or update them independently. Embedding stores the nested objects directly inside the parent entity, which makes reads/writes faster for the entire tree.

修改实体类

First, add Objectify's @Embed annotation to the Option class, and mark the options field in both Item and Option with @Embed (since you mentioned recursion, I’ll assume Option might also have child Options—your original code cut off, but I’ll account for that):

import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;

@Embed
public class Option {
    @Id private Long id;
    @Index private String name;
    @Index private String sku;
    @Index private Long shopId;
    @Index private String imageUrl = "";
    @Index private List<Long> optionIds;
    // Add recursive nested options
    @Embed private List<Option> options;

    // Default constructor required for Objectify
    public Option() {}

    // Getters/setters here
}

import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;

@Entity
public class Item {
    @Id private Long id;
    @Index private String name;
    @Index private String sku;
    @Index private Long shopId;
    @Index private String imageUrl = "";
    @Index private List<Long> optionIds;
    @Embed private List<Option> options;

    // Default constructor required for Objectify
    public Item() {}

    // Getters/setters here
}

保存示例

Saving the entire tree is straightforward—just persist the parent Item, and Objectify will handle embedding all nested Options automatically:

import com.googlecode.objectify.ObjectifyService;

// Initialize Objectify (do this once at app startup)
static {
    ObjectifyService.register(Item.class);
    // No need to register Option since it's @Embed
}

// Save an Item with nested Options
public void saveItemWithOptions() {
    Option childOption = new Option();
    childOption.setName("Color: Red");
    childOption.setSku("OPT-RED");

    Option parentOption = new Option();
    parentOption.setName("Color");
    parentOption.setSku("OPT-COLOR");
    parentOption.setOptions(List.of(childOption));

    Item item = new Item();
    item.setName("T-Shirt");
    item.setSku("ITEM-T-SHIRT");
    item.setShopId(123L);
    item.setOptions(List.of(parentOption));

    ObjectifyService.ofy().save().entity(item).now();
}
方案2:使用实体引用(Parent-Child Relationships)

Use this if you need to query, update, or delete Options independently of their parent Item (or parent Option). This approach creates separate entities linked by parent keys, which is better for large recursive trees or when you need granular access.

修改实体类

We'll use Objectify's @Parent annotation to link Options to their parent (either an Item or another Option). Also, note that Option needs to be an @Entity now:

import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;
import com.googlecode.objectify.Key;

@Entity
public class Option {
    @Parent private Key<?> parent; // Can be Key<Item> or Key<Option>
    @Id private Long id;
    @Index private String name;
    @Index private String sku;
    @Index private Long shopId;
    @Index private String imageUrl = "";
    @Index private List<Long> optionIds;

    // Default constructor required for Objectify
    public Option() {}

    // Constructor to set parent
    public Option(Key<?> parent) {
        this.parent = parent;
    }

    // Getters/setters here
}

import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;

@Entity
public class Item {
    @Id private Long id;
    @Index private String name;
    @Index private String sku;
    @Index private Long shopId;
    @Index private String imageUrl = "";
    @Index private List<Long> optionIds;

    // Default constructor required for Objectify
    public Item() {}

    // Getters/setters here
}

保存示例

You'll need to save the parent entity first, then create child Options with the parent's key, and save them individually. For recursive Options, just set the parent to the key of the parent Option:

import com.googlecode.objectify.ObjectifyService;

// Initialize Objectify (do this once at app startup)
static {
    ObjectifyService.register(Item.class);
    ObjectifyService.register(Option.class);
}

// Save an Item with linked Options (including recursive child Options)
public void saveItemWithLinkedOptions() {
    // Save the parent Item first
    Item item = new Item();
    item.setName("T-Shirt");
    item.setSku("ITEM-T-SHIRT");
    item.setShopId(123L);
    Key<Item> itemKey = ObjectifyService.ofy().save().entity(item).now();

    // Save a top-level Option linked to the Item
    Option parentOption = new Option(itemKey);
    parentOption.setName("Color");
    parentOption.setSku("OPT-COLOR");
    Key<Option> parentOptionKey = ObjectifyService.ofy().save().entity(parentOption).now();

    // Save a child Option linked to the parent Option (recursion)
    Option childOption = new Option(parentOptionKey);
    childOption.setName("Color: Red");
    childOption.setSku("OPT-RED");
    ObjectifyService.ofy().save().entity(childOption).now();
}

加载递归实体

To load the entire tree, you can use @Load with a Ref or manually fetch child entities. For example, to load an Item and all its linked Options:

// Load an Item and fetch its top-level Options
public Item loadItemWithOptions(Long itemId) {
    Item item = ObjectifyService.ofy().load().type(Item.class).id(itemId).now();
    // Fetch all Options where parent is the Item's key
    List<Option> options = ObjectifyService.ofy().load().type(Option.class)
        .ancestor(item.getKey())
        .list();
    // If you need recursive child Options, you'll need to fetch them recursively
    // (write a helper method to traverse the tree)
    return item;
}
Key Considerations
  • Entity Size Limits: Embedded entities count towards the parent entity's 1MB size limit. If your recursive tree is large, use the parent-child relationship approach instead.
  • Indexing: Be careful with indexed fields in embedded entities—too many indexed fields can increase write latency and cost. Use @Unindex for fields you don't need to query on.
  • Recursion Depth: Datastore doesn't limit recursion depth for parent-child relationships, but embedded recursion can hit the 1MB size limit quickly.

内容的提问来源于stack exchange,提问作者Anmol Vijaywargiya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:50:39