5

Próbuję zrozumieć, dlaczego pojawia się błąd w przypadku niektórych wywołań GET, a nie innych z jednostką Google App Engine.Opis wyjątku JsonMappingException w Google Endpoint

Klasa

@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; 
    } 
} 

Works

A jeśli używam żądania list, to działa dobrze.

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

Rezultatem jest jak oczekiwano

{ 
"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", 
    } 
] 
} 

Nie działa

Ale jeśli mam tylko 1 rekord, takich jak select id 12, błąd jest produkowane

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

com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException: Po prostu próbujesz uzyskać dostęp do pola \ "ocena \", ale to pole nie zostało odłączone po odłączeniu obiektu. Albo nie wchodź w to pole , albo odłącz je podczas odłączania obiektu. (przez referencję: łańcuch: com.google.api.server.spi.response.CollectionResponse [\ "items \"] -> com.google.appengine.datanucleus.query.StreamingQueryResult [0] -> com.my.app .client.Client [\ "ocena \"])

Works jeśli usunąć get/zestaw metod

Gdzie jestem mylony jest, jeśli zakomentuj setAssessment(...) i getAssesment() metod wewnątrz Client obiekt, działa poprawnie.

@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; 
    //} 
} 

działa dobrze

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

Rezultat

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

Moje pytania są

  • Dlaczego to działa, kiedy zakomentuj get/set
  • Jak prawidłowo to naprawić?
  • Mam problemy ze znalezieniem dobrych przykładów kodu kodu aplikacji Java Engine, innych niż proste przykłady w dokumentach. Czy znasz jakieś dobre przykłady?

odpowiedni kod Endpoint

/** 
* 
* 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; 
} 
+0

Na powierzchni wygląda to na problem z leniwym ładowaniem, ale nie jestem pewien. Czy masz w zestawie 'assessment' wartość inną niż null? Nie pokazuje wartości w twoim przykładowym wywołaniu 'list', po prostu chcę się upewnić, że jest poprawna. –

+0

Jeszcze nie wprowadziłem niczego do "oceny". Nie wszystkie podmioty będą je mieć. Czy muszę po prostu zainicjować 'assessment' wewnątrz konstruktora? – Kirk

+0

Nie, właśnie sprawdzałem, że brak "oceny" w twoim wywołaniu 'list' nie był błędem. Będę musiał się trochę nad tym trochę zastanowić, co jest nie tak. –

Odpowiedz

4

I napotkał ten sam problem, i to z pewnością kwestia leniwy ładowania:

@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; 
} 

Niestety, nie mogłem znaleźć bardziej przejrzysty sposób aby to naprawić, więc daj mi znać, jeśli masz lepsze alternatywy! Ostatnia rzecz: chciałbym też wiedzieć, dlaczego zachowuje się inaczej, gdy Kirk komentuje pozyskiwania/ustawianie.

EDIT 26/12/2013:
Wydaje się sprowadzić typy są teraz prawidłowo pracować przy użyciu najnowszej wersji App Engine + Datanucleus. Ja również znalazłem bardzo ciekawy problem:

  • Jeśli używasz TypedQuery<T> zamiast Query, JPA spróbuje załadować wszystkich dziedzinach T (ignoruje FetchType.LAZY), a otrzymasz JsonMappingException jeśli pola te nie były ręcznie załadowany.
  • Jeśli użyjesz Query, JPA załaduje tylko pola oznaczone jako FetchType.EAGER, więc uważaj na wyjątki, jeśli zamierzasz używać pól, które zostały oznaczone jako leniwy, ale nie zostały poprawnie załadowane.
+1

Ponadto wydaje się, że FetchType.EAGER jest ignorowany, zgodnie z https://groups.google.com/forum/?fromgroups#!topic/google-appengine-java/Mk3-yaqnJxQ –

+0

Tak, dobry telefon. Wygląda na to, że moja rada dotycząca używania "EAGER" była nieprawidłowa. W przypadku metody 'list' brakuje niektórych wierszy we fragmencie powyżej' for (Lazy obj: execute) 'wykonuje szybkie ładowanie w metodzie listy. Będziesz musiał wykonać podpętlę 'for (Assessment assessment: obj.getAssessment()), aby wczytać tę właściwość. –

+0

Przyjrzę się temu później, gdy będę już z kodem. Dziękuję za pomoc. – Kirk