如何在android中具有一对一或一对多关系的objectify实体中插入记录

Baq*_*qir 5 google-app-engine android objectify google-cloud-endpoints google-cloud-datastore

我有如下类 City 的模型:

@Entity
public class City {
    @Id
    Long id;
    String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
Run Code Online (Sandbox Code Playgroud)

我有另一个模型类 Person 下面给出:

@Entity
public class Person {
    @Id
    Long id;
    String name;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    @ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
    Key<City> city;
}
Run Code Online (Sandbox Code Playgroud)

之后,我使用 android studio 为这两个类生成端点并部署它。

这是生成的端点的代码:

个人端点

@Api(
        name = "personApi",
        version = "v1",
        resource = "person",
        namespace = @ApiNamespace(
                ownerDomain = "backend.faceattendence.morpho.com",
                ownerName = "backend.faceattendence.morpho.com",
                packagePath = ""
        )
)
public class PersonEndpoint {

    private static final Logger logger = Logger.getLogger(PersonEndpoint.class.getName());

    private static final int DEFAULT_LIST_LIMIT = 20;

    static {
        // Typically you would register this inside an OfyServive wrapper. See: https://code.google.com/p/objectify-appengine/wiki/BestPractices
        ObjectifyService.register(Person.class);
    }

    /**
     * Returns the {@link Person} with the corresponding ID.
     *
     * @param id the ID of the entity to be retrieved
     * @return the entity with the corresponding ID
     * @throws NotFoundException if there is no {@code Person} with the provided ID.
     */
    @ApiMethod(
            name = "get",
            path = "person/{id}",
            httpMethod = ApiMethod.HttpMethod.GET)
    public Person get(@Named("id") Long id) throws NotFoundException {
        logger.info("Getting Person with ID: " + id);
        Person person = ofy().load().type(Person.class).id(id).now();
        if (person == null) {
            throw new NotFoundException("Could not find Person with ID: " + id);
        }
        return person;
    }

