външни ключове в objectify / google app engine

Опитвам се да напиша приложение за google app engine с помощта на 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. Как да получа действителния потребителски обект, попълнен в отговора? Имам нужда от достъп до такива полета като име, фамилия и т.н. - не само ID на потребителя. Мога да получа from User от DB, да прегледам резултатите, след това да извикам 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