внешние ключи в объектном / google app engine

Я пытаюсь написать приложение для механизма приложений Google, используя Objectify, и у меня возникли проблемы. Я новичок в хранилищах данных noSQL, поэтому, вероятно, это концептуальная проблема.

У меня есть сущность, называемая сообщением, и у каждого сообщения есть от пользователя — пользователь, создавший сообщение.

@Entity
public class Message {


@Index private Key<User> fromUserKey;
@IgnoreSave private  User fromUser;

Досадно, что мне нужно иметь в сообщении и поле пользователя, и поле ключа. JSON нуждается в объекте пользователя, чтобы заполнить ответ полезными полями, а механизму приложения Google нужен ключ, чтобы иметь возможность хранить ссылку на пользователя. Аннотация @IgnoreSave используется, чтобы запретить Objectify запрашивать у движка приложения Google попытку сохранить объект пользователя (что не удастся).

При получении сообщений ключ от пользователя заполняется, а объект от пользователя — нет. Вот как выглядит код DAO для моей операции «getMessages»:

    public static List<Message> getSentMessages(long userId) {
    List<Message> result;
    result= ofy().load().type(Message.class).filter("from", Key.create(User.class, userId)).limit(1000).list();
    return result;
}

Но объект fromUser не заполняется в каждом сообщении, а только fromUserKey. Как мне получить фактический объект пользователя, заполненный в ответе? Мне нужен доступ к таким полям, как имя, фамилия и т. д., а не только к идентификатору пользователя. Я мог бы получить от пользователя из БД, прокрутить результаты, а затем вызвать setFromUSer для каждого сообщения, но это уродливо. И это также вызывает ConcurrentModificationException...

Я уверен, что я делаю что-то неправильно здесь, но я не могу понять, что.


person mdarwin    schedule 04.05.2014    source источник


Ответы (1)


Что вы можете сделать, так это иметь свойство Ref<User> в объекте Message и аннотировать его с помощью «@Parent». Это означает, что каждый объект Message станет частью группы объектов пользователя. Ref<?> работает как ключ, но позволяет вам напрямую обращаться к фактическому объекту сущности; таким образом, вы можете легко получить имя, фамилию и т. д.

Измените свой класс следующим образом:

@Entity
@Cache
public class Message
{
    @Id Long id;
    @Load @Parent private Ref<User> user;

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

Сделав это, вы сможете выполнять запросы предков для получения всех объектов сообщений, связанных с конкретным пользователем:

public static List<Message> getSentMessages(long userId) 
{
    User parent = ofy().load().type(User.class).id(userId).now();
    List<Message> results = ofy().load().type(Message.class).ancestor(parent).limit(1000).list();   
    return results;
}

Теперь вы сможете делать то, что хотели, и, надеюсь, больше не получите ошибок.

person elcid    schedule 04.05.2014
comment
К этому замечательному ответу можно добавить только одну вещь: вы можете добавить @Load в поле Ref‹User›, чтобы, если вы загрузите сразу целую цепочку сообщений, вы эффективно загрузили всех пользователей. Для OP следует помнить, что сериализация json может управляться методами свойства bean-компонента; вам не нужны настоящие поля. - person stickfigure; 05.05.2014
comment
@stickfigure спасибо за комплимент, вы абсолютно правы. Я добавил аннотацию @Load к Ref<User>, как вы рекомендовали; не уверен, как я это забыл :). Без него каждый загружаемый пользователь был бы отдельным запросом, то есть objectify немедленно извлекала бы пользователя из хранилища данных каждый раз, когда выполнялось this.user.get(); что было бы хитом производительности, особенно при повторении большого количества сообщений. - person elcid; 05.05.2014
comment
@mdarwin тебе помог ответ? Если это так, пожалуйста, примите это. Спасибо - person elcid; 06.05.2014
comment
да, спасибо, у меня все работает так, как вы описали, за исключением того, что я использовал \@Index, а не \@Parent. Учитывая, что отправитель сообщения никогда не изменится, я, вероятно, могу вместо этого использовать \@Parent - при условии, что я могу искать сообщения по родителю. - person mdarwin; 08.05.2014