Los objetos de datos en Datastore se conocen como entidades. Una entidad posee una o más propiedades con nombre, que pueden tener uno o más valores. No es necesario que las entidades del mismo tipo tengan las mismas propiedades, ni que los valores de una entidad para una propiedad determinada correspondan al mismo tipo de datos. (Si fuera necesario, una aplicación puede establecer y aplicar estas restricciones en su propio modelo de datos).
Datastore es compatible con una variedad de tipos de datos para valores de propiedad. Se incluyen, entre otros:
- Números enteros
- números de punto flotante
- Strings
- Fechas
- Datos binarios
Para obtener una lista completa de los tipos, consulta Tipos de valores y propiedades.
Cada entidad en Datastore tiene una clave que la identifica de manera única. La clave consta de los siguientes componentes:
- El espacio de nombres de la entidad, que permite la función de multiusuario
- El tipo de entidad, que la clasifica para realizar consultas de Datastore
- Un identificador de la entidad individual, que puede ser de alguna de las siguientes clases:
- Una string de nombre de clave
- un ID numérico entero
- Una ruta principal opcional que ubica la entidad dentro de la jerarquía de Datastore
Una aplicación puede recuperar una entidad individual de Datastore mediante la clave de la entidad, o una o más entidades si emite una consulta basada en los valores de propiedad o las claves de las entidades.
El SDK de App Engine para Java incluye una API simple, proporcionada en el paquete com.google.appengine.api.datastore
, que admite las funciones de Datastore. Todos los ejemplos de este documento se basan en esta API de bajo nivel; puedes elegir usarla directamente en tu aplicación o como la base sobre la que crear tu propia capa de administración de datos.
Datastore no aplica ninguna restricción en las estructuras de las entidades. Por ejemplo, si una propiedad posee un valor de un tipo en particular, esta tarea le corresponde a la aplicación.
Identificadores y tipos
Cada entidad de Datastore es de un tipo en particular que clasifica la entidad para realizar consultas; por ejemplo, una aplicación de recursos humanos podría representar a cada empleado de una empresa mediante una entidad del tipo Employee
. En la API de Datastore para Java, debes especificar un tipo de entidad cuando la creas, como un argumento en el constructor de Entity()
. Todos los nombres de tipos que comienzan con dos guiones bajos (__
) están reservados y no se pueden usar.
En el siguiente ejemplo, se crea una entidad de tipo Employee
, se propagan sus valores de propiedad y se la guarda en Datastore:
Además de un tipo, cada entidad posee un identificador, que se le asigna cuando se crea la entidad. Debido a que es parte de la clave de la entidad, el identificador se encuentra asociado de forma permanente a la entidad y no se puede cambiar. Se puede asignar de dos maneras:
- Tu aplicación puede especificar su propia string de nombre de clave para la entidad.
- Puedes hacer que Datastore asigne de forma automática un ID de número entero a la entidad.
Si quieres asignarle un nombre de clave a la entidad, proporciona el nombre como el segundo argumento para el constructor cuando crees la entidad:
Para que Datastore asigne un ID numérico de forma automática, omite el siguiente argumento:
Asigna identificadores
Datastore se puede configurar para generar ID de forma automática mediante dos políticas de identificación automática diferentes:
- La política
default
genera una secuencia aleatoria de ID sin usar con una distribución bastante uniforme. Cada ID puede tener hasta 16 dígitos decimales. - La política
legacy
crea una secuencia de ID de números enteros no consecutivos más breves.
Si quieres mostrar al usuario los ID de la entidad o depender de su pedido, lo mejor que puedes hacer es usar la asignación manual.
Datastore genera una secuencia aleatoria de ID sin usar con una distribución bastante uniforme. Cada ID puede tener hasta 16 dígitos decimales.
Rutas de principales
Las entidades de Cloud Datastore conforman un espacio con estructura jerárquica, similar a la estructura de directorios de un sistema de archivos. Cuando creas una entidad, tienes la opción de designar otra entidad como superior, en cuyo caso la nueva entidad es secundaria respecto de la entidad superior (ten en cuenta que, a diferencia de lo que ocurre en un sistema de archivos, no es necesario que efectivamente exista una entidad superior). Las entidades que no tienen una principal se denominan entidades raíz. La asociación entre una entidad secundaria y la entidad superior es permanente, y no puede cambiarse una vez creada la entidad. Cloud Datastore nunca asignará el mismo ID numérico a dos entidades asociadas a la misma entidad superior ni a dos entidades raíz (no asociadas a una entidad superior).
La entidad superior de una entidad determinada, la superior de la superior y así sucesivamente son sus principales. Las entidades secundarias respecto de una entidad determinada, las secundarias de las secundarias y así sucesivamente son sus descendientes. Una entidad raíz y todos sus descendientes pertenecen al mismo grupo de entidad. La secuencia de entidades que comienza con una entidad raíz y sigue de principal a secundaria, hasta llegar a una entidad determinada, constituye la ruta de principal de esa entidad. La clave completa que identifica una entidad consta de una secuencia de pares de tipo-identificador que especifican la ruta de principales y terminan con el tipo y el identificador de la propia entidad:
[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]
En el caso de una entidad raíz, la ruta de principales está vacía, y la clave consta solo del tipo y el identificador de la entidad:
[Person:GreatGrandpa]
Este concepto se ilustra en el diagrama siguiente:
Para designar la entidad principal respecto de una secundaria, proporciona la clave de la entidad principal como argumento al constructor Entity()
cuando creas la entidad secundaria. Para obtener la clave, llama al método getKey()
de la entidad principal:
Si la entidad nueva también tiene un nombre de clave, proporciona el nombre de clave como el segundo argumento al constructor Entity()
y la clave de la entidad principal como tercer argumento:
Transacciones y grupos de entidades
Cada intento de crear, actualizar o borrar una entidad ocurre en el contexto de una transacción. Una transacción puede incluir una cantidad indeterminada de las operaciones mencionadas. Para conservar la coherencia de los datos, la transacción garantiza que todas las operaciones que contiene se apliquen a Datastore como una unidad o, si alguna de ellas falla, que no se aplique ninguna. Además, todas las operaciones de lectura de coherencia sólida (consultas principales o get) que se realizan dentro de la misma transacción ven una sola instantánea coherente de los datos.
Como se mencionó antes, un grupo de entidades es un conjunto de entidades conectadas de manera ascendente a un elemento raíz común. La organización de los datos en grupos de entidades puede limitar las transacciones que se pueden realizar:
- Todos los datos a los que accede una transacción deben estar contenidos en 25 grupos de entidades como máximo.
- Si quieres usar consultas dentro de una transacción, los datos deben estar organizados en grupos de entidades de manera tal que puedas especificar filtros de principales que coincidan con los datos correctos.
- Existe un límite de capacidad de procesamiento de escrituras de alrededor de una transacción por segundo dentro de un mismo grupo de entidades. Esta limitación existe porque Datastore realiza una replicación síncrona sin instancia maestra de cada grupo de entidades en un área geográfica amplia para brindar alta confiabilidad y tolerancia a errores.
En muchas aplicaciones, es aceptable usar la coherencia eventual (es decir, una consulta no principal que abarque varios grupos de entidades y que a veces puede mostrar datos ligeramente inactivos) cuando se obtiene una vista amplia de datos no relacionados y, luego, usar la coherencia sólida (una consulta principal o una get
de una sola entidad) cuando se visualiza o edita un solo conjunto de datos muy relacionados. En tales aplicaciones, suele ser una buena estrategia usar un grupo de entidades separado para cada conjunto de datos sumamente relacionados.
Si deseas obtener más información, consulta Estructura datos para lograr una coherencia sólida.
Tipos de valores y propiedades
Los valores de datos asociados con una entidad constan de una o más propiedades. Cada propiedad tiene un nombre y uno o más valores. Una propiedad puede tener valores de más de un tipo, y dos entidades pueden tener valores de diferentes tipos para la misma propiedad. Las propiedades pueden o no estar indexadas (las consultas que ordenan o filtran en una propiedad P ignorarán las entidades en las que P no esté indexada). Una entidad puede tener como máximo 20,000 propiedades indexadas.
Se admiten los tipos de valor siguientes:
Tipo de valor | Tipo(s) de Java | Ordenar por | Notas |
---|---|---|---|
Número entero | short int long java.lang.Short java.lang.Integer java.lang.Long |
Numérico | Se almacena como un número entero largo y, luego, se convierte al tipo de campo Los valores fuera de rango se superponen |
Número de punto flotante | float double java.lang.Float java.lang.Double |
Numérico | Precisión doble de 64 bits, IEEE 754 |
Booleano | boolean java.lang.Boolean |
false <true |
|
String de texto (corta) | java.lang.String |
Unicode | Hasta 1,500 bytes Valores superiores a 1,500 bytes IllegalArgumentException |
String de texto (larga) | com.google.appengine.api.datastore.Text |
Ninguno | Hasta 1 megabyte Sin indexar |
String de bytes (corta) | com.google.appengine.api.datastore.ShortBlob |
Orden de bytes | Hasta 1,500 bytes Valores más largos que 1,500 bytes IllegalArgumentException |
String de bytes (larga) | com.google.appengine.api.datastore.Blob |
Ninguno | Hasta 1 megabyte Sin indexar |
Fecha y hora | java.util.Date |
Cronológico | |
Punto geográfico | com.google.appengine.api.datastore.GeoPt |
Por latitud, luego por longitud |
|
Dirección postal | com.google.appengine.api.datastore.PostalAddress |
Unicode | |
Número de teléfono | com.google.appengine.api.datastore.PhoneNumber |
Unicode | |
Dirección de correo electrónico | com.google.appengine.api.datastore.Email |
Unicode | |
Usuario de Cuentas de Google | com.google.appengine.api.users.User |
Dirección de correo electrónico en orden Unicode |
|
Controlador de mensajería instantánea | com.google.appengine.api.datastore.IMHandle |
Unicode | |
Vínculo | com.google.appengine.api.datastore.Link |
Unicode | |
Categoría | com.google.appengine.api.datastore.Category |
Unicode | |
Calificación | com.google.appengine.api.datastore.Rating |
Numérico | |
Clave de Datastore | com.google.appengine.api.datastore.Key o el objeto referenciado (como secundario) |
Por elementos de ruta de acceso (tipo, identificador, tipo, identificador…) |
Hasta 1,500 bytes Valores más largos que 1,500 bytes IllegalArgumentException |
Clave de Blobstore | com.google.appengine.api.blobstore.BlobKey |
Orden de bytes | |
Entidad incorporada | com.google.appengine.api.datastore.EmbeddedEntity |
Ninguno | No indexada |
Nulo | null |
Ninguna |
Importante: Se recomienda evitar almacenar users.User
como valor de propiedad porque incluye la dirección de correo electrónico junto con el ID único. Si un usuario cambia su dirección de correo electrónico y la comparas su user.User
anterior almacenado con el valor de user.User
nuevo, no coincidirán. En su lugar, usa el valor de ID del usuario User
como el identificador único estable del usuario.
Para strings de texto y datos binarios sin codificación (strings de bytes), Datastore es compatible con dos tipos de valores:
- Las strings cortas (hasta 1,500 bytes) se indexan y pueden usarse en condiciones de filtros de consultas y para ordenar elementos.
- Las strings largas (de hasta 1 megabyte) no se indexan ni pueden usarse en filtros de consultas ni para ordenar elementos.
Blob
en la API de Datastore. Este tipo no está relacionado con los BLOB, como se usa en la API de Blobstore.
Cuando una consulta incluye una propiedad con valores de varios tipos, Datastore usa un orden determinista basado en representaciones internas:
- Valores nulos
- Números de coma fija
- Números enteros
- Fechas y horas
- Calificaciones
- Valores booleanos
- Secuencias de bytes
- String de bytes
- String de Unicode
- Claves de Blobstore
- Números de coma flotante
- Puntos geográficos
- Usuarios de Cuentas de Google
- Claves de Datastore
Debido a que las strings de texto largas, las strings de bytes largas y las entidades incorporadas no se indexan, no tienen orden definido.
Trabaja con entidades
Las aplicaciones pueden usar la API de Datastore para crear, recuperar, actualizar y borrar entidades. Si la aplicación conoce la clave completa de una entidad (o puede obtenerla del tipo, identificador o clave de la superior), puede usar la clave para operar directamente con la entidad. Una aplicación también puede obtener la clave de una entidad como resultado de una consulta de Datastore. Visita la página Consultas de Datastore para obtener más información.
La API de Datastore para Java usa métodos de la interfaz de DatastoreService
a fin de operar en entidades. A fin de obtener un objeto DatastoreService
, llama al método estático DatastoreServiceFactory.getDatastoreService()
:
Crea una entidad
Puedes crear una entidad nueva si construyes una instancia de la clase Entity
y proporcionas el tipo de entidad como un argumento para el constructor Entity()
.
Después de propagar las propiedades de la entidad, si es necesario, guárdala en el almacén de datos como un argumento para el método DatastoreService.put()
. A fin de especificar el nombre clave de la entidad, pásala como segundo argumento al constructor:
Si no proporcionas un nombre de clave, Datastore generará de forma automática un ID numérico para la clave de la entidad:
Cómo recuperar una entidad
Para recuperar una entidad identificada por una clave determinada, pasa el objeto Key
al método DatastoreService.get()
:
Actualiza una entidad
Para actualizar una entidad existente, modifica los atributos del objeto Entity y, luego, pásalo al método DatastoreService.put()
. Los datos del objeto reemplazan la entidad existente. El objeto completo se envía a Datastore con cada llamada a put()
.
Borra una entidad
Dada la clave de una entidad, puedes borrar la entidad con el método DatastoreService.delete()
:
Propiedades repetidas
Puedes almacenar varios valores dentro de una sola propiedad.
Entidades incorporadas
A veces puede resultar práctico incorporar una entidad como una propiedad de otra entidad. Esto puede ser útil, por ejemplo, para crear una estructura jerárquica de valores de propiedad dentro de una entidad. La clase EmbeddedEntity
de Java te permite realizar las siguientes acciones:
Cuando una entidad incorporada se incluye en los índices, puedes realizar consultas sobre subpropiedades. Si excluyes una entidad incorporada de la indexación, también se excluirán todas las subpropiedades. Opcionalmente, puedes asociar una clave con una entidad incorporada, pero (a diferencia de una entidad completa) no es necesaria la clave e, incluso si existe, no se puede usar para recuperar la entidad.
En lugar de propagar las propiedades de la entidad incorporada, puedes usar el método setPropertiesFrom()
para copiarlas desde una entidad existente:
Más adelante, puedes usar el mismo método para recuperar la entidad original de la entidad incorporada:
Operaciones por lotes
Los métodos de DatastoreService
put()
, get()
y delete()
(y sus equivalentes de AsyncDatastoreService) tiene versiones por lotes que aceptan un objeto iterable (de la clase Entity
para put()
, Key
para get()
y delete()
) y lo usan a fin de operar en varias entidades en una sola llamada a Datastore:
Estas operaciones de lote agrupan todas las entidades o claves por grupo de entidad y luego realizan la operación solicitada en cada grupo de entidad en paralelo. Hacer llamadas por lotes es más rápido que hacer llamadas por separado para cada entidad individual porque estas generan sobrecarga para solo una llamada de servicio. Si hay varios grupos de entidades, el trabajo de todos ellos se realiza en paralelo del lado del servidor.
Genera claves
Las aplicaciones pueden usar la clase KeyFactory
a fin de crear un objeto Key
para una entidad de componentes conocidos, como el identificador y el tipo de entidad. Para una entidad sin superior, pasa el tipo y el identificador (ya sea un ID numérico o una string de nombre de clave) al método estático KeyFactory.createKey()
a fin de crear la clave. En los ejemplos siguientes, se crea una clave para una entidad del tipo Person
con el nombre de clave "GreatGrandpa"
o el ID numérico 74219
:
Si la clave incluye un componente de ruta de acceso, puedes usar la clase auxiliar KeyFactory.Builder
para crear la ruta. El método addChild
de esta clase agrega una sola entidad a la ruta de acceso y muestra el compilador, para que puedas encadenar una serie de llamadas a partir de la entidad raíz, y así crear la ruta de acceso de una entidad a la vez. Después de compilar toda la ruta de acceso, llama a getKey
para recuperar la clave resultante de la siguiente manera:
La clase KeyFactory
también incluye los métodos estáticos keyToString
y stringToKey
para la conversión entre claves y sus representaciones de strings:
La representación de string de una clave es "segura para la Web": no contiene caracteres que se consideren especiales en HTML o en URLs.
Usa una lista vacía
Históricamente, Datastore no tenía una representación para una propiedad que represente una lista vacía. El SDK de Java solucionó esto almacenando colecciones vacías como valores nulos, por lo que no hay manera de distinguir entre valores nulos y listas vacías. Para mantener la retrocompatibilidad, este sigue siendo el comportamiento predeterminado, que se resume de la siguiente manera:- Las propiedades nulas se escriben como nulas en Datastore.
- Las colecciones vacías se escriben como nulas en Datastore.
- Un nulo se lee como nulo desde Datastore.
- Una colección vacía se lee como nula.
Sin embargo, si modificas el comportamiento predeterminado, el SDK para Java será compatible con el almacenamiento de listas vacías. Recomendamos que consideres las consecuencias de modificar el comportamiento predeterminado de tu aplicación y luego actives la asistencia para listas vacías.
Para cambiar el comportamiento predeterminado a fin de que puedas usar listas vacías, configura la propiedad DATASTORE_EMPTY_LIST_SUPPORT durante la inicialización de tu app, de la siguiente manera:
System.setProperty(DatastoreServiceConfig.DATASTORE_EMPTY_LIST_SUPPORT, Boolean.TRUE.toString());
Con esta propiedad configurada en true
como se muestra arriba, ocurre lo siguiente:
- Las propiedades nulas se escriben como nulas en Datastore.
- Las colecciones vacías se escriben como una lista vacía en Datastore.
- Un nulo se lee como nulo desde Datastore.
- Cuando se lee desde Datastore, se muestra una lista vacía como una Colección vacía.