Datenobjekte in Cloud Datastore werden als Entitäten bezeichnet. Eine Entität verfügt über ein oder mehrere benannte Attribute, von denen jedes einen oder mehrere Werte haben kann. Entitäten derselben Art müssen nicht dieselben Attribute haben und die Werte der Entitäten für ein bestimmtes Attribut müssen nicht alle denselben Datentyp haben. Falls erforderlich, kann eine Anwendung derartige Einschränkungen in einem eigenen Datenmodell festlegen und durchsetzen.
Datastore unterstützt viele Datentypen für Attributwerte. Diese umfassen unter anderem:
- Ganzzahlen
- Gleitkommazahlen
- Strings
- Datumsangaben
- Binärdaten
Eine vollständige Liste der Typen finden Sie unter Attribute und Werttypen.
Jede Entität in Datastore hat einen Schlüssel, der sie eindeutig identifiziert. Der Schlüssel besteht aus den folgenden Komponenten:
- Namespace der Entität, der die Mehrinstanzenfähigkeit ermöglicht
- Die Art der Entität, mit der sie für Datastore-Abfragen kategorisiert wird
- Kennung für die einzelne Entität, entweder
- ein Schlüsselnamen-String
- eine ganzzahlige numerische ID
- Optionaler Ancestor-Pfad zur Entität innerhalb der Datastore-Hierarchie
Eine Anwendung kann eine einzelne Entität mithilfe des Entitätsschlüssels aus Datastore abrufen oder eine bzw. mehrere Entitäten mit einer Abfrage ermitteln, die auf den Schlüsseln oder Attributwerten der Entitäten basiert.
Das Java App Engine SDK enthält eine einfache API, die im Paket com.google.appengine.api.datastore
bereitgestellt wird und die Funktionen von Datastore direkt unterstützt. Alle Beispiele in diesem Dokument basieren auf dieser Low-Level-API. Sie können sie entweder direkt in Ihrer Anwendung oder als Grundlage für die Erstellung einer eigenen Datenverwaltungsschicht verwenden.
Datastore selbst erzwingt keine Einschränkungen für die Struktur der Entitäten, beispielsweise ob ein bestimmtes Attribut einen Wert eines bestimmten Typs hat. Diese Aufgabe bleibt der Anwendung überlassen.
Arten und Kennungen
Jede Datastore-Entität ist von einer bestimmten Art. Mit dieser kann die Entität für Abfragen kategorisiert werden. In einer Personalanwendung kann beispielsweise jeder Mitarbeiter eines Unternehmens mit einer Entität der Art Employee
dargestellt werden. In der Java Datastore API wird die Art einer Entität beim Erstellen als Argument für den Konstruktor Entity()
angegeben. Alle Artnamen, die mit zwei Unterstrichen (__
) beginnen, sind reserviert und dürfen nicht verwendet werden.
Der folgende Beispielcode erstellt eine Entität der Art Employee
, gibt ihre Attributwerte an und speichert die Entität in Datastore:
Neben der Art verfügt jede Entität auch über eine Kennung, die beim Erstellen der Entität zugewiesen wird. Weil sie zum Schlüssel der Entität gehört, ist die Kennung dauerhaft mit der Entität verknüpft und kann nicht geändert werden. Es gibt zwei Möglichkeiten der Zuweisung:
- Die Anwendung kann ihren eigenen Schlüsselnamen-String für die Entität angeben.
- Datastore kann der Entität automatisch eine ganzzahlige numerische ID zuweisen.
Übergeben Sie dem Konstruktor beim Erstellen der Entität den Namen als zweites Argument, um einer Entität einen Schlüsselnamen zuzuweisen:
Soll Cloud Datastore automatisch eine numerische ID zuweisen, lassen Sie dieses Argument weg:
Kennungen zuweisen
Datastore kann so konfiguriert werden, dass automatische IDs mithilfe von zwei verschiedenen Richtlinien für automatische IDs generiert werden:
- Die Richtlinie
default
generiert eine zufällige Abfolge von bisher nicht verwendeten IDs, die näherungsweise gleichmäßig verteilt sind. Jede ID kann maximal 16 Dezimalstellen enthalten. - Die Richtlinie
legacy
erstellt eine Abfolge nicht aufeinanderfolgender IDs aus kleineren Ganzzahlen.
Wenn Sie die Entitäts-IDs für den Nutzer anzeigen möchten und/oder deren Reihenfolge wichtig ist, ist eine manuelle Zuordnung die beste Lösung.
Datastore generiert eine zufällige Abfolge von nicht verwendeten IDs, die annähernd gleichmäßig verteilt sind. Jede ID kann maximal 16 Dezimalstellen enthalten.
Ancestor-Pfade
Entitäten in Cloud Datastore sind hierarchisch organisiert, ähnlich der Verzeichnisstruktur eines Dateisystems. Wenn Sie eine Entität erstellen, können Sie optional eine weitere Entität als übergeordnetes Element angeben. Die neue Entität ist dann ein untergeordnetes Element der übergeordneten Entität. Im Gegensatz zu einem Dateisystem muss die übergeordnete Entität nicht tatsächlich vorhanden sein. Eine Entität ohne übergeordnetes Element wird als Stammentität bezeichnet. Die Verknüpfung zwischen einer Entität und ihrer übergeordneten Entität ist dauerhaft und kann nicht geändert werden, nachdem die Entität erstellt wurde. Cloud Datastore weist zwei Entitäten mit derselben übergeordneten Entität oder zwei Stammentitäten (Entitäten ohne übergeordnete Entität) niemals dieselbe numerische ID zu.
Alle übergeordneten Elemente einer Entität werden als ihre Ancestors bezeichnet und alle untergeordneten Entitäten sind ihre Nachfolger. Eine Stammentität und alle ihre Nachfolger gehören zu derselben Entitätengruppe. Die Abfolge der Entitäten, von einer Stammentität über die untergeordneten Elemente bis zu einer bestimmten Entität, bildet den Ancestor-Pfad. Der vollständige Schlüssel, der die Entität identifiziert, besteht aus einer Abfolge von Art/Kennungs-Paaren, die den Ancestor-Pfad angeben und mit dem Paar der Entität selbst enden:
[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]
Bei einer Stammentität ist der Ancestor-Pfad leer und der Schlüssel besteht ausschließlich aus der eigenen Art und der eigenen Kennung der Entität:
[Person:GreatGrandpa]
Dieses Konzept wird anhand des folgenden Diagramms veranschaulicht:
Wenn Sie das übergeordnete Element einer Entität festlegen möchten, geben Sie den Schlüssel der übergeordneten Entität beim Erstellen der untergeordneten Entität als Argument für den Konstruktor Entity()
an. Sie können den Schlüssel abrufen, indem Sie die Methode getKey()
der übergeordneten Entität aufrufen:
Hat die neue Entität auch einen Schlüsselnamen, übergeben Sie dem Entity()
-Konstruktor beim Erstellen der Entität den Namen als zweites Argument und den Schlüssel der übergeordneten Entität als drittes Argument:
Transaktionen und Entitätengruppen
Jeder Vorgang zum Erstellen, Aktualisieren und Löschen einer Entität findet im Rahmen einer Transaktion statt. Eine einzelne Transaktion kann eine beliebige Anzahl solcher Vorgänge umfassen. Um die Konsistenz der Daten zu wahren, sorgt die Transaktion dafür, dass entweder alle enthaltenen Vorgänge auf Datastore als Einheit angewendet werden oder überhaupt nicht, sofern einer der Vorgänge fehlgeschlagen ist. Darüber hinaus greifen alle strikt konsistenten Lesevorgänge (Ancestor-Abfragen oder -Abrufe) innerhalb derselben Transaktion auf einen konsistenten Snapshot der Daten zurück.
Wie oben bereits erwähnt besteht eine Entitätengruppe aus einer Reihe von Entitäten, die über einen Ancestor-Pfad mit einem gemeinsamen Stammelement verbunden sind. Die Anordnung von Daten in Entitätengruppen kann einschränken, welche Transaktionen ausgeführt werden können:
- Alle Daten, auf die eine Transaktion zugreift, müssen in maximal 25 Entitätengruppen enthalten sein.
- Wenn Sie Abfragen innerhalb einer Transaktion verwenden möchten, müssen die Daten so in Entitätengruppen angeordnet werden, dass Sie Ancestor-Filter angeben können, die mit den richtigen Daten übereinstimmen.
- Es gibt einen Schreibdurchsatzgrenzwert von ca. einer Transaktion pro Sekunde innerhalb einer einzelnen Entitätengruppe. Diese Begrenzung ist vorhanden, weil Datastore eine synchrone Replikation ohne Master jeder Entitätengruppe über einen breiten geografischen Bereich hinweg durchführt, um hohe Verlässlichkeit und Fehlertoleranz zu ermöglichen.
In vielen Anwendungen kann es akzeptabel sein, für den Abruf einer breiten Ansicht nicht zusammengehöriger Daten Eventual Consistency zu verwenden, also eine entitätengruppenübergreifende Nicht-Ancestor-Abfrage, die gelegentlich etwas veraltete Daten zurückgeben kann. Anschließend wird dann strikte Konsistenz (eine Ancestor-Abfrage oder ein get
einer einzelnen Entität) verwendet, um eine einzelne Gruppe von eng zusammengehörigen Daten anzusehen oder zu bearbeiten. In derartigen Anwendungen ist die Gruppierung von eng zusammengehörigen Daten in Entitätsgruppen im Allgemeinen ein guter Ansatz.
Weitere Informationen finden Sie unter Für strikte Konsistenz strukturieren.
Attribute und Werttypen
Die mit einer Entität verknüpften Datenwerte bestehen aus einem oder mehreren Attributen. Jedes Attribut hat einen Namen und einen oder mehrere Werte. Ein Attribut kann Werte mit mehr als einem Typ haben und zwei Entitäten können Werte unterschiedlichen Typs für dasselbe Attribut haben. Attribute können indexiert oder nicht indexiert sein. Abfragen, die nach einem Attribut A sortieren oder filtern, ignorieren Entitäten, bei denen A nicht indexiert ist. Eine Entität kann höchstens 20.000 indexierte Attribute haben.
Die folgenden Werttypen werden unterstützt:
Werttyp | Java-Typ(en) | Sortierreihenfolge | Hinweise |
---|---|---|---|
Ganzzahl | short int long java.lang.Short java.lang.Integer java.lang.Long |
Numerisch | Gespeichert als lange Ganzzahl, dann Umwandlung in den Feldtyp Überlauf der außerhalb des Bereichs liegenden Werte |
Gleitkommazahl | float double java.lang.Float java.lang.Double |
Numerisch | 64 Bit mit doppelter Genauigkeit, IEEE 754 |
Boolesch | boolean java.lang.Boolean |
false <true |
|
Textstring (kurz) | java.lang.String |
Unicode | Bis zu 1.500 Byte Werte über 1.500 Byte geben IllegalArgumentException aus |
Textstring (lang) | com.google.appengine.api.datastore.Text |
Ohne | Bis zu 1 Megabyte Nicht indexiert |
Bytestring (kurz) | com.google.appengine.api.datastore.ShortBlob |
Bytereihenfolge | Bis zu 1.500 Byte Werte, die länger als 1.500 Byte sind, geben IllegalArgumentException aus |
Bytestring (lang) | com.google.appengine.api.datastore.Blob |
Ohne | Bis zu 1 Megabyte Nicht indexiert |
Datum und Uhrzeit | java.util.Date |
Chronologisch | |
Geografischer Punkt | com.google.appengine.api.datastore.GeoPt |
Nach Breitengrad, dann nach Längengrad |
|
Postanschrift | com.google.appengine.api.datastore.PostalAddress |
Unicode | |
Telefonnummer | com.google.appengine.api.datastore.PhoneNumber |
Unicode | |
E-Mail-Adresse | com.google.appengine.api.datastore.Email |
Unicode | |
Nutzer eines Google-Kontos | com.google.appengine.api.users.User |
E-Mail-Adresse in Unicode-Reihenfolge |
|
Instant-Messaging-Handle | com.google.appengine.api.datastore.IMHandle |
Unicode | |
Link | com.google.appengine.api.datastore.Link |
Unicode | |
Kategorie | com.google.appengine.api.datastore.Category |
Unicode | |
Bewertung | com.google.appengine.api.datastore.Rating |
Numerisch | |
Datastore-Schlüssel | com.google.appengine.api.datastore.Key oder das referenzierte Objekt (als untergeordnetes Element) |
Nach Pfadelementen (Art, Kennung, Art, Kennung...) |
Bis zu 1.500 Byte Werte, die länger als 1.500 Byte sind, geben IllegalArgumentException aus |
Blobstore-Schlüssel | com.google.appengine.api.blobstore.BlobKey |
Bytereihenfolge | |
Eingebettete Entität | com.google.appengine.api.datastore.EmbeddedEntity |
Ohne | Nicht indexiert |
Null | null |
Ohne |
Wichtig: Das Speichern von users.User
als Attributwert sollte unbedingt vermieden werden, da die E-Mail-Adresse und die eindeutige ID enthalten sind. Wenn ein Nutzer seine E-Mail-Adresse ändert und Sie das alte gespeicherte Objekt user.User
mit dem neuen user.User
-Wert vergleichen, würden sie nicht übereinstimmen. Stattdessen sollten Sie den Nutzer-ID-Wert User
als stabile eindeutige Kennung des Nutzers verwenden.
Für Textstrings und nicht codierte Binärdaten (Bytestrings) unterstützt Datastore zwei Werttypen:
- Kurze Strings (bis zu 1.500 Byte) werden indexiert und können in Filterbedingungen der Abfrage und in Sortierfolgen verwendet werden.
- Lange Strings (bis zu 1 Megabyte) werden nicht indexiert und können nicht in Abfragefiltern und Sortierfolgen verwendet werden.
Blob
. Dieser Typ steht nicht im Zusammenhang mit den Blobs in der Blobstore API.
Wenn eine Abfrage ein Attribut mit Werten verschiedener Typen enthält, verwendet Datastore eine deterministische Sortierung anhand der internen Darstellungen:
- Nullwerte
- Festkommazahlen
- Ganzzahlen
- Datums- und Uhrzeitwerte
- Bewertungen
- Boolesche Werte
- Bytesequenzen
- Bytestring
- Unicode-String
- Blobstore-Schlüssel
- Gleitkommazahlen
- Geografische Punkte
- Nutzer von Google-Konten
- Datastore-Schlüssel
Da lange Textstrings, lange Bytestrings und eingebettete Entitäten nicht indexiert werden, ist für sie keine Reihenfolge definiert.
Mit Entitäten arbeiten
Anwendungen können mit der Datastore API Entitäten erstellen, abrufen, aktualisieren und löschen. Wenn die Anwendung den vollständigen Schlüssel für eine Entität kennt (oder ihn aus dem übergeordneten Schlüssel, der Art und der Kennung ableiten kann), kann sie mit dem Schlüssel direkt mit der Entität arbeiten. Eine Anwendung kann den Schlüssel einer Entität auch als Ergebnis einer Datastore-Abfrage abrufen. Weitere Informationen finden Sie unter Datastore-Abfragen.
Die Java Datastore API verwendet zur Verarbeitung von Entitäten Methoden der Schnittstelle DatastoreService
. Sie erhalten ein DatastoreService
-Objekt, indem Sie die statische Methode DatastoreServiceFactory.getDatastoreService()
aufrufen:
Entität erstellen
Sie können eine neue Entität erstellen, indem Sie eine Instanz der Klasse Entity
erstellen, die die Art der Entität als Argument für den Konstruktor Entity()
liefert.
Nachdem Sie gegebenenfalls Attribute für die Entität eingegeben haben, speichern Sie sie im Datenspeicher. Dazu übergeben Sie die Entität als Argument an die Methode DatastoreService.put()
. Sie können den Schlüsselnamen der Entität angeben, wenn Sie ihn als zweites Argument an den Konstruktor übergeben:
Wenn Sie keinen Schlüsselnamen angeben, generiert Datastore automatisch eine numerische ID für den Schlüssel der Entität:
Entität abrufen
Um eine Entität mit einem bestimmten Schlüssel abzurufen, übergeben Sie das Objekt Key
an die Methode DatastoreService.get()
:
Entität aktualisieren
Wenn Sie eine vorhandene Entität aktualisieren möchten, ändern Sie die Attribute des Entitätsobjekts und übergeben es dann an die Methode DatastoreService.put()
. Die bestehende Entität wird mit den Objektdaten überschrieben. Bei jedem Aufruf von put()
wird das ganze Objekt an Datastore gesendet.
Entität löschen
Sie können eine Entität unter Angabe ihres Schlüssels mit der Methode DatastoreService.delete()
löschen:
Wiederkehrende Attribute
Sie können mehrere Werte in einem einzelnen Attribut speichern.
Eingebettete Entitäten
In manchen Fällen ist es sinnvoll, eine Entität als Attribut in eine andere Entität einzubetten. Dies kann beispielsweise nützlich sein, um eine hierarchische Struktur von Attributwerten innerhalb einer Entität zu erstellen. Mit der Java-Klasse EmbeddedEntity
ist das kein Problem:
Ist eine eingebettete Entität in Indexen enthalten, können Sie untergeordnete Attribute abfragen. Schließen Sie eine eingebettete Entität von der Indexierung aus, werden auch alle untergeordneten Attribute von der Indexierung ausgeschlossen. Sie können einer eingebetteten Entität optional einen Schlüssel zuordnen. Aber im Gegensatz zu einer vollwertigen Entität ist der Schlüssel nicht erforderlich und kann, selbst wenn er vorhanden ist, nicht zum Abrufen der Entität verwendet werden.
Statt die Attribute der eingebetteten Entität manuell auszufüllen, können Sie sie mit der Methode setPropertiesFrom()
aus einer vorhandenen Entität kopieren:
Sie können später dieselbe Methode zum Wiederherstellen der ursprünglichen Entität aus der eingebetteten Entität verwenden:
Batchvorgänge
Die DatastoreService
-Methoden put()
, get()
und delete()
und ihre Gegenstücke in AsyncDatastoreService haben Batchversionen, die ein iterierbares-Objekt der Klasse Entity
für put()
, Key
für get()
und delete()
akzeptieren. Dadurch können mehrere Entitäten mit einem einzigen Cloud Datastore-Aufruf verarbeitet werden:
Diese Batchvorgänge gruppieren alle Entitäten oder Schlüssel nach Entitätengruppe und führen dann den angeforderten Vorgang für alle Entitätengruppen parallel durch. Solche Batchaufrufe sind schneller als separate Aufrufe für jede einzelne Entität, weil nur der Overhead für einen einzigen Dienstaufruf anfällt. Wenn mehrere Entitätengruppen betroffen sind, wird die Arbeit für alle Gruppen im Parallelverfahren auf der Serverseite ausgeführt.
Schlüssel generieren
Anwendungen können die Klasse KeyFactory
verwenden, um ein Key
-Objekt für eine Entität aus bekannten Komponenten wie Typ und ID der Entität zu erstellen. Für eine Entität ohne übergeordnetes Element übergeben Sie den Typ und die ID an die statische Methode KeyFactory.createKey()
, um den Schlüssel zu erstellen. In den folgenden Beispielen wird ein Schlüssel für eine Entität vom Typ Person
mit dem Schlüsselnamen "GreatGrandpa"
oder der numerischen ID 74219
erstellt:
Wenn der Schlüssel eine Pfadkomponente enthält, können Sie den Pfad mit der Hilfsklasse KeyFactory.Builder
erstellen. Die Methode addChild
dieser Klasse fügt dem Pfad eine einzelne Entität hinzu und gibt den Builder selbst zurück, sodass Sie eine Reihe von Aufrufen, beginnend mit der Stammentität, verketten können, um den Pfad eine Entität aufzubauen nacheinander. Rufen Sie nach dem Erstellen des vollständigen Pfads getKey
auf, um den resultierenden Schlüssel abzurufen:
Die Klasse KeyFactory
enthält außerdem die statischen Methoden keyToString
und stringToKey
für die Konvertierung zwischen Schlüsseln und ihren Stringdarstellungen:
Die Stringdarstellung eines Schlüssels ist "websicher". Das bedeutet, dass sie keine Zeichen enthält, die in HTML oder URLs als Sonderzeichen gelten.
Leere Liste verwenden
In der Vergangenheit konnte Datastore keine Attribute für eine leere Liste darstellen. Im Java SDK wurden leere Sammlungen daher als Nullwerte gespeichert, wodurch es nicht möglich ist, zwischen Nullwerten und leeren Listen zu unterscheiden. Zur Aufrechterhaltung der Abwärtskompatibilität bleibt dies das Standardverhalten, das sich so zusammenfassen lässt:- Nullattribute werden in Datastore als Null geschrieben.
- Leere Sammlungen werden in den Datenspeicher als Null geschrieben.
- Nullwerte werden von Datastore als Null gelesen.
- Leere Sammlungen werden als Null gelesen.
Wenn Sie jedoch das Standardverhalten ändern, unterstützt das Java SDK das Speichern leerer Listen. Wir empfehlen, die Auswirkungen zu erwägen, die durch eine Änderung des Standardverhaltens Ihrer Anwendung entstehen, und anschließend die Unterstützung für leere Listen zu aktivieren.
Legen Sie das Attribut DATASTORE_EMPTY_LIST_SUPPORT während der Initialisierung der Anwendung fest, um das Standardverhalten so zu ändern, dass Sie leere Listen verwenden können:
System.setProperty(DatastoreServiceConfig.DATASTORE_EMPTY_LIST_SUPPORT, Boolean.TRUE.toString());
Wird dieses Attribut wie oben auf true
gesetzt, gilt:
- Nullattribute werden in Datastore als Null geschrieben.
- Leere Sammlungen werden als leere Liste in Datastore geschrieben.
- Nullwerte werden von Datastore als Null gelesen.
- Beim Lesen aus Datastore wird eine leere Liste als leere Sammlung zurückgegeben.