Samstag, 8. März 2014

Google Cloud Endpoints - Verstärkung für Android Apps (Teil 3)

In den beiden ersten Beiträgen dieser Reihe haben wir eine App entwickelt, die einen einfachen Service nutzt. Dieser Service wird noch nicht in der Google Cloud, sondern auf unserem Entwickler-Host betrieben. In diesem Beitrag sorgen wir erst einmal dafür, dass die Objekte, die unser Dienst zu Verfügung stellt, in einer Datenbank verwaltet werden.



JDO

Java Data Objects (JDO)  ist ein Standard, in dem definiert ist, wie Objekte persistiert  - also dauerhaft gespeichert - werden können. Ob das genutzte Datenbanksystem relational oder nicht-relational ist, ist dabei transparent. Beides ist möglich. Die Google App Engine nutzt JDO zur Verwaltung von Daten. Das macht es uns besonders leicht: 

Wir müssen keine SQL-Anweisungen in unseren Code integrieren, sondern konfigurieren unsere Klasse Band mit Hilfe von Java-Annotationen. Entsprechend dieser Konfiguration werden die Daten dann mit Hilfe von JDO persistiert. Der Artikel von Romin Irani war im Folgenden hilfreich.


Änderungen an der Bean-Klasse

Wir passen unsere Klasse Band geringfügig an:

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Band {
  @PrimaryKey
  @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  private Long id;
  private String name;
  private int founded;

  public Band(String name, int founded) {
    super();
    this.name = name;
    this.founded = founded;
  }
  
  public Band() {
    super();
  }

  public int getFounded() {
    return founded;
  }

  public String getName() {
    return name;
  }

  public void setFounded(int founded) {
    this.founded = founded;
  }

  public void setName(String name) {
    this.name = name;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public Long getId() {
    return id;
  }
}


Wenn wir unsere Daten mit JDO persistieren wollen, müssen wir die Klassen, deren Daten abgespeichert werden sollen mit der PersistenceCapable-Annotation versehen. Um die Datensätze unterscheiden zu können, wird ein Primärschlüssel verwendet, den wir mit PrimaryKey auszeichnen. Das zugehörige Attribut id vom Typ Long haben wir in der Klasse Band bisher nicht verwendet. Da wir die Erzeugung der Schlüssel dem System überlassen wollen, fügen wir noch die Annotation 
  @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) 
hinzu.


Der Neustart

Das war es fast schon. Die Services am Backend, die zur Klasse Band gehören,  implementieren wir dieses Mal nicht selbst, sondern lassen sie erzeugen: Rechter Mausklick auf den Klassennamen Band, 'Google' und 'Generate Cloud Endpoint Class' wählen und prompt wird die Service-Klasse BandEndpoint erzeugt. Wenn wir den Server neu starten, können wir diese neuen Services gleich testen.

Im Service-Explorer
  http://localhost:8888/_ah/api/explorer
wählen wir die ‚bandendpoint API’ und dort etwa den Service bandendpoint.insertBand. Im Feld 'Request body' tragen wir die Daten eines neuen Datensatzes wie in der Abbildung ein:

  

  

Für das Attribut id setzen wir keinen Wert ein, darum soll sich das System kümmern. Wenn wir den Service ausführen kommt es zu einem Fehlermeldung. Der Grund liegt in einem Fehler im generierten Code der Methode insertBand der Klasse BandEndpoint; hier finden wir die Zeilen:

if (containsBand(band)) {
  throw new EntityExistsException("Object already exists");
}

Hier wird überprüft, ob es das Objekt, das wir einfügen wollen, bereits gibt. Dazu werden in containsBand alle Attribute zum Vergleich herangezogen - auch das noch nicht gesetzte Attribut id. Dies führt zu der Exception, die uns das Leben schwer macht. Als Workaround lassen wir den Aufruf von containsBand nur zu, wenn das Attribut id gesetzt ist. Der Code sieht jetzt wie folgt aus:

if (band.getId() != null && containsBand(band)) {
     throw new EntityExistsException("Object already exists");
}

Wir speichern die Änderungen, starten den Server neu und führen den Test nochmals aus. Die id sollte jetzt erzeugt werden. Wir fügen noch ein, zwei weitere Datensätze ein und rufen anschließend den Service bandendpoint.listBand auf. Die Eingabefelder lassen wir dabei leer; als Ergebnis sehen wir alle Bands, die wir in der Datenbank erfasst haben.


Die App auf den aktuellen Stand bringen

Diese Stichproben sollen erst einmal reichen. Wir kümmern uns um unsere App. Wie im vorhergehenden Beitrag erzeugen wir eine passende Klassen-Bibliothek für den Client: Rechtsklick auf das Artists-AppEngine Projekt und im Menüpunkt 'Google' den Punkt 'Generate Cloud Endpoint Client Library' wählen.

In der App hat sich jetzt Einiges geändert und wir müssen die MainActivity-Klasse entsprechend korrigieren. Die Klasse BandService gibt es nicht mehr. Der Endpoint wird jetzt in Bandendpoint definiert. Da sich auch die Paketnamen geändert haben, müssen die import-Anweisungen angepasst werden.

In der doInBackground-Methode haben wir bisher mit dem Service getBand gearbeitet, der zu einer ID die passende Band findet. Da die ID jetzt vom System erzeugt wird, kennen wir sie nicht mehr. 

als Ersatz für getBand arbeiten wir mit dem listBand-Service, der uns alle Objekte liefert. Sie sind in einem listenähnlichen Objekt vom Typ CollectionResponseBand zusammengefasst. Im folgenden Beispiel ändern wir doInBackground so, dass wir einfach das erste Listenelement nehmen und es wie gehabt in der unveränderten Methode onPostExecute in die Text-View einfügen:

@Override
protected Band doInBackground(Void... params) {
  Band result = null;
  try {
    result = endpoint.listBand().execute().getItems().get(0);
  } catch (IOException e) {
    e.printStackTrace();
  }
  return result;
}


Die App sollte jetzt fehlerfrei arbeiten und eine Band anzeigen.

Alles Weitere

Die übrigen generierten CRUD-Services 
  • insertBand
  • updateBand
  • removeBand
zum einfügen, ändern und löschen von Daten, lassen sich ähnlich bequem nutzen. Konkrete Beispiele findet  man in der Dokumentation von  Google  oder in dem bereits erwähnten Artikel  und seinen Nachfolgern.  Die letzte Aufgabe besteht im Deployment in die Infrastruktur von Google und wird im folgenden abschließenden Beitrag beschrieben.

Keine Kommentare:

Kommentar veröffentlichen