Dans Datastore, les objets de données sont appelés entités. Une entité est associée à une ou plusieurs propriétés nommées, chacune pouvant avoir une ou plusieurs valeurs. Les entités du même genre n'ont pas besoin d'avoir les mêmes propriétés, et il n'est pas nécessaire que les valeurs d'une entité pour une propriété donnée soient toutes du même type de données. (Si nécessaire, une application peut établir et appliquer de telles restrictions dans son propre modèle de données.)
Datastore est compatible avec divers types de données pour les valeurs de propriété, parmi lesquels :
- Entiers
- Nombres à virgule flottante
- Chaînes
- Dates
- Données binaires
Pour obtenir la liste complète des types, consultez la section Propriétés et types de valeurs.
Chaque entité dans Datastore possède une clé qui l'identifie de manière unique. Cette clé comprend les composants suivants :
- L'espace de noms de l'entité, qui permet de définir l'architecture mutualisée.
- Le genre de l'entité, qui permet de la classer pour les besoins des requêtes Datastore.
- Un identifiant associé à l'entité de manière individuelle, qui peut être :
- Une chaîne de nom de clé
- Un ID numérique sous forme d'entier.
- Un chemin d'ancêtre facultatif localisant l'entité dans la hiérarchie Datastore
Une application peut extraire une entité individuelle de Datastore à l'aide de la clé de l'entité, ou extraire une ou plusieurs entités en émettant une requête basée sur leurs clés ou leurs valeurs de propriétés.
Le SDK Java App Engine inclut une API simple, fournie dans le package com.google.appengine.api.datastore
, qui est directement compatible avec les fonctionnalités de Datastore. Tous les exemples de ce document sont basés sur cette API de bas niveau. Vous pouvez choisir de l'utiliser directement dans votre application ou comme base pour créer votre propre couche de gestion de données.
Datastore lui-même n'applique aucune restriction à la structure des entités, par exemple une propriété donnée ayant une valeur d'un type particulier. Cette tâche revient à l'application.
Genres et identifiants
Chaque entité Datastore est d'un genre particulier, ce qui permet de la classer dans une catégorie pour les besoins des requêtes. Par exemple, une application de ressources humaines peut représenter chaque employé d'une entreprise avec une entité du genre Employee
. Dans l'API Datastore Java, vous spécifiez le genre d'une entité lors de sa création en tant qu'argument au constructeur Entity()
. Tous les noms de genre commençant par deux traits de soulignement (__
) sont réservés et ne peuvent pas être employés.
L'exemple suivant crée une entité du genre Employee
, renseigne ses valeurs de propriété et l'enregistre dans Datastore :
En plus d'un genre, chaque entité possède un identifiant, qui lui est attribué au moment de sa création. Comme il fait partie de la clé de l'entité, l'identifiant est associé de manière permanente à celle-ci et ne peut pas être modifié. Il peut être attribué de deux manières :
- L'application peut spécifier sa propre chaîne de nom de clé pour l'entité.
- Datastore peut attribuer automatiquement à l'entité un ID numérique sous forme d'entier.
Pour attribuer un nom de clé à une entité, indiquez le nom comme deuxième argument du constructeur lors de la création de l'entité :
Pour que Datastore attribue un ID numérique automatiquement, omettez cet argument :
Attribuer des identifiants
Datastore peut être configuré de sorte à générer des identifiants automatiques à l'aide de deux règles d'ID automatique différentes :
- La règle
default
génère une séquence aléatoire d'ID inutilisés distribués d'une manière approximativement uniforme. Chaque ID peut comporter jusqu'à 16 chiffres décimaux. - La règle
legacy
crée une séquence d'ID entiers plus petits non consécutifs.
Si vous souhaitez que les ID d'entité soient visibles par l'utilisateur et/ou si vous souhaitez les afficher en fonction de leur ordre, l'allocation manuelle est la meilleure solution.
Datastore génère une séquence aléatoire d'ID inutilisés distribués de manière approximativement uniforme. Chaque ID peut comporter jusqu'à 16 chiffres décimaux.
Chemins d'ancêtre
Dans Cloud Datastore, les entités forment un espace structuré de manière hiérarchique, semblable à la structure de répertoires d'un système de fichiers. Lorsque vous créez une entité, vous pouvez éventuellement en désigner une autre comme son parent. La nouvelle entité est alors un enfant de l'entité parente. Contrairement à ce qui se produit dans un système de fichiers, l'entité parente n'a pas besoin d'exister réellement. Une entité sans parent est une entité racine. L'association entre une entité et son parent est permanente, et elle ne peut plus être modifiée une fois l'entité créée. Cloud Datastore n'attribue jamais le même ID numérique à deux entités ayant le même parent ou à deux entités racines (celles sans parent).
Les ancêtres d'une entité correspondent à son parent, au parent de son parent et ainsi de suite, de manière récursive. Ses descendants sont ses enfants, les enfants de ses enfants, etc. Une entité racine et tous ses descendants appartiennent au même groupe d'entités. La séquence d'entités commençant par une entité racine, puis allant du parent à l'enfant et menant à une entité donnée, constitue le chemin d'ancêtre de cette entité. La clé complète identifiant l'entité consiste en une séquence de paires genre/identifiant, qui spécifie son chemin d'ancêtre et se termine par les valeurs de l'entité elle-même :
[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]
Pour une entité racine, le chemin d'ancêtre est vide, et la clé est constituée uniquement du genre et de l'identifiant de l'entité :
[Person:GreatGrandpa]
Le schéma suivant illustre ce concept :
Pour désigner le parent d'une entité, fournissez la clé de l'entité parente en tant qu'argument au constructeur Entity()
lors de la création de l'entité enfant. Vous pouvez obtenir la clé en appelant la méthode getKey()
de l'entité parente :
Si la nouvelle entité possède également un nom de clé, fournissez le nom de clé en tant que deuxième argument du constructeur Entity()
et la clé de l'entité parent en tant que troisième argument :
Transactions et groupes d'entités
Toute tentative de création, de mise à jour ou de suppression d'une entité a lieu dans le cadre d'une transaction. Une même transaction peut inclure un nombre illimité d'opérations de ce type. Pour maintenir la cohérence des données, la transaction garantit que toutes les opérations qu'elle contient sont appliquées à Datastore de manière unitaire ou, en cas d'échec de l'une de ces opérations, qu'aucune d'entre elles n'est appliquée. En outre, toutes les lectures fortement cohérentes (requêtes ascendantes ou opérations "get") effectuées dans la même transaction observent un instantané cohérent des données.
Comme mentionné ci-dessus, un groupe d'entités est un ensemble d'entités connectées par le biais d'un ancêtre à un élément racine commun. L'organisation des données en groupes d'entités peut limiter le nombre de transactions pouvant être effectuées :
- Toutes les données auxquelles accède une transaction doivent être contenues dans 25 groupes d'entités au maximum.
- Si vous souhaitez employer des requêtes dans une transaction, les données doivent être organisées en groupes d'entités de sorte que vous puissiez spécifier des filtres d'ancêtres qui correspondent aux données appropriées.
- Le débit en écriture est limité à environ une transaction par seconde pour un seul groupe d'entités. Cette limite a été définie car Datastore effectue une réplication synchrone sans maître de chaque groupe d'entités sur une vaste zone géographique, afin de fournir une fiabilité et une tolérance aux pannes élevées.
Dans de nombreuses applications, il est acceptable de recourir à la cohérence à terme (à savoir à une requête non ascendante couvrant plusieurs groupes d'entités et pouvant parfois renvoyer des données légèrement obsolètes) lors de l'obtention d'une vue d'ensemble de données non liées, puis de passer à la cohérence forte (requête ascendante ou opération get
d'une seule entité) lors de la visualisation ou de la modification d'un seul ensemble de données étroitement liées. Dans ces applications, il est généralement recommandé d'utiliser un groupe d'entités distinct pour chaque ensemble de données étroitement liées.
Pour en savoir plus, consultez la page Structurer des données pour renforcer la cohérence.
Propriétés et types de valeurs
Les valeurs de données associées à une entité consistent en une ou plusieurs propriétés. Chaque propriété a un nom, et une ou plusieurs valeurs. Une propriété peut avoir des valeurs de plus d'un type, et deux entités peuvent avoir des valeurs de types différents pour la même propriété. Les propriétés peuvent être indexées ou non indexées (les requêtes qui ordonnent ou filtrent une propriété P ignorent les entités pour lesquelles P n'est pas indexée.) Une entité peut avoir 20 000 propriétés indexées au maximum.
Les types de valeurs suivants sont acceptés :
Type de valeur | Type Java | Ordre de tri | Remarques |
---|---|---|---|
Entier | short int long java.lang.Short java.lang.Integer java.lang.Long |
Numérique | Stocké sous forme d'entier long, puis converti au type de champ Dépassement des valeurs hors plage |
Nombre à virgule flottante | float double java.lang.Float java.lang.Double |
Numérique | Double précision 64 bits, IEEE 754. |
Booléen | boolean java.lang.Boolean |
false <true |
|
Chaîne de texte (courte) | java.lang.String |
Unicode | Jusqu'à 1 500 octets Valeurs supérieures à 1 500 octets renvoient IllegalArgumentException |
Chaîne de texte (longue) | com.google.appengine.api.datastore.Text |
Aucun | Jusqu'à 1 mégaoctet Non indexé |
Chaînes d'octets (courtes) | com.google.appengine.api.datastore.ShortBlob |
Ordre des octets | Jusqu'à 1 500 octets Valeurs supérieures à 1 500 octets renvoient IllegalArgumentException |
Chaîne d'octets (longue) | com.google.appengine.api.datastore.Blob |
Aucun | Jusqu'à 1 mégaoctet Non indexé |
Date et heure | java.util.Date |
Chronologique | |
Point géographique | com.google.appengine.api.datastore.GeoPt |
En fonction de la latitude, puis de la longitude |
|
Adresse postale | com.google.appengine.api.datastore.PostalAddress |
Unicode | |
Numéro de téléphone | com.google.appengine.api.datastore.PhoneNumber |
Unicode | |
Adresse e-mail | com.google.appengine.api.datastore.Email |
Unicode | |
Utilisateur de Google Accounts | com.google.appengine.api.users.User |
Adresse e-mail dans l'ordre Unicode |
|
Descripteur de messagerie instantanée | com.google.appengine.api.datastore.IMHandle |
Unicode | |
Lien | com.google.appengine.api.datastore.Link |
Unicode | |
Catégorie | com.google.appengine.api.datastore.Category |
Unicode | |
Note | com.google.appengine.api.datastore.Rating |
Numérique | |
Clé Datastore | com.google.appengine.api.datastore.Key ou l'objet référencé (en tant qu'enfant) |
Par éléments de chemin d'accès (genre, identifiant, genre, identifiant, etc.) |
Jusqu'à 1 500 octets Valeurs supérieures à 1 500 octets renvoient IllegalArgumentException |
Clé Blobstore | com.google.appengine.api.blobstore.BlobKey |
Ordre des octets | |
Entité intégrée | com.google.appengine.api.datastore.EmbeddedEntity |
Aucun | Pas indexé |
Vide | null |
Aucun |
Important : Nous vous recommandons vivement de ne pas enregistrer de valeur de propriété users.User
, car elle inclut l'adresse e-mail et l'ID unique. Si un utilisateur change d'adresse e-mail et que vous comparez l'ancienne valeur user.User
stockée à la nouvelle valeur user.User
, elles ne correspondront pas. Envisagez plutôt d'utiliser la valeur d'ID d'utilisateur User
comme identifiant unique stable de l'utilisateur.
Pour les chaînes de texte et les données binaires non codées (chaînes d'octets), Datastore prend en charge deux types de valeurs :
- Les chaînes courtes (jusqu'à 1 500 octets) sont indexées et peuvent être utilisées dans les conditions de filtre de requêtes et les ordres de tri.
- Les chaînes longues (jusqu'à 1 Mo) ne sont pas indexées et ne peuvent pas être utilisées dans les filtres de requête et les ordres de tri.
Blob
dans l'API Datastore. Ce type n'est pas lié aux objets blob utilisés dans l'API Blobstore.
Lorsqu'une requête implique une propriété avec des valeurs de types mixtes, Datastore utilise un ordre déterministe basé sur les représentations internes :
- Valeurs Null
- Nombres à virgule fixe
- Entiers
- Dates et heures
- Notes
- Valeurs booléennes
- Séquences d'octets
- Chaîne d'octets
- Chaîne Unicode
- Clés Blobstore
- Nombres à virgule flottante
- Points géographiques
- Utilisateurs disposant d'un compte Google
- Clés Datastore
Aucun ordre n'est défini pour les chaînes de texte longues et les chaînes d'octets longues, car elles ne sont pas indexées.
Utiliser des entités
Les applications peuvent utiliser l'API Datastore pour créer, récupérer, mettre à jour et supprimer des entités. Si l'application connaît la clé complète d'une entité (ou si elle peut la déduire de sa clé parente, de son genre et de son identifiant), elle peut s'en servir pour effectuer des opérations directement sur l'entité. Une application peut également obtenir la clé d'une entité à la suite d'une requête Datastore. Pour plus d'informations, consultez la page Requêtes Datastore.
L'API Datastore Java utilise des méthodes de l'interface DatastoreService
pour effectuer des opérations sur des entités. Vous obtenez un objet DatastoreService
en appelant la méthode statique DatastoreServiceFactory.getDatastoreService()
:
Créer une entité
Vous pouvez créer une entité en générant une instance de classe Entity
, en fournissant le genre de l'entité en tant qu'argument au constructeur Entity()
.
Après avoir renseigné les propriétés de l'entité si nécessaire, vous devez l'enregistrer dans le datastore en la transmettant en tant qu'argument à la méthode DatastoreService.put()
. Vous pouvez spécifier le nom de la clé de l'entité en le transmettant comme second argument au constructeur.
Si vous ne fournissez pas de nom de clé, Datastore génère automatiquement un ID numérique pour la clé de l'entité :
Récupérer une entité
Pour récupérer une entité identifiée par une clé donnée, transmettez l'objet Key
à la méthode DatastoreService.get()
:
Mettre à jour une entité
Pour mettre à jour une entité existante, modifiez les attributs de l'objet Entity, puis transmettez-le à la méthode DatastoreService.put()
. Les données de l'objet écrasent l'entité existante. L'objet entier est envoyé à Datastore à chaque appel de put()
.
Supprimer une entité
Selon la clé d'une entité, vous pouvez supprimer l'entité à l'aide de la méthode DatastoreService.delete()
.
Propriétés répétées
Vous pouvez stocker plusieurs valeurs dans une même propriété.
Entités intégrées
Il peut parfois être utile d'intégrer une entité en tant que propriété d'une autre entité. Cela peut être utile, par exemple, pour créer une structure hiérarchique de valeurs de propriété au sein d'une entité. La classe Java EmbeddedEntity
vous permet d'effectuer les opérations suivantes :
Lorsqu'une entité intégrée est incluse dans les index, vous pouvez interroger les sous-propriétés. Si vous excluez une entité intégrée de l'indexation, toutes les sous-propriétés en sont également exclues. Si vous le souhaitez, vous pouvez associer une clé à une entité intégrée, mais contrairement à une entité complète, la clé n'est pas obligatoire. De plus, même si elle est présente, elle ne peut pas être utilisée pour récupérer l'entité.
Au lieu de renseigner manuellement les propriétés de l'entité intégrée, vous pouvez utiliser la méthode setPropertiesFrom()
pour les copier à partir d'une entité existante :
Vous pouvez utiliser ultérieurement la même méthode pour récupérer l'entité d'origine à partir de l'entité intégrée :
Opérations par lot
Les méthodes DatastoreService
put()
, get()
et delete()
(et leurs équivalents AsyncDatastoreService) ont des versions par lot acceptant un objet iterable (de classe Entity
pour put()
, Key
pour get()
et delete()
) et l'utilisent pour intervenir sur plusieurs entités via un seul appel Datastore :
Ces opérations par lots regroupent toutes les entités ou clés par groupe d'entités, puis exécutent l'opération demandée sur chaque groupe d'entités en parallèle. Il est plus rapide d'effectuer des appels par lot que des appels distincts pour chaque entité individuelle, car ils entraînent une surcharge pour un seul appel de service. Si plusieurs groupes d'entités sont impliqués, les opérations pour tous les groupes sont effectuées en parallèle côté serveur.
Générer des clés
Les applications peuvent utiliser la classe KeyFactory
afin de créer un objet Key
pour une entité à partir de composants connus, tels que le genre et l'identifiant de l'entité. Pour une entité sans parent, transmettez le genre et l'identifiant (chaîne de nom de clé ou ID numérique) à la méthode statique KeyFactory.createKey()
pour créer la clé. Les exemples suivants créent une clé pour une entité de genre Person
avec le nom de clé "GreatGrandpa"
ou l'ID numérique 74219
:
Si la clé inclut un composant de chemin, vous pouvez utiliser la classe d'assistance KeyFactory.Builder
pour créer le chemin. La méthode addChild
de cette classe ajoute une seule entité au chemin et renvoie le générateur lui-même. Vous pouvez ainsi exécuter une série d'appels à la suite, en commençant par l'entité racine, afin de créer le chemin une entité à la fois. Après avoir créé le chemin complet, appelez getKey
pour récupérer la clé obtenue :
La classe KeyFactory
inclut également les méthodes statiques keyToString
et stringToKey
pour effectuer une conversion entre les clés et leurs représentations sous forme de chaîne :
La représentation sous forme de chaîne d'une clé est adaptée au Web : elle ne contient aucun caractère considéré comme spécial en HTML ou dans les URL.
Utiliser une liste vide
Auparavant, Datastore n'avait pas de représentation pour une propriété représentant une liste vide. Le SDK Java a résolu ce problème en stockant les collections vides en tant que valeurs Null. Il n'y a donc aucun moyen de distinguer les valeurs Null des listes vides. Pour assurer la rétrocompatibilité, ce comportement par défaut s'applique toujours, comme résumé ci-dessous :- Les propriétés Null sont écrites comme nulles dans Datastore
- Les collections vides sont écrites comme nulles dans Datastore.
- Une valeur Null est lue comme étant nulle à partir de Datastore.
- Les collections vides sont lues comme étant Null.
Toutefois, si vous modifiez le comportement par défaut, le SDK pour Java prendra en charge le stockage de listes vides. Nous vous recommandons de prendre en compte les conséquences de la modification du comportement par défaut de votre application, puis d'activer la prise en charge des listes vides.
Pour modifier le comportement par défaut afin d'utiliser des listes vides, définissez la propriété DATASTORE_EMPTY_LIST_SUPPORT lors de l'initialisation de votre application comme suit :
System.setProperty(DatastoreServiceConfig.DATASTORE_EMPTY_LIST_SUPPORT, Boolean.TRUE.toString());
Avec cette propriété définie sur true
, comme illustré ci-dessus :
- Les propriétés Null sont écrites comme nulles dans Datastore
- Les collections vides sont écrites sous forme de liste vide dans Datastore
- Une valeur Null est lue comme étant nulle à partir de Datastore.
- Lors de la lecture depuis Datastore, une liste vide est renvoyée sous forme de collection vide.