【问题标题】:Safe embedded entity with objectify带有 objectify 的安全嵌入式实体
【发布时间】:2015-04-06 16:50:49
【问题描述】:

我有两个实体。

@Entity
public class Recipe {
    @Id
    private Long id;
    private List<Step> steps;
}
@Entity
public class Step {
    @Id
    private Long id;
    private String instruction;
}

以及以下 Cloud Endpoint

@ApiMethod(
        name = "insert",
        path = "recipe",
        httpMethod = ApiMethod.HttpMethod.POST)
public Recipe insert(Recipe recipe) {
    ofy().save().entities(recipe.getSteps()).now();  //superfluous?
    ofy().save().entity(recipe).now();

    logger.info("Created Recipe with ID: " + recipe.getId());

    return ofy().load().entity(recipe).now();
}

我想知道如何跳过必须首先保存嵌入实体的步骤。两个实体的Id 均未设置。我希望 objectify 自动创建这些。但是如果不保存嵌入的实体,我会得到一个异常。

com.googlecode.objectify.SaveException:保存 com.devmoon.meadule.backend.entities.Recipe@59e4ff19 时出错:您无法为 @Id 为空的对象创建密钥。对象是 com.devmoon.meadule.backend.entities.Step@589a3afb

由于我的对象结构会变得更加复杂,我需要找到一种方法来跳过这个手动步骤。

【问题讨论】:

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


    【解决方案1】:

    我认为您正在尝试创建真正的嵌入式对象,而不是存储在数据存储中并链接的单独对象。您额外的 save() 实际上是在保存单独的实体。你不想这样。

    你有两个选择:

    1. 不要给你的嵌入对象一个ID。不要给它@Entity,也不要给它一个id字段(或至少消除@Id)。这只是一个 POJO。 90% 的情况下,这是人们对嵌入式对象的期望。
    2. 使用分配器自己分配 id,通常在您的(非默认)构造函数中。

    假设您想要一个真正的嵌入式实体和一个真正的键,#2 可能是您应该使用的。请记住,此键有些异想天开,因为您实际上无法加载它;只能在数据存储中查找容器对象。

    我建议更进一步,永远不要对任何实体使用自动 id 生成。始终在实体的(非默认)构造函数中使用分配器。这可确保实体始终具有有效、稳定的 ID。如果您总是在事务开始之前分配 id,它会修复可以在重试事务时创建的重复实体。填充 null id 只是一个坏主意,真的不应该添加到 GAE 中。

    【讨论】:

    • “始终在实体的(非默认)构造函数中使用分配器”是什么意思?哪个分配器?有这方面的文档吗?
    • “我建议更进一步,永远不要为任何实体使用自动 id 生成”直到现在我认为,如果我的实体包含 Long id,那么 objectify 会像这样来生成 id。是这种情况吗?如果我有一个 Long id 字段,GAE 本身是否会自动生成 ID?
    • 是的,GAE 将使用与allocateId() 相同的逻辑来生成 id。但使用这种机制是一种不好的做法,因为这意味着您的应用程序中有具有空 id 的实体。它往往会搞砸诸如双向关系之类的事情。
    【解决方案2】:

    嵌入的概念是嵌入的内容被持久化主体实体中。

    这是您尝试配置的行为吗?

    @Entity 注释类的集合(列表)的默认行为是引用它们而不是嵌入它们。在您当前的配置中,List&lt;Step&gt; 变量没有任何注释来覆盖默认配置,这是与另一个相关的不同实体。

    您得到的错误是因为 Objectify 在保存 recipe 实体时,正试图获取每个 step 的键来创建关系(并保存它们在 recipe 实体中),但如果实体 step 尚未保存在数据存储中,则没有密钥

    如果您尝试在 recipe 实体中保留 steps,则需要像这样设置 objectify

    @Entity
    public class Recipe {
        @Id
        private Long id;
    
        private List<Step> steps;
    }
    
    public class Step {
        private Long id;
        private String instruction;
    }
    

    如您所见,我从 Step 类中删除了 @Id 注释(嵌入式实体不需要 ID,因为它位于另一个实体中)和 @Entity。使用此配置,Objectify 将步骤实体保存在 inside 配方实体

    来源:https://code.google.com/p/objectify-appengine/wiki/Entities#Embedded_Object_Native_Representation

    【讨论】:

    • 注意,这是针对 Objectify 的一个非常老的版本。
    • Objectify当前版本没有@Embedded注解
    • 糟糕,我忘记在上次编辑时删除它。谢谢
    猜你喜欢
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-09
    相关资源
    最近更新 更多