update tutorial google java google-app-engine google-cloud-datastore nosql

java - update - google cloud datastore tutorial



Errores de App Engine al intentar insertar muchas entidades en bloque con la condiciĆ³n de que la propiedad "UserNo" aumente en 1 cada vez que se inserte una entidad (1)

Quiero insertar muchas entidades de usuario a granel. La entidad de usuario tiene propiedad Nombre y UsuarioNo . El requisito es que la propiedad UserNo aumentará automáticamente en 1 cada vez que se inserte una entidad User.

El siguiente código funciona bien si intento insertar 1 registro a la vez.

public void insertUser(String name){ Entity userEntity=new Entity("User"); long maxID=Utility.getPropertyMaxID("User", "UserNo")+1; userEntity.setProperty("Name",name); datastore.put(userEntity); } public static long getPropertyMaxID(String entityName, String propertyName){ // Order alphabetically by property name: Query q = new Query(entityName) .addSort(propertyName, SortDirection.DESCENDING); List<Entity> results = datastore.prepare(q) .asList(FetchOptions.Builder.withDefaults()); if(results.size()>0){ Entity e=results.get(0); long maxID=(Long)e.getProperty(propertyName); return maxID; } else{ return 0; } }

Sin embargo, si intento insertar muchas entidades a la vez

for(int i=0; i<10; i++){ insertUser(names[i]); }

Luego, al verificar los datos, puedo ver que el nombre se insertó correctamente. Sin embargo, el UserNo se equivocó ya que no aumentó de 1 en 1 correctamente.

Por lo tanto, creo que esta podría ser la " consistencia eventual " del almacén de datos de Google App Engine porque los datos tardan en insertarse, por lo que el programa puede tomar el maxUserNo incorrectamente.

¿Entonces qué debo hacer? ¿No debería no usar " UserNo "?

Intenté la Transacción, pero no funcionó de todos modos

public void insertUser(String name){ Transaction tx=datastore.beginTransaction(); try{ Entity userEntity=new Entity("User"); long maxID=Utility.getPropertyMaxID("User", "UserNo")+1; userEntity.setProperty("Name",name); datastore.put(userEntity); tx.commit();} catch (Exception ex){ ex.printStackTrace()}; }

Entonces, ¿cuál es la mejor solución para esto?

Nota: Escuché que " las consultas no ancestrales siempre son consistentes ". ASÍ QUE la consulta dentro del método getPropertyMaxID no es muy consistente.

Extra : ¿qué tal si hago una Persona falsa y obligo a todos los usuarios a ser hijos de esa persona y luego realizo la consulta de Ancestro a todos los usuarios? Bucle a través de todos los usuarios para consultar el correo electrónico, por ejemplo? Vea este código:

Entity personEntity=new Entity("Person", "Tom"); Key personKey=personEntity.getKey(); Entity userEntity=new Entity("User", userName); userEntity.setProperty("Email", email); datastore.put(userEntity);

Luego haga una función para verificar el correo electrónico único

public static boolean checkUniqueEmail(String email){ Entity personEntity = new Entity("Person", "Tom"); Key personKey = personEntity.getKey(); Query query = new Query("User") .setAncestor(personKey); List<Entity> results = datastore.prepare(query) .asList(FetchOptions.Builder.withDefaults()); for(Entity e : results){ String em=(String)e.getProperty("Email"); if(e.equals(email)) return false; } }


Encontré la respuesta

Ver este enlace

Para comprender cómo estructurar sus datos para una mayor coherencia, compare dos enfoques diferentes para la aplicación de ejemplo de libro de visitas del ejercicio de Inicio de App Engine. El primer enfoque crea una nueva entidad raíz para cada saludo:

import com.google.appengine.api.datastore.Entity; Entity greeting = new Entity("Greeting"); // No parent key specified, so Greeting is a root entity. greeting.setProperty("user", user); greeting.setProperty("date", date); greeting.setProperty("content", content);

A continuación, consulta sobre el Saludo tipo de entidad para los diez saludos más recientes.

import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.appengine.api.datastore.Entity; DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Query query = new Query("Greeting") .addSort("date", Query.SortDirection.DESCENDING); List<Entity> greetings = datastore.prepare(query) .asList(FetchOptions.Builder.withLimit(10));

Sin embargo, debido a que estamos utilizando una consulta que no es ancestro, la réplica utilizada para realizar la consulta en este esquema puede no haber visto el nuevo saludo en el momento en que se ejecuta la consulta. No obstante, casi todas las escrituras estarán disponibles para consultas que no sean ancestros dentro de unos segundos de la confirmación. Para muchas aplicaciones, una solución que proporciona los resultados de una consulta que no es antepasado en el contexto de los propios cambios del usuario actual será suficiente para hacer que dichas latencias de replicación sean completamente aceptables.

Si la coherencia fuerte es importante para su aplicación, un enfoque alternativo es escribir entidades con una ruta de ancestro que identifique la misma entidad raíz en todas las entidades que deben leerse en una única consulta de antecesor fuertemente consistente:

import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.appengine.api.datastore.Entity; String guestbookName = req.getParameter("guestbookName"); Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName); String content = req.getParameter("content"); Date date = new Date(); // Place greeting in same entity group as guestbook Entity greeting = new Entity("Greeting", guestbookKey); greeting.setProperty("user", user); greeting.setProperty("date", date); greeting.setProperty("content", content); You will then be able to perform a strongly-consistent ancestor query within the entity group identified by the common root entity: import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.appengine.api.datastore.Entity; DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName); Query query = new Query("Greeting", guestbookKey) .setAncestor(guestbookKey) .addSort("date", Query.SortDirection.DESCENDING); List<Entity> greetings = datastore.prepare(query) .asList(FetchOptions.Builder.withLimit(10));

Este enfoque logra una gran consistencia al escribir en un solo grupo de entidades por libro de visitas, pero también limita los cambios en el libro de visitas a no más de 1 escritura por segundo (el límite admitido para grupos de entidades). Si es probable que su aplicación experimente un uso de escritura más pesado, es posible que deba considerar el uso de otros medios: por ejemplo, puede poner publicaciones recientes en un Memcache con vencimiento y mostrar una mezcla de publicaciones recientes de Memcache y Datastore, o podría guardarlos en una cookie, poner un estado en la URL, o algo completamente distinto. El objetivo es encontrar una solución de almacenamiento en caché que proporcione los datos para el usuario actual durante el período de tiempo en el que el usuario está publicando en su aplicación. Recuerde, si realiza una consulta get, ancestro o cualquier operación dentro de una transacción, siempre verá los datos escritos más recientes.