【问题标题】:Understanding a JsonMappingException with Google Endpoint使用 Google Endpoint 了解 JsonMappingException
【发布时间】:2013-06-07 03:43:41
【问题描述】:

我试图了解为什么我在某些 GET 调用中收到错误,而在其他 Google App Engine 实体中却没有。

@Entity
public class Client {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Key id;
    private String firstName;
    private String lastName;

    @Basic(fetch = FetchType.EAGER)
    @ElementCollection
    private List<Assessment> assessment;

    public List<Assessment> getAssessment() {
        return assessment;
    }

    public void setAssessment(List<Assessment> assessment) {
        this.assessment = assessment;
    }
}

作品

如果我使用list 请求,它工作正常。

GET http://localhost:8888/_ah/api/clientendpoint/v1/client

结果如预期

{
 "items": [
  {
   "id": {
    "kind": "Client",
    "appId": "no_app_id",
    "id": "12",
    "complete": true
   },
   "firstName": "Jane",
   "lastName": "Doe"
  },
  {
   "id": {
    "kind": "Client",
    "appId": "no_app_id",
    "id": "13",
    "complete": true
   },
   "firstName": "Jon",
   "lastName": "Doe",
  }
 ]
}

不起作用

但如果我只得到 1 条记录,例如选择 id 12,则会产生错误

GET http://localhost:8888/_ah/api/clientendpoint/v1/client/12

com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException: 您刚刚尝试访问字段“评估”但此字段 分离对象时未分离。要么不要访问这个 字段,或在分离对象时将其分离。 (通过参考 链: com.google.api.server.spi.response.CollectionResponse[\"items\"]->com.google.appengine.datanucleus.query.StreamingQueryResult[0]->com.my.app.client.Client[\"评估\"])

如果我删除 get/set 方法就可以使用

我感到困惑的是,如果我在 Client 对象中注释掉 setAssessment(...)getAssesment() 方法,它可以正常工作。

@Entity
public class Client {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Key id;
    private String firstName;
    private String lastName;

    @Basic(fetch = FetchType.EAGER)
    @ElementCollection
    private List<Assessment> assessment;

    //public List<Assessment> getAssessment() {
    //    return assessment;
    //}

    //public void setAssessment(List<Assessment> assessment) {
    //    this.assessment = assessment;
    //}
}

效果很好

GET http://localhost:8888/_ah/api/clientendpoint/v1/client/12

结果

{
 "id": {
  "kind": "Client",
  "appId": "no_app_id",
  "id": "12",
  "complete": true
 },
 "firstName": "Jane",
 "lastName": "Doe"
}

我的问题是

  • 为什么当我注释掉 get/set 时它会起作用
  • 如何正确解决此问题?
  • 除了文档中的简单示例之外,我无法为 Java App Engine 代码找到好的代码示例。有人知道什么好的例子吗?

相关端点代码

/**
 *
 * This method works fine
 *
 */
@SuppressWarnings({ "unchecked", "unused" })
@ApiMethod(name = "listClient")
public CollectionResponse<Client> listClient(
        @Nullable @Named("cursor") String cursorString,
        @Nullable @Named("limit") Integer limit) {

    EntityManager mgr = null;
    Cursor cursor = null;
    List<Client> execute = null;

    try {
        mgr = getEntityManager();
        Query query = mgr.createQuery("select from Client as Client");
        if (cursorString != null && cursorString != "") {
            cursor = Cursor.fromWebSafeString(cursorString);
            query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
        }

        if (limit != null) {
            query.setFirstResult(0);
            query.setMaxResults(limit);
        }

        execute = (List<Client>) query.getResultList();
        cursor = JPACursorHelper.getCursor(execute);
        if (cursor != null)
            cursorString = cursor.toWebSafeString();

    } finally {
        mgr.close();
    }

    return CollectionResponse.<Client> builder().setItems(execute)
            .setNextPageToken(cursorString).build();
}

/**
 *
 * This method errors out
 *
 */
@ApiMethod(name = "getClient")
public Client getClient(@Named("id") Long id) {
    EntityManager mgr = getEntityManager();
    Client client = null;
    client = mgr.find(Client.class, id);
    mgr.close();
    return client;
}

【问题讨论】:

  • 从表面上看,这似乎又是延迟加载的问题,但我不确定。您是否在您的任何实体中将assessment 设置为非空值?它没有在您的示例 list 调用中显示值,我只是想确保它是准确的。
  • 我还没有在assessment 中输入任何内容。并非所有实体都会拥有它们。我需要在构造函数内部初始化assessment 吗?
  • 不,我只是检查您的list 调用中缺少assessments 不是错误。我将不得不尝试一下以找出问题所在。

标签: java google-app-engine rest google-cloud-storage google-cloud-endpoints


【解决方案1】:

我也遇到了同样的问题,肯定是延迟加载的问题:

@ApiMethod(name = "getClient")
public Client getClient(@Named("id") Long id) {
    EntityManager mgr = getEntityManager();
    Client client = mgr.find(Client.class, id);
    if (client != null)
        client.getAssessment();  // Forces it to load your objects
    mgr.close();
    return client;
}

很遗憾,我找不到更简洁的方法来解决此问题,所以如果您有更好的选择,请告诉我! 最后一件事:我也很想知道为什么当 Kirk 取出 getter/setter 时它的行为会有所不同。

2013 年 12 月 26 日编辑:
使用最新版本的 App Engine + Datanucleus 似乎获取类型现在可以正常工作。我还发现了一个非常有趣的行为:

  • 如果您使用 TypedQuery&lt;T&gt; 而不是 Query,JPA 将尝试加载 T 的所有字段(它会忽略 FetchType.LAZY),如果未手动加载这些字段,您将收到 JsonMappingException。
  • 如果您使用 Query,JPA 将仅加载标记为 FetchType.EAGER 的字段,因此如果您打算使用标记为惰性但未正确加载的字段,请注意例外情况。

【讨论】:

  • 另外,根据groups.google.com/forum/?fromgroups#!topic/…,FetchType.EAGER 似乎被忽略了
  • 是的,好电话。看来我使用 EAGER 的建议无效。对于list 方法,上面的sn-p 中缺少几行for (Lazy obj : execute) 在list 方法中执行预加载。您需要执行一个子循环 for (Assessment assessment : obj.getAssessment()) 来急切地加载该属性。
  • 我将在今天晚些时候看一下这个,一旦我使用了代码。感谢您的帮助
  • 工作得很好,感谢您跟进我的最后一个问题@DanHolevoet。那行得通。不知道为什么注释掉这两种方法之前修复了它。
猜你喜欢
  • 1970-01-01
  • 2014-04-04
  • 2014-01-15
  • 1970-01-01
  • 2021-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-02
相关资源
最近更新 更多