Safe embedded entity with objectify

别说谁变了你拦得住时间么 提交于 2019-12-11 10:37:31

问题


I have two entities.

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

And the following Clound 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();
}

I'm wondering how do I skip the step where I have to save the emebedded entity first. The Id of neither entity is set. I want objectify to automatically create those. But if don't save the embedded entity I get an exception.

com.googlecode.objectify.SaveException: Error saving com.devmoon.meadule.backend.entities.Recipe@59e4ff19: You cannot create a Key for an object with a null @Id. Object was com.devmoon.meadule.backend.entities.Step@589a3afb

Since my object structure will get a lot more complex, I need to find a way to skip this manual step.


回答1:


I presume you are trying to create real embedded objects, not separate objects stored in the datastore and linked. Your extra save() is actually saving separate entities. You don't want that.

You have two options:

  1. Don't give your embedded object an id. Don't give it @Entity and don't give it an id field (or at least eliminate @Id). It's just a POJO. 90% of the time, this is what people want with embedded objects.
  2. Allocate the id yourself with the allocator, typically in your (non-default) constructor.

Assuming you want a true embedded entity with a real key, #2 is probably what you should use. Keep in mind that this key is somewhat whimsical since you can't actually load it; only the container object can be looked up in the datastore.

I suggest going one step further and never use automatic id generation for any entities ever. Always use the allocator in the (non-default) constructor of your entities. This ensures that entities always have a valid, stable id. If you always allocate the id before a transaction start, it fixes duplicate entities that can be created when a transaction gets retried. Populating null ids is just a bad idea all around and really should not have been added to GAE.




回答2:


The concept of the embedded is that the embedded content is persisted inside the main entity.

Is this the behaviour you are trying to configure?

The default behaviour of a Collection (List) of @Entity annoted class is to refer them instead of embed them. As you current configuration, the List<Step> variable does not have any annotation to override the default configuration, which is a different entity related to another one.

The error you are getting is because Objectify, when it saves the recipe entity, is trying to get the key of each step to create the relationship (and save them in the recipe entity), but if the entity step is not saved yet on the datastore, does not have a key

If you are trying to persist the steps inside the recipe entity, you need to setup objectify like this

@Entity
public class Recipe {
    @Id
    private Long id;

    private List<Step> steps;
}

public class Step {
    private Long id;
    private String instruction;
}

As you can see, I removed the @Id annotation (an embedded Entity does not require an ID because is inside another entity) and the @Entity from the Step class. With this configuration, Objectify save the step entities inside the recipe entity

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



来源:https://stackoverflow.com/questions/29475637/safe-embedded-entity-with-objectify

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!