【问题标题】:Objectify - how to @Load a List<Ref<?>>?Objectify - 如何@Load a List<Ref<?>>?
【发布时间】:2012-10-31 08:13:04
【问题描述】:

我有一个实体,其中一个字段包含一个引用其他实体的列表(总是 4)。我正在尝试获取一些实体,并将它们分派给 jsp 进行显示。我希望该字段中的所有 Refs 也被加载,并在 jsp 中访问它们。

这是我的基本结构:

@Entity
public class Question {
    @Id Long id;
    @Index String question;
    @Load List<Ref<Answer>> answers = new ArrayList<Ref<Answer>>();
}

当我提取这样的问题时,显然 jsp 中有错误。有道理,因为 answers 字段不是答案列表,而是参考:

ObjectifyService.register(Question.class);
ObjectifyService.register(Answer.class);

List<Question> questions = ofy().load().type(Question.class).limit(50).list();

req.setAttribute("questions", questions);
try { 
    getServletContext().getRequestDispatcher("/admin/view-questions.jsp").forward(req, resp); 
} catch (ServletException e) {
    System.out.println (e.getMessage());
}

那么如何访问jsp中的答案呢?是手动循环问题并为答案字段执行 get() 的唯一方法吗?

【问题讨论】:

    标签: google-app-engine objectify


    【解决方案1】:

    这样的事情怎么样:

    @Entity
    public class Question {
        @Id Long id;
        @Index String question;
        @Load List<Ref<Answer>> answers = new ArrayList<Ref<Answer>>();
    
        public List<Answer> loadAnswers()
        {
            return new ArrayList<Answer>(ofy().load().refs(answers).values());
        }
    }
    

    由于您已经使用 @Load 注释加载了实体,我认为 objectify 只会从会话缓存中提取它们(它们不会被加载 2 次)。

    如果你没有使用@Load注解,那么这个方法将执行批量加载,这可能比循环遍历 Refs 并单独调用 get() 更好。

    【讨论】:

    • 这是截至 2015 年的最佳答案。
    • 这是一个坏主意的原因是函数 loadAnswers() 只能在后端代码中工作,尽管使用 appEngine 生成的客户端库也将包含 loadAnswers() 函数并且它根本不起作用在客户端中。
    • 我认为这取决于您的需求和您使用的框架,并不是每个人都会使用应用引擎生成客户端;原始海报只提到了 JSP 文件。基本概念应该仍然是合理的,您可能只需要稍微重构代码以满足您自己的需求。
    • 谢谢。接受的解决方案不起作用。
    • 这是截至 2019 年的最佳答案 ;)
    【解决方案2】:

    您可能会觉得这很方便:

    public class Deref {
        public static class Func<T> implements Function<Ref<T>, T> {
            public static Func<Object> INSTANCE = new Func<Object>();
    
            @Override
            public T apply(Ref<T> ref) {
                return deref(ref);
            }
        }
    
        public static <T> T deref(Ref<T> ref) {
            return ref == null ? null : ref.get();
        }
    
        @SuppressWarnings({ "unchecked", "rawtypes" })
        public static <T> List<T> deref(List<Ref<T>> reflist) {
            return Lists.transform(reflist, (Func)Func.INSTANCE);
        }
    }
    

    使用它,您可以提供如下方法:

    @Entity
    public class Question {
        @Id Long id;
        @Index String question;
        @Load List<Ref<Answer>> answers = new ArrayList<Ref<Answer>>();
        public List<Answer> getAnswers() { return Deref.deref(answers); }
    }
    

    需要注意的是,它是一个只读列表。通过一点创意,您可以创建一个可写列表,但它需要创建一个 ReversibleFunction 和一个新的 List.transform() 方法。如果您只想为 JSP 提供一些简单的东西,您可能不想担心它。

    或者,您可以在大多数表达式语言中使用 Ref 的“值”属性。

    【讨论】:

    • @stickfigure 添加Answer (myQuestion.getAnswers().add(newAnswer);) 我得到exception: java.lang.UnsupportedOperationException at java.util.AbstractList.add(Unknown Source)
    【解决方案3】:

    如果更改列表类型会发生什么?

    @Entity
    public class Question {
        @Id Long id;
        @Index String question;
        @Load List<Answer> answers = new ArrayList<Answer>();
    }
    

    我检查了一下,一切顺利,但在文档 (https://code.google.com/p/objectify-appengine/wiki/Entities#Ref_s) 中没有任何提及此案例。

    这样做安全吗?

    【讨论】:

    • 您以这种方式存储对象而不是引用。不要这样做。
    • 它将创建一个嵌入式实体。
    猜你喜欢
    • 1970-01-01
    • 2014-02-28
    • 2018-04-26
    • 1970-01-01
    • 1970-01-01
    • 2017-06-23
    • 2012-12-22
    • 1970-01-01
    • 2022-12-02
    相关资源
    最近更新 更多