    /**
     * Inserts a new {@code Person}.
     */
    @ApiMethod(
            name = "insert",
            path = "person",
            httpMethod = ApiMethod.HttpMethod.POST)
    public Person insert(Person person) {
        // Typically in a RESTful API a POST does not have a known ID (assuming the ID is used in the resource path).
        // You should validate that person.id has not been set. If the ID type is not supported by the
        // Objectify ID generator, e.g. long or String, then you should generate the unique ID yourself prior to saving.
        //
        // If your client provides the ID then you should probably use PUT instead.
        ofy().save().entity(person).now();
        logger.info("Created Person.");

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

    /**
     * Updates an existing {@code Person}.
     *
     * @param id     the ID of the entity to be updated
     * @param person the desired state of the entity
     * @return the updated version of the entity
     * @throws NotFoundException if the {@code id} does not correspond to an existing
     *                           {@code Person}
     */
    @ApiMethod(
            name = "update",
            path = "person/{id}",
            httpMethod = ApiMethod.HttpMethod.PUT)
    public Person update(@Named("id") Long id, Person person) throws NotFoundException {
        // TODO: You should validate your ID parameter against your resource's ID here.
        checkExists(id);
        ofy().save().entity(person).now();
        logger.info("Updated Person: " + person);
        return ofy().load().entity(person).now();
    }

    /**
     * Deletes the specified {@code Person}.
     *
     * @param id the ID of the entity to delete
     * @throws NotFoundException if the {@code id} does not correspond to an existing
     *                           {@code Person}
     */
    @ApiMethod(
            name = "remove",
            path = "person/{id}",
            httpMethod = ApiMethod.HttpMethod.DELETE)
    public void remove(@Named("id") Long id) throws NotFoundException {
        checkExists(id);
        ofy().delete().type(Person.class).id(id).now();
        logger.info("Deleted Person with ID: " + id);
    }

    /**
     * List all entities.
     *
     * @param cursor used for pagination to determine which page to return
     * @param limit  the maximum number of entries to return
     * @return a response that encapsulates the result list and the next page token/cursor
     */
    @ApiMethod(
            name = "list",
            path = "person",
            httpMethod = ApiMethod.HttpMethod.GET)
    public CollectionResponse<Person> list(@Nullable @Named("cursor") String cursor, @Nullable @Named("limit") Integer limit) {
        limit = limit == null ? DEFAULT_LIST_LIMIT : limit;
        Query<Person> query = ofy().load().type(Person.class).limit(limit);
        if (cursor != null) {
            query = query.startAt(Cursor.fromWebSafeString(cursor));
        }
        QueryResultIterator<Person> queryIterator = query.iterator();
        List<Person> personList = new ArrayList<Person>(limit);
        while (queryIterator.hasNext()) {
            personList.add(queryIterator.next());
        }
        return CollectionResponse.<Person>builder().setItems(personList).setNextPageToken(queryIterator.getCursor().toWebSafeString()).build();
    }

    private void checkExists(Long id) throws NotFoundException {
        try {
            ofy().load().type(Person.class).id(id).safe();
        } catch (com.googlecode.objectify.NotFoundException e) {
            throw new NotFoundException("Could not find Person with ID: " + id);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

城市端点

@Api(
        name = "cityApi",
        version = "v1",
        resource = "city",
        namespace = @ApiNamespace(
                ownerDomain = "backend.faceattendence.morpho.com",
                ownerName = "backend.faceattendence.morpho.com",
                packagePath = ""
        )
)
public class CityEndpoint {

    private static final Logger logger = Logger.getLogger(CityEndpoint.class.getName());

    private static final int DEFAULT_LIST_LIMIT = 20;

    static {
        // Typically you would register this inside an OfyServive wrapper. See: https://code.google.com/p/objectify-appengine/wiki/BestPractices
        ObjectifyService.register(City.class);
    }

    /**
     * Returns the {@link City} with the corresponding ID.
     *
     * @param id the ID of the entity to be retrieved
     * @return the entity with the corresponding ID
     * @throws NotFoundException if there is no {@code City} with the provided ID.
     */
    @ApiMethod(
            name = "get",
            path = "city/{id}",
            httpMethod = ApiMethod.HttpMethod.GET)
    public City get(@Named("id") Long id) throws NotFoundException {
        logger.info("Getting City with ID: " + id);
        City city = ofy().load().type(City.class).id(id).now();
        if (city == null) {
            throw new NotFoundException("Could not find City with ID: " + id);
        }
        return city;
    }

    /**
     * Inserts a new {@code City}.
     */
    @ApiMethod(
            name = "insert",
            path = "city",
            httpMethod = ApiMethod.HttpMethod.POST)
    public City insert(City city) {
        // Typically in a RESTful API a POST does not have a known ID (assuming the ID is used in the resource path).
        // You should validate that city.id has not been set. If the ID type is not supported by the
        // Objectify ID generator, e.g. long or String, then you should generate the unique ID yourself prior to saving.
        //
        // If your client provides the ID then you should probably use PUT instead.
        ofy().save().entity(city).now();
        logger.info("Created City.");

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

    /**
     * Updates an existing {@code City}.
     *
     * @param id   the ID of the entity to be updated
     * @param city the desired state of the entity
     * @return the updated version of the entity
     * @throws NotFoundException if the {@code id} does not correspond to an existing
     *                           {@code City}
     */
    @ApiMethod(
            name = "update",
            path = "city/{id}",
            httpMethod = ApiMethod.HttpMethod.PUT)
    public City update(@Named("id") Long id, City city) throws NotFoundException {
        // TODO: You should validate your ID parameter against your resource's ID here.
        checkExists(id);
        ofy().save().entity(city).now();
        logger.info("Updated City: " + city);
        return ofy().load().entity(city).now();
    }

    /**
     * Deletes the specified {@code City}.
     *
     * @param id the ID of the entity to delete
     * @throws NotFoundException if the {@code id} does not correspond to an existing
     *                           {@code City}
     */
    @ApiMethod(
            name = "remove",
            path = "city/{id}",
            httpMethod = ApiMethod.HttpMethod.DELETE)
    public void remove(@Named("id") Long id) throws NotFoundException {
        checkExists(id);
        ofy().delete().type(City.class).id(id).now();
        logger.info("Deleted City with ID: " + id);
    }

    /**
     * List all entities.
     *
     * @param cursor used for pagination to determine which page to return
     * @param limit  the maximum number of entries to return
     * @return a response that encapsulates the result list and the next page token/cursor
     */
    @ApiMethod(
            name = "list",
            path = "city",
            httpMethod = ApiMethod.HttpMethod.GET)
    public CollectionResponse<City> list(@Nullable @Named("cursor") String cursor, @Nullable @Named("limit") Integer limit) {
        limit = limit == null ? DEFAULT_LIST_LIMIT : limit;
        Query<City> query = ofy().load().type(City.class).limit(limit);
        if (cursor != null) {
            query = query.startAt(Cursor.fromWebSafeString(cursor));
        }
        QueryResultIterator<City> queryIterator = query.iterator();
        List<City> cityList = new ArrayList<City>(limit);
        while (queryIterator.hasNext()) {
            cityList.add(queryIterator.next());
        }
        return CollectionResponse.<City>builder().setItems(cityList).setNextPageToken(queryIterator.getCursor().toWebSafeString()).build();
    }

    private void checkExists(Long id) throws NotFoundException {
        try {
            ofy().load().type(City.class).id(id).safe();
        } catch (com.googlecode.objectify.NotFoundException e) {
            throw new NotFoundException("Could not find City with ID: " + id);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我想在 City 和 Person 之间建立关系,以便许多人可以属于一个城市。 问题:

  1. 对于这种关系,这是类的正确建模吗?如果不是,请告诉我一对一和一对多关系的正确模型

  2. 如何通过java代码(端点)和API资源管理器在数据存储中插入这种关系的记录?

  3. 是否需要使用@Parent 批注或@Index 批注?

  4. 建立这种关系后,如果我删除一个城市,那么所有属于该城市的人都必须自动删除。这种建模能够实现吗?请告诉我执行此操作的代码。如果不是,那么我如何才能实现这种使用关系?

Baq*_*qir 1

首先阅读下面链接中与 objectify groups 发生的讨论: how to insert record in objectify entity had one-to-one or one-to-many relation in android 因此,这是基于键设计 API 的更好方法(即网络安全密钥)不是基于 ID,因为 ID 在数据存储中不是唯一的。所以我将 Person 模型类更改为

@Entity
public class Person {
    @Id
    Long id;
    String name;
    @Parent
    @Index
    @ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
    Key<City> city;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getWebsafeKey() {
        return Key.create(city.getKey(), Person.class, id).getString();
    }

}
Run Code Online (Sandbox Code Playgroud)

getWebsafeKey() 将返回具有其父信息的 person 类的网络安全密钥。我想要这个网络安全密钥,以便我可以通过查询密钥直接检索存储的实体。

对于 City 模型类,由于它没有父 getWebsafeKey() 方法,因此如下所示:

public String getWebsafeKey() {
        return Key.create( City.class, id).getString();
    }
Run Code Online (Sandbox Code Playgroud)

问题2的答案: 插入城市将与问题中的相同,意味着城市端点的insert()方法没有变化。在城市中插入人物将如下所示:

@ApiMethod(
       name = "insert",
       path = "city/{city_web_safe_key}/person",
       httpMethod = ApiMethod.HttpMethod.POST)
       public Person insert(@Named("city_web_safe_key") String cityWebSafeKey,Person person) {
        Key<City> cityKey = Key.create(cityWebSafeKey);
                person.city = Ref.create(cityKey);
                    ofy().save().entity(person).now();
                    logger.info("Created Person.");
                    return ofy().load().entity(person).now();
                }
Run Code Online (Sandbox Code Playgroud)

同样,您还必须更改具有“id”参数的所有其他 @ApiMethod ,因为当您从模型类自动生成端点时,所有 @ApiMethod 将基于“id”,如问题所示。所以你必须用“网络安全密钥”替换“id”参数。同样的事情也适用于城市端点方法。为了更好地了解参数,您应该[单击此处][2]

例如,人员端点的 get 方法将如下所示:

@ApiMethod(
            name = "get",
            path = "person/{person_websafe_key}",
            httpMethod = ApiMethod.HttpMethod.GET)
    public Site get(@Named("person_websafe_key") String personWSKey) throws NotFoundException {
        logger.info("Getting person with WSkey: " + personWSKey);
        Person person = (Person) ofy().load().key(Key.create(personWSKey)).now();
        if (person == null) {
            throw new NotFoundException("Could not find person with WSKey: " + personWSKey);
        }
        return person;
    }
Run Code Online (Sandbox Code Playgroud)

问题 4 的答案: Objectify 不会强制开发人员进行此类删除。这完全取决于您有什么样的要求。这是城市的删除方法,它也会删除该城市中的人员。

@ApiMethod(
            name = "remove",
            path = "city/{city_web_safe_key}",
            httpMethod = ApiMethod.HttpMethod.DELETE)
    public void remove(@Named("city_web_safe_key") String cityWSKey) throws NotFoundException {
        Key<City> cityKey = Key.create(cityWSKey);
        checkExists(cityKey);
        //ofy().delete().key(cityKey).now();
        ofy().delete().keys(ofy().load().ancestor(cityKey).keys().list());

    }
Run Code Online (Sandbox Code Playgroud)

问题1和3的答案 已经在上面的答案中涵盖,详细知识请参阅文档