Está en la página 1de 95

Introduccin Te damos la bienvenida a Google App Engine.

Crear una aplicacin de App Engine es fcil y solo tardars unos minutos. Adems, empezar es gratis: sube tu aplicacin y comprtela con los usuarios inmediatamente, sin ningn tipo de coste ni compromiso. Las aplicaciones de Google App Engine pueden estar escritas en los lenguajes de programacin Java o Python. Este tutorial trata el lenguaje de programacin Java. Si, por el contrario, utilizas con ms frecuencia Python para crear tus aplicaciones, consulta Introduccin: Python. En este tutorial aprenders a: crear una aplicacin de App Engine mediante tecnologas web Java estndar, como servlets y pginas JSP, crear un proyecto Java App Engine con y sin Eclipse, utilizar el complemento de Google para Eclipse para el desarrollo de App Engine, utilizar el almacn de datos de App Engine mediante la interfaz estndar de objetos de datos Java (JDO), integrar una aplicacin App Engine en el servicio Google Accounts para la autenticacin del usuario, subir tu aplicacin a App Engine. Al finalizar este tutorial, habrs implementado una aplicacin activa: un libro de visitas sencillo que permite a los usuarios publicar mensajes en un tabln de mensajes pblico. Siguiente... Para empezar a desarrollar las aplicaciones Java Google App Engine, descarga y configura el kit de desarrollo de software Java de App Engine y los componentes relacionados. Para continuar, consulta Instalacin del SDK de Java.

Instalacin del SDK de Java Puedes desarrollar y subir aplicaciones Google App Engine Java con el kit de desarrollo de software (SDK) Java de App Engine. El SDK incluye software para un servidor web que puedes ejecutar en tu propio equipo con el fin de probar las aplicaciones Java. El servidor simula todos los servicios de App Engine, incluida una versin local del almacn de datos, el servicio Google Accounts y la capacidad de extraer direcciones URL y de enviar mensajes de correo electrnico desde tu equipo a travs de las API de App Engine. Obtencin de JavaE Google App Engine es compatible con Java 5 y con Java 6. Cuando la aplicacin Java se ejecuta en App Engine, lo hace a travs de la mquina virtual de Java (JVM) 6 y de las bibliotecas estndar. Lo ideal sera que utilizaras Java 6 para compilar y para probar la aplicacin, y que te aseguraras de que el servidor local se comporta de forma similar a App Engine. Para los desarrolladores que no puedan acceder fcilmente a Java 6 (como, por ejemplo, los que utilizan Mac OS X), el SDK de App Engine es compatible con Java 5. Puedes subir a App Engine clases compiladas y archivos JAR creados con Java 5. Si es necesario, descarga e instala el kit de desarrollo de Java SE (JDK) para tu plataforma. Los usuarios de Mac pueden consultar el sitio de desarrolladores Java de Apple y descargar e instalar la ltima versin del kit de desarrollo de Java disponible para Mac OS X. Una vez instalado el JDK, ejecuta los siguientes comandos desde una lnea de comandos (en Windows, desde el smbolo del sistema; en Mac OS X, desde Terminal) para comprobar que se pueden ejecutar y para determinar la versin que est instalada. Si tienes instalado Java 6, estos comandos mostrarn un nmero de versin similar a 1.6.0. Si tienes instalado Java 5, el nmero de versin ser similar a 1.5.0. java -version javac -version Uso de Eclipse y del complemento de Google para Eclipse Si usas el entorno de desarrollo Eclipse, la forma ms fcil de desarrollar, de probar y de subir aplicaciones de App Engine es mediante el complemento de Google para Eclipse. Este complemento incluye todo lo necesario para crear, probar e implementar la aplicacin con Eclipse. El complemento est disponible para las versiones 3.3, 3.4 y 3.5 de Eclipse. Puedes instalarlo a travs de la funcin de actualizacin de software de Eclipse. Las ubicaciones de la instalacin son las siguientes: Complemento de Google para la versin 3.3 de Eclipse (Europa): http://dl.google.com/eclipse/plugin/3.3 Complemento de Google para la versin 3.4 de Eclipse (Ganymede): http://dl.google.com/eclipse/plugin/3.4 Complemento de Google para la versin 3.5 de Eclipse (Galileo): http://dl.google.com/eclipse/plugin/3.5 Para obtener informacin detallada sobre cmo utilizar la funcin de actualizacin de software para instalar el complemento y cmo crear un proyecto nuevo, consulta Uso del complemento de Google para Eclipse. Obtencin del SDK Si utilizas Eclipse y el complemento de Google, puedes instalar el SDK de App Engine desde Eclipse mediante la funcin de actualizacin de software. Si an no lo tienes, instala el componente SDK de Java de Google App Engine a travs de las ubicaciones anteriores. Si no utilizas ni Eclipse ni el complemento de Google, puedes descargar el SDK Java de App Engine como un archivo ZIP.

Descarga el SDK Java de App Engine. Descomprime el archivo en una ubicacin adecuada de tu disco duro. Nota: cuando se descomprime el archivo, se crea un directorio cuyo nombre es similar a appengine-java-sdk-X.X.X, donde X.X.X es el nmero de versin del SDK. En este documento, nos referiremos a este directorio como appengine-java-sdk/. Una vez descomprimido el archivo, puedes cambiar el nombre del directorio. Aplicaciones de prueba El SDK Java de App Engine incluye varias aplicaciones de prueba en el directorio demos/. La versin final de la aplicacin de libro de visitas que crears con este tutorial se incluye en el directorio guestbook/. Esta aplicacin de prueba se ha compilado previamente para que puedas utilizarla de inmediato. Si utilizas Eclipse, el SDK se encuentra en el directorio de instalacin de Eclipse, en plugins/com.google.appengine.eclipse.sdkbundle_VERSION/, donde VERSION es un identificador de versin del SDK. Desde la lnea de comandos, cambia el directorio de trabajo actual por este otro directorio para ejecutar el siguiente comando. Si utilizas Mac OS X o Linux, puede que tengas que asignar permisos de ejecucin a los archivos de comandos para poder ejecutarlos (como ocurre con el comando chmod u+x dev_appserver.sh). Si utilizas Windows, inicia la demostracin del libro de visitas en el servidor de desarrollo mediante la ejecucin del siguiente comando en el smbolo del sistema: appengine-java-sdk\bin\dev_appserver.cmd appengine-java-sdk\demos\guestbook\war Si utilizas Mac OS X o Linux, ejecuta el siguiente comando: ./appengine-java-sdk/bin/dev_appserver.sh appengine-java-sdk/demos/guestbook/war El servidor de desarrollo se inicia y detecta las solicitudes en el puerto 8080. Accede a la siguiente URL mediante el navegador: http://localhost:8080/ Nota: si inicias el servidor de desarrollo desde Eclipse mediante el complemento de Google para Eclipse (tal como se explica ms adelante), el servidor utiliza el puerto 8888 de forma predeterminada: http://localhost:8888/. Para obtener ms informacin sobre la ejecucin del servidor web de desarrollo desde la lnea de comandos y de cmo cambiar el nmero de puerto que utiliza, consulta el documento de referencia del servidor web de desarrollo. Para detener el servidor, asegrate de que la ventana del smbolo del sistema est activa y, a continuacin, pulsa Control-C. Siguiente... El entorno de desarrollo te permite desarrollar y probar aplicaciones App Engine completas en tu equipo. Comencemos por un proyecto sencillo. Para continuar, consulta Creacin de un proyecto.

Creacin de un proyecto Las aplicaciones App Engine Java utilizan el estndar Java Servlet para interactuar con el entorno del servidor web. Los archivos de la aplicacin, junto con las clases compiladas, los archivos JAR, los archivos estticos y los archivos de configuracin, estn organizados en una estructura de directorio que utiliza el formato estndar WAR para aplicaciones web Java. Puedes utilizar el proceso de desarrollo que desees para desarrollar servlets web y para generar un directorio WAR. Los archivos del directorio WAR no son compatibles con el SDK. El directorio del proyecto En este tutorial se utilizar un nico directorio denominado Guestbook/ para todos los archivos del proyecto. Un subdirectorio llamado src/ contiene el cdigo fuente Java, mientras que otro subdirectorio llamado war/ contiene la aplicacin completa organizada en el formato WAR. El proceso de compilacin compila los archivos de cdigo fuente Java y coloca las clases compiladas en la ubicacin apropiada en war/. El directorio completo del proyecto es similar a: Guestbook/ src/ ...Java source code... META-INF/ ...other configuration... war/ ...JSPs, images, data files... WEB-INF/ ...app configuration... lib/ ...JARs for libraries... classes/ ...compiled classes...

Si utilizas Eclipse, crea un nuevo proyecto haciendo clic en el botn "New Web Application" (Nueva aplicacin web) en la barra de herramientas: Asigna al proyecto un nombre ("Project name"), por ejemplo, Guestbook, y un paquete ("Package"), por ejemplo, guestbook. Asegrate tambin de que la casilla "Use Google Web Toolkit" (Utilizar Google Web Toolkit) est desactivada y de que la casilla "Use Google App Engine" (Utilizar Google App Engine) est activada. Para obtener ms informacin, consulta Uso del complemento de Google para Eclipse. El asistente crea la estructura de directorios y los archivos tal y como se describe a continuacin. Si no utilizas Eclipse, crea la estructura de directorio descrita anteriormente. Mientras lees los archivos descritos en esta seccin, crea los archivos con los nombres y las ubicaciones especificados. Puedes tambin copiar la nueva plantilla del proyecto, que se incluye en el SDK, en el directorio appengine-javasdk/demos/new_project_template/. La clase Servlet Las aplicaciones App Engine Java utilizan el API Java Servlet para interactuar con el servidor web. Un servlet HTTP es una clase de aplicacin que puede procesar y responder solicitudes web. Esta clase ampla la clase javax.servlet.GenericServlet o la clase javax.servlet.http.HttpServlet. Nuestro proyecto del libro de visitas comienza con una clase servlet sencilla que muestra un mensaje. Si no utilizas el complemento de Eclipse, crea los directorios de la ruta src/guestbook/ y el archivo de clase servlet descrito a continuacin. El directorio src/guestbook/ incluye un archivo denominado GuestbookServlet.java que contiene lo siguiente: package guestbook; import java.io.IOException; import javax.servlet.http.*; public class GuestbookServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType("text/plain"); resp.getWriter().println("Hello, world"); } } El archivo web.xml Cuando el servidor web recibe una solicitud, decide qu clase servlet se invoca mediante un archivo de configuracin conocido como el descriptor de implementacin de la aplicacin web. Este archivo se denomina web.xml y se encuentra en el directorio war/WEB-INF/ del WAR. WEB-INF/ y web.xml forman parte de la especificacin del servlet. El directorio war/WEB-INF/ incluye un archivo denominado web.xml que contiene lo siguiente: <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5"> <servlet> <servlet-name>guestbook</servlet-name> <servlet-class>guestbook.GuestbookServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>guestbook</servlet-name> <url-pattern>/guestbook</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app> Este archivo web.xml declara un servlet denominado guestbook y lo asigna a la ruta de URL /guestbook. Especifica tambin que siempre que el usuario vaya en busca de una ruta de URL que no est ya asignada a un servlet y que represente una ruta del directorio dentro del WAR de la aplicacin, el servidor debe buscar un archivo denominado index.html en dicho directorio y suministrarlo si lo encuentra.

El archivo appengine-web.xml App Engine necesita un archivo de configuracin adicional para poder desarrollar y ejecutar la aplicacin. Este archivo se denomina appengine-web.xml y se encuentra en WEB-INF/ junto con web.xml. Incluye el ID registrado de la aplicacin (que Eclipse crea a partir de un ID vaco que rellenas ms adelante), el nmero de versin de la aplicacin y las listas de archivos que se deben tratar como archivos estticos (por ejemplo, las imgenes y las hojas de estilo CSS), as como archivos de recursos (por ejemplo, los archivos JSP y otros datos de aplicacin). El directorio war/WEB-INF/ incluye un archivo denominado appengine-web.xml que contiene lo siguiente: <?xml version="1.0" encoding="utf-8"?> <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> <application></application> <version>1</version> </appengine-web-app> appengine-web.xml es especfico de App Engine y no forma parte del estndar de servlet. En el directorio appengine-javasdk/docs/, puedes encontrar archivos de esquema XML que describen el formato de este archivo en el SDK. Para obtener ms informacin acerca de este archivo, consulta Configuracin de una aplicacin. Ejecucin del proyecto El SDK de App Engine incluye una aplicacin de servidor web que puedes utilizar para probar la aplicacin. El servidor simula los servicios y el entorno de App Engine, que incluyen restricciones en la zona de pruebas, el almacn de datos y los servicios. Si utilizas Eclipse, puedes iniciar el servidor de desarrollo en el depurador de Eclipse. Asegrate de seleccionar el proyecto ("Guestbook" o libro de visitas) y, a continuacin, en el men Run (Ejecutar) selecciona Debug As > Web Application (Depurar como > Aplicacin web). Para obtener ms informacin sobre cmo crear la configuracin de depuracin, consulta Uso del complemento de Google para Eclipse. Si no utilizas Eclipse, consulta Uso de Apache Ant para obtener una secuencia de comandos de compilacin que puede compilar el proyecto e iniciar el servidor de desarrollo. Para iniciar el servidor con esta secuencia de comandos de compilacin, introduce el siguiente comando: ant runserver. Para detener el servidor, pulsa Control-C. Realizacin de pruebas de la aplicacin Inicia el servidor y, a continuacin, accede a la URL del servidor mediante el navegador. Si utilizas Eclipse y el complemento de Google para Eclipse, el servidor se ejecuta de forma predeterminada a travs del puerto 8888: http://localhost:8888/guestbook Si utilizas el comando dev_appserver para iniciar el servidor, el puerto predeterminado es 8080: http://localhost:8080/guestbook En el resto del tutorial se asume que el servidor utiliza el puerto 8888. El servidor ejecuta el servlet y muestra el mensaje en el navegador. Siguiente... Enhorabuena, acabas de crear una aplicacin de App Engine. Puedes implementar esta sencilla aplicacin ahora mismo y compartirla con usuarios de todo el mundo. Esta aplicacin muestra un saludo general a todos los usuarios. Aadamos una funcin para personalizar el saludo para cada usuario que utilice Google Accounts. Para continuar, consulta Uso del servicio de usuarios.

Uso del servicio de usuarios Google App Engine ofrece varios servicios tiles basados en la infraestructura de Google a los que pueden acceder las aplicaciones mediante una serie de bibliotecas incluidas en el SDK. Uno de ellos es el servicio de usuarios, que te permite integrar la aplicacin con las cuentas de usuario de Google. Con el servicio de usuarios, los usuarios pueden utilizar sus cuentas de Google existentes para acceder a la aplicacin. Utilizaremos el servicio de usuarios para personalizar el saludo de esta aplicacin. Uso del servicio de usuarios Modifica src/guestbook/GuestbookServlet.java tal como se indica para que se parezca a lo que se muestra a continuacin:

package guestbook; import import import import import java.io.IOException; javax.servlet.http.*; com.google.appengine.api.users.User; com.google.appengine.api.users.UserService; com.google.appengine.api.users.UserServiceFactory;

public class GuestbookServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); if (user != null) { resp.setContentType("text/plain"); resp.getWriter().println("Hello, " + user.getNickname()); } else { resp.sendRedirect(userService.createLoginURL(req.getRequestURI())); } } } Si utilizas Eclipse y tu servidor de desarrollo se ejecuta en el depurador, cuando guardes los cambios realizados en este archivo, Eclipse compilar el nuevo cdigo de forma automtica y, a continuacin, intentar insertarlo en el servidor en ejecucin. Los cambios realizados en las clases, en las JSP, en los archivos estticos y en appengine-web.xml se reflejan de inmediato en el servidor en ejecucin sin necesidad de reiniciarlo. Si modificas web.xml o cualquier otro archivo de configuracin, debes detener e iniciar el servidor para ver los cambios. Si utilizas Ant, debes detener el servidor y volver a crear el proyecto para ver los cambios realizados en el cdigo fuente. Los cambios realizados en las JSP y en los archivos estticos no requieren que se reinicie el servidor. Vuelve a crear tu proyecto y reinicia el servidor en caso necesario. Para probar la aplicacin, consulta la URL del servlet en el navegador: http://localhost:8888/guestbook En lugar de mostrar el mensaje, el servidor te solicita una direccin de correo electrnico. Introduce una direccin de correo electrnico (como, por ejemplo, alfred@example.com) y, a continuacin, haz clic en el botn para acceder. La aplicacin muestra un mensaje que ahora contiene la direccin de correo electrnico que has introducido. El nuevo cdigo de la clase GuestbookServlet utiliza el API de usuarios para comprobar si el usuario ha accedido con una cuenta de Google. Si no es as, se redirecciona al usuario a la pantalla de acceso de Google Accounts. userService.createLoginURL(...) devuelve la URL de la pantalla de acceso. La funcin de acceso sabe cmo redireccionar al usuario a la aplicacin de nuevo gracias a la URL que se transfiere a createLoginURL(...) y que, en este caso, se trata de la URL de la pgina actual. El servidor de desarrollo sabe cmo simular la funcin de acceso a Google Accounts. Si se ejecuta en tu equipo local, se redirecciona a la pgina en la que puedes introducir una direccin de correo electrnico para simular un acceso a la cuenta. Si se ejecuta en App Engine, se redirecciona a la pantalla real de Google Accounts. Ahora has accedido a tu aplicacin de prueba. Si vuelves a cargar la pgina, el mensaje vuelve a aparecer. Para permitir que el usuario acceda, proporciona un enlace a la pantalla de acceso generado por el mtodo createLogoutURL(). Ten en cuenta que al hacer clic en un enlace de salida, el usuario sale de todos los servicios de Google. Siguiente... Ahora que sabemos cmo identificar al usuario, podemos invitarlo a publicar mensajes en el libro de visitas. Diseemos una interfaz de usuario para esta aplicacin mediante la tecnologa JavaServer Pages (JSP). Para continuar, consulta Uso de JSP.

Uso de JSP Aunque podramos generar el cdigo HTML para la interfaz de usuario directamente a partir del cdigo Java del servlet, sera algo difcil de mantener ya que el cdigo HTML se complica. Es ms conveniente utilizar un sistema de plantillas en el que la interfaz de usuario est diseada e implementada en archivos independientes con los marcadores y la lgica necesarios para insertar los datos proporcionados por la aplicacin. Existen muchos sistemas de plantillas disponibles para Java que podran ser compatibles con App Engine.

En este tutorial, utilizaremos la tecnologa JavaServer Pages (JSP) para implementar la interfaz de usuario del libro de visitas. Las JSP forman parte del estndar de servlet. App Engine compila los archivos JSP en el WAR de la aplicacin de forma automtica y los asigna a rutas de URL. Hola, JSP! La aplicacin de libro de visitas escribe cadenas en un flujo de salida, aunque esto tambin podra escribirse como una JSP. Empecemos trasladando la ltima versin del ejemplo a una JSP. En el directorio war/, crea un archivo llamado guestbook.jsp con el siguiente contenido: <%@ <%@ <%@ <%@ page page page page contentType="text/html;charset=UTF-8" language="java" %> import="com.google.appengine.api.users.User" %> import="com.google.appengine.api.users.UserService" %> import="com.google.appengine.api.users.UserServiceFactory" %>

<html> <body> <% UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); if (user != null) { %> <p>Hello, <%= user.getNickname() %>! (You can <a href="<%= userService.createLogoutURL(request.getRequestURI()) %>">sign out</a>.)</p> <% } else { %> <p>Hello! <a href="<%= userService.createLoginURL(request.getRequestURI()) %>">Sign in</a> to include your name with greetings you post.</p> <% } %> </body> </html> De forma predeterminada, cualquier archivo en war/ o en un subdirectorio (distinto de WEB-INF/) cuyo nombre termine en .jsp se asigna de forma automtica a una ruta de URL. La ruta de URL es la ruta al archivo .jsp, incluido el nombre de archivo. Esta JSP se asigna de forma automtica a la URL /guestbook.jsp. En la aplicacin de libro de visitas, queremos que esta sea la pgina principal de la aplicacin y que aparezca cuando un usuario acceda a la URL /. Una forma sencilla de hacer esto es declarar en web.xml que guestbook.jsp es el servlet de "bienvenida" (welcome) de esta ruta. Modifica war/WEB-INF/web.xml y reemplaza el elemento <welcome-file> actual de <welcome-file-list>. Asegrate de eliminar index.html de la lista, ya que los archivos estticos tienen preferencia sobre las JSP y sobre los servlets. <welcome-file-list> <welcome-file>guestbook.jsp</welcome-file> </welcome-file-list> Consejo: si utilizas Eclipse, el editor puede abrir este archivo en modo de "diseo". Para modificar este archivo como XML, selecciona la pestaa "Source" (Cdigo fuente) en la parte inferior del marco. Detn el servidor de desarrollo y, a continuacin, incialo. Consulta la siguiente URL: http://localhost:8888/ La aplicacin muestra el contenido de guestbook.jsp, incluido el apodo del usuario si este ha accedido. Cuando se carga una JSP por primera vez, el servidor de desarrollo la convierte en cdigo fuente Java y, a continuacin, compila este cdigo en cdigo de bytes de Java. El cdigo fuente Java y la clase compilada se guardan en un directorio temporal. El servidor de desarrollo vuelve a generar y a compilar las JSP de forma automtica si los archivos JSP originales se modifican. En el momento en que se sube la aplicacin a App Engine, el SDK compila todas las JSP en cdigo de bytes y sube nicamente el cdigo de bytes. Cuando la aplicacin se ejecuta en App Engine, utiliza las clases JSP compiladas. El formulario del libro de visitas

La aplicacin de libro de invitados necesita un formulario web para que el usuario publique un nuevo saludo, as como una for ma de procesar ese formulario. El cdigo HTML del formulario se inserta en la JSP. El destino del formulario es una nueva URL, /sign, que controla una nueva clase de servlet, SignGuestbookServlet. SignGuestbookServlet procesa el formulario y, a continuacin, redirecciona el navegador del usuario a /guestbook.jsp otra vez. Por el momento, el nuevo servlet nicamente escribe el mensaje publicado en el registro. Modifica guestbook.jsp y escribe las lneas que se muestran a continuacin justo antes de la etiqueta </body> de cierre: ... <form action="/sign" method="post"> <div><textarea name="content" rows="3" cols="60"></textarea></div> <div><input type="submit" value="Post Greeting" /></div> </form> </body> </html> Crea una nueva clase llamada SignGuestbookServlet en el paquete guestbook. (Aquellos usuarios que no utilicen Eclipse pueden crear el archivo SignGuestbookServlet.java en el directorio src/guestbook/). Asigna al archivo de origen el siguiente contenido: package guestbook; import import import import import import java.io.IOException; java.util.logging.Logger; javax.servlet.http.*; com.google.appengine.api.users.User; com.google.appengine.api.users.UserService; com.google.appengine.api.users.UserServiceFactory;

public class SignGuestbookServlet extends HttpServlet { private static final Logger log = Logger.getLogger(SignGuestbookServlet.class.getName()); public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); String content = req.getParameter("content"); if (content == null) { content = "(No greeting)"; } if (user != null) { log.info("Greeting posted by user " + user.getNickname() + ": " + content); } else { log.info("Greeting posted anonymously: " + content); } resp.sendRedirect("/guestbook.jsp"); } } Modifica war/WEB-INF/web.xml y aade las siguientes lneas para declarar el servlet SignGuestbookServlet y para asignarlo a la URL /sign: <web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5"> ... <servlet> <servlet-name>sign</servlet-name> <servlet-class>guestbook.SignGuestbookServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>sign</servlet-name>

<url-pattern>/sign</url-pattern> </servlet-mapping> ... </web-app> Este nuevo servlet utiliza la clase java.util.logging.Logger para escribir mensajes en el registro. Puedes controlar el comportamiento de esta clase a travs de un archivo logging.properties y de un conjunto de propiedades del sistema en el archivo appengine-web.xml de la aplicacin. Si utilizas Eclipse, tu aplicacin se cre con una versin predeterminada de este archivo en el directorio src/ de la aplicacin y con la propiedad del sistema adecuada. Si no utilizas Eclipse, debes configurar el archivo de configuracin del registrador de forma manual. Copia el archivo de ejemplo del SDK appengine-java-sdk/config/user/logging.properties en el directorio war/WEB-INF/ de la aplicacin. A continuacin, modifica el archivo war/WEB-INF/appengine-web.xml de la aplicacin tal como se indica: <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> ... <system-properties> <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/> </system-properties> </appengine-web-app> El servlet registra mensajes a travs de un nivel de registro INFO (para lo que utiliza log.info()). El nivel de registro predeterminado es WARNING, que suprime los mensajes INFO del resultado. Para cambiar el nivel de registro de todas las clases del paquete guestbook, modifica el archivo logging.properties y aade una entrada para guestbook.level, tal como se muestra a continuacin: .level = WARNING guestbook.level = INFO ... Consejo: si tu aplicacin registra mensajes a travs del API java.util.logging.Logger mientras se ejecuta en App Engine, App Engine registra los mensajes y los pone a tu disposicin para que los explores en la consola de administracin y para que los descargues mediante la herramienta AppCfg. La consola de administracin te permite explorar los mensajes por nivel de registro. Vuelve a compilar, reinicia y, a continuacin, prueba http://localhost:8888/. Se muestra el formulario. Introduce texto en el formulario y envalo. El navegador enva el formulario a la aplicacin y, a continuacin, se redirecciona al formulario vaco. El servidor registra los datos de saludo que introdujiste en la consola. Siguiente... Disponemos de una interfaz de usuario en la que se solicita al usuario que introduzca un saludo. Ahora se necesita una forma de recordar los saludos que los usuarios publican para mostrrselos a otros usuarios. Para ello, utilizaremos el almacn de datos de App Engine. Para continuar, consulta Uso del almacn de datos a travs de JDO.

Uso del almacn de datos a travs de JDO Almacenar datos en una aplicacin web escalable puede ser complicado. Un usuario podra estar interactuando con cualquiera de una docena de servidores web en un momento dado y la siguiente solicitud de ese usuario podra dirigirse a un servidor web distinto del servidor que haya gestionado la solicitud anterior. Todos los servidores web deben interactuar con datos que tambin estn distribuidos por docenas de equipos y que posiblemente se encuentran en distintas partes del mundo. Google App Engine soluciona todos estos problemas. La infraestructura de App Engine se encarga de todas las tareas de distribucin, replicacin y equilibrio de carga de los datos de un API sencilla, adems de ofrecer un potente motor de consulta y transacciones. El almacn de datos de App Engine es uno de los diversos servicios que ofrece App Engine con dos API: un API estndar y otra de nivel inferior. El uso de las API estndares facilita el traslado de las aplicaciones a otros entornos de alojamiento y a otras tecnologas de bases de datos, en caso necesario. Las API estndares "desconectan" la aplicacin de los servicios de App Engine. Los servicios de App Engine ofrecen tambin varias API de nivel inferior que muestran directamente las funciones del servicio. Puedes utilizar las API de nivel inferior para implementar nuevas interfaces de adaptadores o utilizarlas directamente en la aplicacin.

App Engine es compatible con dos estndares de API diferentes para el almacn de datos: Objetos de datos Java (JDO) y API de persistencia Java (JPA). DataNucleus Access Platform, una implementacin de cdigo abierto de varios estndares de persistencia Java que dispone de un adaptador para el almacn de datos de App Engine, proporciona estas interfaces. Para el libro de visitas, utilizaremos la interfaz JDO para la recuperacin y para la publicacin de los mensajes de los usuarios. Configuracin de DataNucleus Access Platform Access Platform necesita un archivo de configuracin que le indique que debe utilizar el almacn de datos de App Engine como servidor para la implementacin de JDO. En el WAR final, este archivo se denomina jdoconfig.xml y se encuentra en el directorio war/WEB-INF/classes/META-INF/. Si utilizas Eclipse, este archivo se crea como src/META-INF/jdoconfig.xml. Cuando creas el proyecto, el archivo se copia automticamente en war/WEB-INF/classes/META-INF/. Si no utilizas Eclipse, puedes crear el directorio war/WEB-INF/classes/META-INF/ directamente o dejar que se genere durante el proceso de compilacin y copiar el archivo de configuracin de otra ubicacin. La secuencia de comandos de compilacin Ant que se describe en Uso de Apache Ant copia este archivo de src/META-INF/. El archivo jdoconfig.xml debe contener lo siguiente: <?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> </persistence-manager-factory> </jdoconfig> Mejora de la clase JDO Cuando creas clases JDO, debes utilizar anotaciones Java para describir cmo se deben guardar las instancias en el almacn de datos y cmo se deben volver a crear al recuperarlas de dicho almacn. Access Platform conecta las clases de datos a la implementacin mediante un paso de procesamiento posterior a la compilacin que DataNucleus denomina "mejora" de las clases. Si utilizas Eclipse y el complemento de Google, dicho complemento realiza el paso de mejora de la clase JDO automticamente durante el proceso de compilacin. Si utilizas la secuencia de comandos de compilacin Ant que se describe en Uso de Apache Ant, dicha secuencia incluye el paso de mejora necesario. Para obtener ms informacin acerca de la mejora de la clase JDO, consulta Uso de JDO. POJO y anotaciones de JDO JDO permite almacenar objetos Java (a veces denominados "objetos Java antiguos y simples" o POJO) en cualquier almacn de datos con un adaptador compatible con JDO, como DataNucleus Access Platform. El SDK de App Engine incluye un complemento Access Platform para el almacn de datos de App Engine. Esto significa que puedes almacenar instancias de clases definidas en el almacn de datos de App Engine y recuperarlas como objetos mediante el API JDO. Puedes indicar a JDO cmo debe almacenar y reconstruir las instancias de tu clase a travs de anotaciones Java. Vamos a crear una clase Greeting para representar los mensajes individuales publicados en el libro de visitas. Crea una nueva clase llamada Greeting en el paquete guestbook. (Aquellos usuarios que no utilicen Eclipse pueden crear el archivo Greeting.java en el directorio src/guestbook/). Asigna al archivo de origen el siguiente contenido:

package guestbook; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.users.User; import import import import import java.util.Date; javax.jdo.annotations.IdGeneratorStrategy; javax.jdo.annotations.PersistenceCapable; javax.jdo.annotations.Persistent; javax.jdo.annotations.PrimaryKey;

@PersistenceCapable public class Greeting { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private User author; @Persistent private String content; @Persistent private Date date; public Greeting(User author, String content, Date date) { this.author = author; this.content = content; this.date = date; } public Key getKey() { return key; } public User getAuthor() { return author; } public String getContent() { return content; } public Date getDate() { return date; } public void setAuthor(User author) { this.author = author; } public void setContent(String content) { this.content = content; } public void setDate(Date date) { this.date = date; } } Esta sencilla clase define tres propiedades de un saludo author (autor), content (contenido) y date (fecha). Estos tres campos privados presentan la anotacin @Persistent, que indica a DataNucleus que debe almacenarlos como propiedades de los objetos en el almacn de datos de App Engine.

Esta clase define captadores y definidores de las propiedades que solo utiliza la aplicacin. Con el uso de definidores, puedes garantizar de forma sencilla que la implementacin de JDO reconozca las actualizaciones. La modificacin de los campos omite directamente la funcin de JDO que guarda los campos actualizados de forma automtica, a menos que realices otros cambios en el cdigo que habiliten esto. La clase tambin define un campo key (clave), es decir, una clase Key que presenta dos anotaciones: @Persistent y @PrimaryKey. El almacn de datos de App Engine tiene una nocin de las claves de entidades y puede representar las claves de varias formas en el objeto. La clase Key representa todos los aspectos de las claves del almacn de datos de App Engine, incluido un ID numrico que se define automticamente como un valor exclusivo cuando se guarda el objeto. Para obtener ms informacin acerca de las anotaciones de JDO, consulta Definicin de las clases de datos. La clase PersistenceManagerFactory Todas las solicitudes que utilizan el almacn de datos crean una nueva instancia de la clase PersistenceManager. Para ello, utilizan una instancia de la clase PersistenceManagerFactory. Una instancia de PersistenceManagerFactory tarda algn tiempo en inicializarse. Afortunadamente, solo se necesita una instancia para cada aplicacin, y esta instancia se puede almacenar en una variable esttica que pueden utilizar varias solicitudes y clases. Una forma sencilla de realizar este procedimiento es mediante la creacin de una clase envoltorio "singleton" para la instancia esttica. Crea una nueva clase denominada PMF en el paquete guestbook (un archivo denominado PMF.java en el directorio src/guestbook/) que contenga el siguiente contenido: package guestbook; import javax.jdo.JDOHelper; import javax.jdo.PersistenceManagerFactory; public final class PMF { private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional"); private PMF() {} public static PersistenceManagerFactory get() { return pmfInstance; } }

Creacin y almacenamiento de objetos Con DataNucleus y la clase Greeting, la lgica de procesamiento de formularios puede almacenar ahora nuevos saludos en el almacn de datos. Modifica src/guestbook/SignGuestbookServlet.java tal como se indica para que se parezca a lo que se muestra a continuacin: package guestbook; import import import import import import import import java.io.IOException; java.util.Date; java.util.logging.Logger; javax.jdo.PersistenceManager; javax.servlet.http.*; com.google.appengine.api.users.User; com.google.appengine.api.users.UserService; com.google.appengine.api.users.UserServiceFactory;

import guestbook.Greeting; import guestbook.PMF; public class SignGuestbookServlet extends HttpServlet { private static final Logger log = Logger.getLogger(SignGuestbookServlet.class.getName()); public void doPost(HttpServletRequest req, HttpServletResponse resp)

throws IOException { UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); String content = req.getParameter("content"); Date date = new Date(); Greeting greeting = new Greeting(user, content, date); PersistenceManager pm = PMF.get().getPersistenceManager(); try { pm.makePersistent(greeting); } finally { pm.close(); } resp.sendRedirect("/guestbook.jsp"); } } Este cdigo crea una nueva instancia Greeting mediante la invocacin del constructor. Para guardar la instancia en el almacn de datos, crea una clase PersistenceManager a travs de una clase PersistenceManagerFactory y, a continuacin, transmite la instancia al mtodo makePersistent() de PersistenceManager. Las anotaciones y la mejora del cdigo de bytes se toman de ah. Una vez que se obtiene makePersistent(), el nuevo objeto se guarda en el almacn de datos.

Consultas con JDOQL El estndar JDO define un mecanismo para las consultas de objetos persistentes denominado JDOQL. Puedes utilizar JDOQL para realizar consultas de entidades del almacn de datos de App Engine y para recuperar objetos con mejoras de la clase JDO. En este ejemplo, no nos complicaremos y escribiremos el cdigo de consulta directamente en guestbook.jsp. Si se tratara de una aplicacin de mayores dimensiones, la lgica de consulta se debera delegar en otra clase. Modifica war/guestbook.jsp y aade las lneas indicadas de forma que se parezca a lo que se muestra a continuacin: <%@ <%@ <%@ <%@ <%@ <%@ <%@ <%@ page page page page page page page page contentType="text/html;charset=UTF-8" language="java" %> import="java.util.List" %> import="javax.jdo.PersistenceManager" %> import="com.google.appengine.api.users.User" %> import="com.google.appengine.api.users.UserService" %> import="com.google.appengine.api.users.UserServiceFactory" %> import="guestbook.Greeting" %> import="guestbook.PMF" %>

<html> <body> <% UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); if (user != null) { %> <p>Hello, <%= user.getNickname() %>! (You can <a href="<%= userService.createLogoutURL(request.getRequestURI()) %>">sign out</a>.)</p> <% } else { %> <p>Hello! <a href="<%= userService.createLoginURL(request.getRequestURI()) %>">Sign in</a> to include your name with greetings you post.</p> <% } %> <% PersistenceManager pm = PMF.get().getPersistenceManager(); String query = "select from " + Greeting.class.getName(); List<Greeting> greetings = (List<Greeting>) pm.newQuery(query).execute();

if (greetings.isEmpty()) { %> <p>The guestbook has no messages.</p> <% } else { for (Greeting g : greetings) { if (g.getAuthor() == null) { %> <p>An anonymous person wrote:</p> <% } else { %> <p><b><%= g.getAuthor().getNickname() %></b> wrote:</p> <% } %> <blockquote><%= g.getContent() %></blockquote> <% } } pm.close(); %> <form action="/sign" method="post"> <div><textarea name="content" rows="3" cols="60"></textarea></div> <div><input type="submit" value="Post Greeting" /></div> </form> </body> </html>

Para preparar una consulta, debes ejecutar el mtodo newQuery() de una instancia PersistenceManager con el texto de la consulta como cadena. El mtodo devuelve un objeto de consulta. El mtodo execute() del objeto de consulta realiza la consulta y, a continuacin, muestra una lista List<> con objetos de resultado del tipo adecuado. La cadena de consulta debe incluir el nombre completo de la clase a la que se dirige la consulta, incluido el nombre del paquete. Vuelve a crear el proyecto y reinicia el servidor. Consulta http://localhost:8888/. Introduce un saludo y envalo. El saludo aparecer encima del formulario. Introduce otro saludo y envalo. Se mostrarn los dos saludos. Prueba a salir y a volver a acceder utilizando los enlaces; a continuacin, intenta enviar mensajes con la sesin iniciada y con la sesin cerrada. Consejo: en una aplicacin real, puede ser una buena idea utilizar caracteres de escape con los caracteres HTML cuando se muestra el contenido enviado por los usuarios como, por ejemplo, los saludos de esta aplicacin. JavaServer Pages Standard Tag Library (JSTL) incluye una serie de rutinas que permiten realizar este procedimiento. App Engine incluye JSTL (y otros archivos JAR de tiempo de ejecucin relacionados con JSP), as que no es necesario que los incluyas en la aplicacin. Busca la funcin escapeXml en la biblioteca de etiquetas disponible en http://java.sun.com/jsp/jstl/functions. Para obtener ms informacin, consulta el tutorial de J2EE 1.4 de Sun. Introduccin a JDOQL El libro de invitados muestra actualmente todos los mensajes que se han publicado en el sistema. Adems, los mensajes aparecen en el orden en que se crearon. Cuando el libro de invitados contenga muchos mensajes, es posible que resulte ms til mostrar solo los mensajes recientes y que aparezca en primer lugar el mensaje ms reciente. Para ello, podemos ajustar la consulta del almacn de datos. Puedes realizar una consulta con la interfaz JDO mediante JDOQL, un lenguaje de consulta parecido a SQL que permite recuperar objetos de datos. En la pgina JSP, la cadena de consulta JDOQL se define con la siguiente lnea: String query = "select from " + Greeting.class.getName(); Dicho de otro modo, la cadena de consulta JDOQL es la siguiente: select from guestbook.Greeting Esta consulta busca en el almacn de datos todas las instancias de la clase Greeting que se han guardado hasta el momento. Una consulta puede especificar el orden en que se deben mostrar los resultados en trminos de los valores de las propiedades. Para recuperar todos los objetos Greeting en el orden inverso al de su publicacin (es decir, del ms reciente al ms antiguo), se debera utilizar la siguiente consulta: select from guestbook.Greeting order by date desc

Se pueden realizar consultas que limiten tambin el nmero de resultados. Por ejemplo, para obtener solamente los cinco saludos ms recientes, se debera utilizar order by y range, tal como se muestra a continuacin: select from guestbook.Greeting order by date desc range 0,5 Prueba a realizar este procedimiento con la aplicacin del libro de visitas. En guestbook.jsp, sustituye la definicin de query por lo siguiente: String query = "select from " + Greeting.class.getName() + " order by date desc range 0,5"; Publica ms de cinco saludos en el libro de visitas. solo se muestran los cinco ms recientes en orden cronolgico inverso. Puedes obtener ms informacin acerca de las consultas y del lenguaje JDOQL en Consultas e ndices. Siguiente... Todas las aplicaciones web devuelven cdigo HTML generado de forma dinmica a partir del cdigo de la aplicacin mediante plantillas o algn otro mecanismo. La mayor parte de las aplicaciones web tambin necesitan publicar contenido esttico, como imgenes, hojas de estilo CSS o archivos JavaScript. Para una mayor eficiencia, App Engine no trata igual los archivos estticos que el cdigo fuente de la aplicacin y los archivos de datos. Vamos a crear ahora una hoja de estilo CSS para esta aplicacin que sea un archivo esttico. Para continuar, consulta Uso de archivos estticos.

Uso de archivos estticos Hay muchos casos en los que querrs mostrar los archivos estticos directamente en el navegador web. Las imgenes, las hojas de estilo CSS, el cdigo JavaScript, los vdeos y las animaciones Flash se suelen mostrar directamente en el navegador. Para una mayor eficiencia, App Engine muestra los archivos estticos desde servidores independientes en lugar de los que invocan servlets. De forma predeterminada, App Engine pone a tu disposicin todos los archivos del WAR como archivos estticos, salvo las JSP y los archivos de WEB-INF/. Cualquier solicitud de una URL cuya ruta coincida con un archivo esttico muestra el archivo directamente en el servidor, incluso si la ruta coincide tambin con una asignacin de filtro o de servlet. Puedes configurar los archivos que quieres que App Engine considere como archivos estticos a travs del archivo appengine-web.xml. Cambiemos la apariencia de nuestro libro de visitas con una hoja de estilo CSS. En este ejemplo, no modificaremos la configuracin de los archivos estticos. Para obtener ms informacin sobre la configuracin de archivos estticos y de archivos de recursos, consulta Configuracin de aplicaciones.

Una hoja de estilo sencilla En el directorio war/, crea un directorio llamado stylesheets/. En este directorio, crea un archivo llamado main.css con el siguiente contenido: body { font-family: Verdana, Helvetica, sans-serif; background-color: #FFFFCC; } Modifica war/guestbook.jsp e inserta las siguientes lneas justo despus de la lnea <html> en la parte superior: <html> <head> <link type="text/css" rel="stylesheet" href="/stylesheets/main.css" /> </head> <body> ... </body> </html> Consulta http://localhost:8888/. La nueva versin utiliza la hoja de estilo. Siguiente... Ha llegado la hora de mostrarle al mundo la versin definitiva de tu aplicacin. Para continuar, consulta Subida de aplicaciones.

Subida de aplicaciones Puedes crear y administrar aplicaciones en App Engine mediante la consola de administracin. Una vez que hayas registrado el ID de la aplicacin, sbela a App Engine mediante el complemento de Eclipse o mediante una herramienta de lnea de comandos del SDK. Nota: una vez que hayas registrado el ID de la aplicacin, podrs eliminarlo, pero no podrs volver a registrar este mismo ID. Puedes omitir los pasos siguientes si no deseas registrar un ID en estos momentos. Registro de la aplicacin Puedes crear y administrar aplicaciones web en App Engine con la consola de administracin de App Engine a travs de la siguiente URL: https://appengine.google.com/ Accede a App Engine con tu cuenta de Google. Si no tienes una cuenta de Google, puedes crear una con una direccin de correo electrnico y con una contrasea. Para crear una nueva aplicacin, haz clic en el botn "Crear una aplicacin". Sigue las instrucciones para registrar el ID de la aplicacin, es decir, el nombre exclusivo de esta aplicacin. Si decides utilizar el nombre de dominio gratuito appspot.com, la URL completa de la aplicacin ser http://application-id.appspot.com/. Tambin puedes comprar un nombre de dominio de nivel superior para la aplicacin o utilizar un nombre que ya tengas registrado. Modifica el archivo appengine-web.xml y, a continuacin, cambia el valor del elemento <application> de forma que sea el ID registrado de la aplicacin. Subida de la aplicacin Puedes subir la aplicacin a travs de Eclipse o de un comando del smbolo del sistema. Subida desde Eclipse Puedes subir el cdigo y los archivos de la aplicacin desde Eclipse a travs del complemento de Google. Para subir la aplicacin desde Eclipse, haz clic en el botn de implementacin de App Engine de la barra de herramientas: . Introduce el nombre de usuario (tu direccin de correo electrnico) y la contrasea de tu cuenta de Google cuando el sistema lo solicite y, a continuacin, haz clic en el botn Upload (Subir). Eclipse obtiene el ID de la aplicacin y la informacin sobre la versin a partir del archivo appengine-web.xml y sube el contenido del directorio war/. Subida a travs del smbolo del sistema Puedes subir el cdigo y los archivos de la aplicacin a travs de un comando del SDK llamado appcfg.cmd (Windows) o appcfg.sh (Mac OS X y Linux). AppCfg es una herramienta multiuso para interactuar con la aplicacin en App Engine. El comando toma el nombre de una accin, la ruta al directorio war/ de la aplicacin y otras opciones. Para subir el cdigo y los archivos de la aplicacin a App Engine, utiliza la accin update. Para subir la aplicacin en Windows: ..\appengine-java-sdk\bin\appcfg.cmd update war Para subir la aplicacin en Mac OS X o en Linux: ../appengine-java-sdk/bin/appcfg.sh update war Introduce el nombre de usuario y la contrasea de tu cuenta de Google cuando el sistema lo solicite. Acceso a la aplicacin Una vez hecho esto, podrs ver la aplicacin en ejecucin en App Engine. Si estableces un nombre de dominio gratuito appspot.com, la URL del sitio web empezar con el ID de la aplicacin: http://application-id.appspot.com/ Enhorabuena. Has terminado este tutorial. Para obtener ms informacin acerca de los temas que se han tratado aqu, consulta el resto de la documentacin de App Engine.

Aspectos generales de App Engine para Java


Te damos la bienvenida a Google App Engine para Java. Con App Engine, puedes crear aplicaciones web a travs de tecnologas estndar de Java y ejecutarlas en la infraestructura escalable de Google. El entorno de Java proporciona un JVM Java 6, una interfaz de servlets Java y la compatibilidad de interfaces estndar con los servicios y el almacn de datos escalable de App Engine como, por ejemplo, JDO, JPA, JavaMail y JCache. La compatibilidad con los estndares permite desarrollar tu aplicacin de forma fcil y familiar, as como trasladarla fcilmente hacia tu propio entorno de servlet y desde este. El complemento de Google para Eclipse aade asistentes para proyectos nuevos y configuraciones de depuracin a tu entorno integrado de desarrollo (IDE) de Eclipse para proyectos de App Engine. App Engine para Java facilita especialmente el desarrollo y la implementacin de aplicaciones web de gran calidad a travs de Google Web Toolkit (GWT). El complemento de Eclipse se suministra junto con los SDK de GWT y de App Engine. Los complementos de terceros tambin estn disponibles para otros IDE Java. Para NetBeans, consulta la compatibilidad de NetBeans con Google App Engine. Para IntelliJ, consulta la integracin de Google App Engine para IntelliJ. (Estos enlaces te llevan a los sitios web de terceros). Si an no lo has hecho, consulta la Gua de introduccin de Java que ofrece una presentacin interactiva del desarrollo de aplicaciones web con tecnologas Java y Google App Engine.

El entorno de tiempo de ejecucin Java App Engine ejecuta aplicaciones Java que utilizan el equipo virtual Java 6 (JVM). El SDK de App Engine es compatible con Java 5 y versiones posteriores, y JVM Java 6 puede utilizar clases compiladas con cualquier versin del compilador de Java hasta Java 6. App Engine utiliza el estndar Java Servlet para aplicaciones web. Proporcionas clases de servlets de tu aplicacin, pginas del servidor Java (JSP), archivos estticos y archivos de datos, junto con el descriptor de implementacin (el archivo web.xml) y otros archivos de configuracin en una estructura de directorio WAR estndar. App Engine muestra solicitudes a travs de la invocacin de servlets segn el descriptor de implementacin. El JVM se ejecuta en un entorno seguro de espacio aislado para aislar tu aplicacin por servicio y seguridad. El espacio aislado garantiza que la aplicacin solo pueda realizar acciones que no interfieran con el rendimiento ni con la escalabilidad de otras aplicaciones. Por ejemplo, una aplicacin no puede generar cadenas, escribir datos en el sistema de archivos local ni establecer conexiones de red arbitrarias. Una aplicacin tampoco puede utilizar JNI ni ningn otro cdigo nativo. El JVM puede ejecutar cualquier cdigo de bytes de Java que opere dentro de las restricciones del espacio aislado. Para obtener ms informacin, consulta Entorno de servlet.

El almacn de datos, los servicios y las interfaces estndar App Engine proporciona servicios escalables que pueden utilizar las aplicaciones para almacenar datos continuamente, acceder a recursos en la red y llevar a cabo otras tareas como, por ejemplo, administrar datos de imgenes. Siempre que sea posible, las interfaces Java para estos servicios se ajustan a las API estndar establecidas para poder trasladar aplicaciones hacia y desde App Engine. Adems, cada uno de los servicios proporciona una interfaz completa de nivel inferior para implementar nuevos adaptadores de interfaz o para acceder directamente. Las aplicaciones pueden utilizar el almacn de datos de App Engine para un almacenamiento de datos continuo, escalable y fiable. El almacn de datos admite dos interfaces Java estndar: los objetos de datos Java (JDO) 2.3 y el API Java de persistencia (JPA) 1.0. Estas interfaces se implementan a travs de DataNucleus Access Platform, la implementacin de cdigo abierto de estos estndares. Memcache de App Engine proporciona un almacenamiento en cach distribuido, transitorio y rpido de los resultados de clculos y consultas al almacn de datos. La interfaz Java implementa JCache (JSR 107). Las aplicaciones utilizan el servicio de extraccin de URL para acceder a recursos en la Web y para comunicarse con otros hosts que utilicen los protocolos HTTP y HTTPS. Las aplicaciones Java pueden utilizar simplemente java.net.URLConnection y clases relacionadas de la biblioteca estndar de Java para acceder a este servicio. Una aplicacin puede utilizar el servicio de correo para enviar mensajes de correo electrnico en nombre de los administradores de la aplicacin o del usuario actual. Las aplicaciones Java utilizan la interfaz JavaMail para enviar mensajes de correo electrnico.

El servicio de imgenes permite a las aplicaciones transformar y administrar datos de imgenes en varios formatos, entre los que se incluyen las funciones de recorte, rotacin, cambio de tamao y mejora del color de fotografa. El servicio puede gestionar tareas de procesamiento de imgenes que utilizan muchos recursos de CPU y deja ms recursos disponibles para que el servidor de la aplicacin administre solicitudes web. (Tambin puedes utilizar cualquier software de procesamiento de imgenes basado en JVM en el servidor de la aplicacin, siempre y cuando funcione dentro de las restricciones del espacio aislado). Una aplicacin puede utilizar Google Accounts para la autenticacin de usuarios. Google Accounts gestiona la creacin y el acceso de cuentas de usuario. Los usuarios que ya dispongan de una cuenta de Google (p. ej., una cuenta de Gmail) podrn utilizarla con tu aplicacin. Una aplicacin puede detectar cundo el usuario actual ha iniciado sesin y puede acceder a su direccin de correo electrnico. Las aplicaciones Java pueden utilizar restricciones de seguridad en el descriptor de implementacin para controlar el acceso a travs de Google Accounts, as como detectar si el usuario ha accedido y obtener la direccin de correo electrnico a travs del mtodo getUserPrincipal() del objeto de solicitud del servlet. Una aplicacin puede utilizar el API de nivel inferior de Google Accounts para generar URL de acceso y de salida, as como para obtener un objeto de datos de usuario adecuado para que se guarde en el almacn de datos.

Tareas programadas Una aplicacin puede configurar tareas programadas que invocan URL de la aplicacin segn los intervalos especificados. Para obtener ms informacin, consulta tareas cron. Herramientas Java El SDK Java de App Engine incluye herramientas para probar tu aplicacin, subir archivos de la aplicacin y descargar datos de registro. El SDK tambin incluye un componente para Apache Ant que permite simplificar tareas comunes en proyectos de App Engine. El complemento de Google para Eclipse aade funciones al IDE de Eclipse para desarrollar, probar e implementar App Engine y, adems, incluye el SDK completo de App Engine. El complemento de Eclipse tambin facilita el desarrollo de las aplicaciones de Google Web Toolkit y las ejecuta en App Engine. El servidor de desarrollo ejecuta la aplicacin en tu equipo local para desarrollarla y probarla. El servidor simula el almacn de datos, los servicios y las restricciones del espacio aislado de App Engine. El servidor de desarrollo tambin puede generar configuraciones para ndices de almacenes de datos basados en las consultas que realiza la aplicacin durante la prueba. Una herramienta multiuso llamada AppCfg gestiona toda la interaccin del smbolo del sistema mientras tu aplicacin se ejecuta en App Engine. AppCfg puede subir tu aplicacin a App Engine o simplemente actualizar la configuracin de ndices del almacn de datos para que puedas compilar nuevos ndices antes de actualizar el cdigo. Tambin puede descargar los datos de registro de la aplicacin para que puedas analizar el rendimiento de tu aplicacin mediante tus propias herramientas.

El entorno Java Servlet App Engine ejecuta tu aplicacin web Java a travs del JVM Java 6 en un entorno seguro de espacio aislado. App Engine invoca las clases de servlet de tu aplicacin para administrar solicitudes y preparar respuestas en este entorno. Seleccin de la versin del API Java Solicitudes y dominios Solicitudes y servlets Respuestas El periodo de tiempo de las solicitudes La zona de pruebas Accesos permitidos a la clase de JRE Acceso El entorno Cuotas y lmites

Seleccin de la versin del API Java App Engine sabe que debe utilizar el entorno de tiempo de ejecucin Java para tu aplicacin cuando utilizas la herramienta AppCfg del SDK Java para subir la aplicacin.

En el momento de la publicacin de este artculo, solo exista una versin del API Java de App Engine. Esta API se representa mediante appengine-api-*.jar incluido en el SDK (donde * representa la versin del API y del SDK). Selecciona la versin del API que utiliza tu aplicacin incluyendo este JAR en el directorio WEB-INF/lib/ de la aplicacin. Si se publica una nueva versin del entorno de tiempo de ejecucin Java que introduzca cambios no compatibles con las aplicaciones existentes, ese entorno dispondr de un nuevo nmero de versin. Tu aplicacin seguir utilizando la versin anterior hasta que sustituyas el JAR por la nueva versin (de un SDK ms nuevo) y vuelvas a subir la aplicacin. Solicitudes y dominios App Engine determina que una solicitud entrante est destinada a tu aplicacin a partir del nombre de dominio de la solicitud. Una solicitud cuyo nombre de dominio sea application-id.appspot.com se dirige a la aplicacin cuyo ID es application-id. Todas las aplicaciones obtienen un nombre de dominio appspot.com de forma gratuita. Los dominios appspot.com tambin son compatibles con subdominios que presentan el formato subdomain.applicationid.appspot.com, donde subdomain puede ser cualquier cadena permitida en una parte de un nombre de dominio (no .). Las solicitudes enviadas a un subdominio de esta forma se dirigen a tu aplicacin. Puedes configurar un dominio de nivel superior personalizado mediante Google Apps. Google Apps te permite asignar subdominios del dominio de tu empresa a distintas aplicaciones, como Gmail o Sites. Adems, puedes asociar una aplicacin App Engine a un subdominio. Para mayor comodidad, puedes configurar un dominio de Google Apps cuando registres tu ID de aplicacin o ms tarde desde la Consola del administrador. Consulta este artculo para obtener ms informacin. Las solicitudes de estas URL se dirigen todas a la versin de tu aplicacin que has seleccionado como versin predeterminada en la Consola del administrador. Cada versin de tu aplicacin tambin incluye su propia URL, de modo que puedes implementar y probar una nueva versin antes de establecerla como versin predeterminada. La URL especfica de la versin utiliza el identificador de versiones del archivo de configuracin de tu aplicacin adems del nombre de dominio appspot.com, siguiendo este patrn: versionid.latest.application-id.appspot.com. Tambin puedes utilizar subdominios con la URL especfica de la versin: subdomain.version-id.latest.application-id.appspot.com El nombre de dominio utilizado para la solicitud se incluye en los datos de la solicitud transmitidos a la aplicacin. Si quieres que tu aplicacin responda de forma diferente en funcin del nombre de dominio utilizado para acceder a ella (por ejemplo, restringir el acceso a determinados dominios o redireccionar a un dominio oficial), puedes comprobar los datos de la solicitud (como la cabecera de la solicitud Host) para el dominio desde dentro del cdigo de la aplicacin y responder adecuadamente. Solicitudes y servlets Cuando App Engine recibe una solicitud web para tu aplicacin, invoca el servlet correspondiente de la URL, tal y como se describe en el descriptor de implementacin (el archivo web.xml del directorio WEB-INF/). Utiliza el API Java Servlet para proporcionar los datos de la solicitud al servlet y acepta los datos de respuesta. App Engine utiliza varios servidores web para ejecutar tu aplicacin y ajusta automticamente el nmero de servidores en uso para procesar las solicitudes de una forma segura. Una determinada solicitud se puede enviar a cualquier servidor, que no tiene que ser el mismo servidor que proces una solicitud anterior procedente del mismo usuario. En la siguiente clase de servlet de ejemplo aparece un mensaje sencillo en el navegador del usuario. import java.io.IOException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType("text/plain"); resp.getWriter().println("Hello, world"); } }

Respuestas App Engine invoca el servlet mediante un objeto de solicitud y un objeto de respuesta. A continuacin, espera a que el servlet rellene el objeto de respuesta y devuelve los resultados. Cuando el servlet devuelve los resultados, los datos del objeto de respuesta se envan al usuario. App Engine no permite enviar datos al cliente, realizar clculos adicionales en la aplicacin y, posteriormente, enviar ms datos. En otras palabras, App Engine no admite la transmisin de datos como respuesta a una nica solicitud. Si el cliente enva cabeceras HTTP con la solicitud que indican que puede aceptar contenido comprimido (gzipeed), App Engine comprime los datos de respuesta de forma automtica y adjunta las cabeceras de respuesta correspondientes. Utiliza las dos cabeceras de solicitud Accept-Encoding y User-Agent para determinar si el cliente puede recibir respuestas comprimidas de forma segura. Los clientes personalizados fuerzan la compresin del contenido mediante la especificacin de las cabeceras Accept-Encoding y UserAgent con un valor "gzip". Si accedes a tu sitio despus de haber iniciado sesin con una cuenta de administrador, App Engine incluye estadsticas por solicitud en las cabeceras de respuesta. La cabecera X-AppEngine-Estimated-CPM-US-Dollars representa una estimacin del coste de 1.000 solicitudes similares a esta solicitud en dlares estadounidenses. La cabecera X-AppEngine-Resource-Usage representa los recursos utilizados por la solicitud, incluido el tiempo en el servidor, el tiempo de CPU del servidor de aplicaciones y el tiempo de CPU del API (en milisegundos). El periodo de tiempo de las solicitudes Un controlador de solicitudes tiene una cantidad limitada de tiempo para generar y devolver una respuesta a una solicitud, proceso que suele requerir 30 segundos aproximadamente. Transcurrido este tiempo, el controlador de solicitudes se interrumpe. El entorno de tiempo de ejecucin Java interrumpe el servlet con la generacin de una excepcin com.google.apphosting.api.DeadlineExceededException. Si el controlador de solicitudes no detecta esta excepcin, al igual que sucede con todas las excepciones no detectadas, el entorno de tiempo de ejecucin devuelve al cliente un error de servidor HTTP 500. El controlador de solicitudes puede detectar este error para personalizar la respuesta. El entorno de tiempo de ejecucin proporciona al controlador de solicitudes un poco ms de tiempo (menos de un segundo) tras generar la excepcin para preparar una respuesta personalizada. Aunque una solicitud puede tardar hasta 30 segundos en responder, App Engine se ha optimizado para utilizarse con aplicaciones cuyas solicitudes tengan tiempos de respuesta breves, normalmente cientos de milisegundos. Una aplicacin eficiente responde rpidamente a la mayora de las solicitudes. Una aplicacin que no lo es no lograr escalarse con la infraestructura de App Engine. La zona de pruebas A fin de permitir a App Engine distribuir solicitudes para aplicaciones a travs de varios servidores web y evitar que una aplicacin interfiera en otra, la aplicacin se ejecuta en un entorno de zona de pruebas restringido. En este entorno, la aplicacin puede ejecutar datos de consulta, almacenamiento y cdigo en el almacn de datos de App Engine, utilizar los servicios de correo, de extraccin de URL y de usuarios de App Engine, as como examinar las solicitudes web del usuario y preparar la respuesta. Una aplicacin App Engine no puede: escribir en un sistema de archivos. Las aplicaciones deben utilizar el almacn de datos de App Engine para almacenar los datos permanentes. La lectura desde el sistema de archivos est permitida y todos los archivos de aplicacin subidos con la aplicacin estn disponibles. abrir un socket o acceder a otro host directamente. Una aplicacin puede utilizar el servicio de extraccin de URL de App Engine para realizar solicitudes HTTP y HTTPS a otros hosts en los puertos 80 y 443, respectivamente. generar un proceso secundario o subproceso. El procesamiento de una solicitud web realizada a una aplicacin debe ser un nico proceso que dure unos cuantos segundos. Los procesos que tardan mucho tiempo en responder se finalizan para evitar la sobrecarga del servidor web. realizar otro tipo de llamadas al sistema.

Cadenas Una aplicacin Java no puede crear un nuevo java.lang.ThreadGroup ni un nuevo java.lang.Thread. Estas restricciones tambin son aplicables a las clases JRE que utilizan cadenas. Por ejemplo, una aplicacin no puede crear un nuevo java.util.concurrent.ThreadPoolExecutor ni un java.util.Timer. Una aplicacin puede realizar operaciones relacionadas con la cadena actual como, por ejemplo, Thread.currentThread().dumpStack(). El sistema de archivos Una aplicacin Java no puede utilizar ninguna clase que se utilice para escribir en el sistema de archivos como, por ejemplo, java.io.FileWriter. Una aplicacin puede leer sus propios archivos del sistema de archivos a travs de clases como, por ejemplo, java.io.FileReader. Una aplicacin tambin puede acceder a sus propios archivos como "recursos", como en el caso de Class.getResource() o de ServletContext.getResource(). La aplicacin solo puede acceder a los archivos que se consideran "archivos de recursos" a travs del sistema de archivos. De forma predeterminada, todos los archivos del WAR son "archivos de recursos". Puedes excluir archivos de este conjunto a travs del archivo appengine-web.xml. java.lang.System Se inhabilitan las funciones de la clase java.lang.System que no son aplicables a App Engine. Los siguientes mtodos System no tienen ninguna funcin en App Engine: exit(), gc(), runFinalization() y runFinalizersOnExit(). Los siguientes mtodos System devuelven null: inheritedChannel() y console(). Una aplicacin no puede proporcionar ni invocar de forma directa ningn cdigo JNI nativo. Los siguientes mtodos System generan una excepcin java.lang.SecurityException: load(), loadLibrary() y setSecurityManager(). Reflexin Una aplicacin tiene acceso completo, ilimitado y reflectivo a sus propias clases. Puede realizar la consulta de cualquier miembro privado, utilizar java.lang.reflect.AccessibleObject.setAccessible() y leer/establecer miembros privados. Una aplicacin tambin puede reflejarse en las clases JRE y API como, por ejemplo, java.lang.String y javax.servlet.http.HttpServletRequest. Sin embargo, solo puede acceder a miembros pblicos de estas clases, no a protegidos ni a privados. Una aplicacin no puede reflejarse en ninguna otra clase que no pertenezca a s misma y no puede utilizar el mtodo setAccessible() para evitar estas restricciones. Carga de clases personalizadas App Engine admite completamente la carga de clases personalizadas. Sin embargo, debes tener en cuenta que App Engine anula todos los ClassLoaders para asignar los mismos permisos a todas las clases cargadas por tu aplicacin. Si realizas una carga de clases personalizadas, ten cuidado al cargar cdigo de terceros que no sean de confianza. Accesos permitidos a la clase de JRE El acceso a las clases de la biblioteca estndar de Java (el entorno de tiempo de ejecucin Java o JRE) se limita a las clases de los accesos permitidos a JRE de App Engine. Acceso Tu aplicacin puede escribir informacin en los registros de la aplicacin a travs de java.util.logging.Logger. Puedes ver y analizar los datos de registro de tu aplicacin con la Consola del administrador o descargarlos mediante appcfg.sh request_logs. La Consola del administrador puede reconocer los niveles de registro de la clase Logger y mostrar mensajes de forma interactiva a diferentes niveles.

App Engine detecta y registra en los registros de la aplicacin todo lo que el servlet escriba en el flujo de salida estndar (System.out) y en el flujo de errores estndar (System.err). Las lneas que se escriben en el flujo de salida estndar se registran en el nivel INFO (Informacin) y las que se escriben en el flujo de errores estndar se registran en el nivel WARNING (Advertencia). Puede funcionar cualquier estructura de registro (como, por ejemplo, log4j) que se registre en los flujos de errores o de salida. Sin embargo, para un control ms preciso de la visualizacin del nivel de registro de la Consola del administrador, la estructura de registro debe utilizar un adaptador java.util.logging. import java.util.logging.Logger; // ... public class MyServlet extends HttpServlet { private static final Logger log = Logger.getLogger(MyServlet.class.getName()); public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { log.info("An informational message."); log.warning("A warning message."); log.severe("An error message."); } } El SDK Java de App Engine incluye un archivo de plantilla logging.properties en el directorio appengine-javasdk/config/user/. Para utilizarlo, copia el archivo en tu directorio WEB-INF/classes (o en cualquier otra ubicacin del WAR). A continuacin, copia la propiedad del sistema java.util.logging.config.file en "WEBINF/classes/logging.properties" (o en cualquier otra ruta asociada al directorio raz de la aplicacin). Puedes establecer las propiedades del sistema en el archivo appengine-web.xml tal y como se muestra a continuacin: <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> ... <system-properties> <property name="java.util.logging.config.file" value="WEBINF/classes/logging.properties" /> </system-properties> </appengine-web-app> El asistente para nuevos proyectos del complemento de Google para Eclipse crea estos archivos de configuracin de registro y los copia en WEB-INF/classes/ de forma automtica. Para java.util.logging, debes establecer la propiedad del sistema que debe utilizar este archivo.

El entorno Todas las propiedades del sistema y variables de entorno son privadas para tu aplicacin. La configuracin de una propiedad del sistema solo afecta a la vista de esa propiedad de tu aplicacin, y no a la vista del JVM. Puedes establecer propiedades del sistema y variables de entorno para tu aplicacin en el descriptor de implementacin. App Engine establece dos propiedades del sistema que identifican el entorno de tiempo de ejecucin: com.google.appengine.runtime.environment es "Production" cuando se ejecuta en App Engine, y "Development" cuando se ejecuta en el servidor de desarrollo. com.google.appengine.runtime.version es el ID de la versin del entorno de tiempo de ejecucin como, por ejemplo, "1.3.0".

Adems de utilizar System.getProperty(), puedes acceder a las propiedades del sistema mediante el API de seguridad de tipo. Por ejemplo: if (SystemProperty.environment.value() == SystemProperty.Environment.Value.Production) { // The app is running on App Engine... }

App Engine establece las siguientes propiedades del sistema cuando inicializa el JVM en un servidor de aplicaciones: file.separator path.separator line.separator java.version java.vendor java.vendor.url java.class.version java.specification.version java.specification.vendor java.specification.name java.vm.vendor java.vm.name java.vm.specification.version java.vm.specification.vendor java.vm.specification.name user.dir

Cuotas y lmites Google App Engine asigna recursos a tu aplicacin de forma automtica a medida que el trfico aumenta para que pueda admitir distintas solicitudes al mismo tiempo. No obstante, App Engine se reserva la capacidad de escalado automtica para las aplicaciones con baja latencia, que responden a las solicitudes en menos de un segundo. Las aplicaciones con latencia muy alta (ms de un segundo por solicitud para muchas solicitudes) estn limitadas por el sistema, y requieren una exencin especial con el fin de tener una mayor cantidad de solicitudes dinmicas simultneas. Si es muy necesario que tu aplicacin tenga una gran capacidad para procesar solicitudes de ejecucin con mayor latencia, puedes solicitar una exencin del lmite de solicitudes dinmicas simultneas. La gran mayora de las aplicaciones no requieren ninguna excepcin. Las aplicaciones que estn muy limitadas por la CPU tambin pueden utilizar latencia adicional para compartir de forma eficaz recursos con otras aplicaciones en los mismos servidores. Las solicitudes de archivos estticos estn exentas de estos lmites de latencia. Cada solicitud que recibe la aplicacin se contabiliza en la cuota de solicitudes. Los datos recibidos como parte de una solicitud se contabilizan en la cuota de ancho de banda de entrada (facturable). Los datos enviados en respuesta a una solicitud se contabilizan en la cuota de ancho de banda de salida (facturable). Las solicitudes HTTP y HTTPS (seguras) se contabilizan en las cuotas de solicitudes, ancho de banda de entrada (facturable) y ancho de banda de salida (facturable). En la pgina Detalles de la cuota de la Consola del administrador tambin aparecen las solicitudes seguras, el ancho de banda de entrada seguro y el ancho de banda de salida seguro como valores individuales para fines informativos. Solo las solicitudes HTTPS se contabilizan en estos valores. El tiempo de procesamiento de CPU destinado a ejecutar un controlador de solicitudes se contabiliza en la cuota de tiempo de CPU (facturable). Si quieres obtener ms informacin sobre las cuotas, consulta Cuotas y la seccin "Detalles de la cuota" de la Consola del administrador. Adems de las cuotas, los controladores de solicitudes presentan estos lmites: Lmite Valor tamao de la solicitud tamao de la respuesta duracin de la solicitud tamao mximo de un archivo de aplicacin tamao mximo de un archivo esttico 10 megabytes 10 megabytes 30 segundos 10 megabytes 10 megabytes

nmero mximo total de archivos (archivos de aplicacin y estticos) 3.000

tamao mximo total de todos los archivos de aplicacin y estticos 150 megabytes

El API Java del almacn de datos El almacn de datos de App Engine es un almacn de datos de objetos sin esquema, que dispone de un motor de consultas y transacciones atmicas. El SDK de Java incluye implementaciones de las interfaces de objetos de datos Java (JDO, Java Data Objects) y del API de persistencia Java (JPA, Java Persistence API), as como un API del almacn de datos de nivel inferior. El almacn de datos de duplicacin con alta disponibilidad constituye una solucin de almacenamiento todava ms fiable sin periodos de inactividad no programados y ofrece mayor fiabilidad de lectura y escritura, consistencia eventual para todas las consultas, excepto las de ancestro, y consistencia fuerte para las lecturas y las consultas de ancestro. En la referencia se describen las interfaces Java para el almacn de datos de App Engine, especialmente la de JDO. Presenta la siguiente estructura: Aspectos generales Entidades Consultas Transacciones Eleccin de un almacn de datos Uso del almacn de datos de duplicacin con alta disponibilidad Consultas de metadatos Estadsticas API de servicio asncrono JDO o Aspectos generales o Definicin de clases de datos con JDO o Creacin, obtencin y eliminacin de datos en JDO o Relaciones de entidad en JDO o Consultas en JDO JPA o Aspectos generales Referencia sobre Java

Aspectos generales del almacn de datos El almacn de datos de App Engine ofrece un almacenamiento slido y escalable para las aplicaciones web, con especial atencin en el rendimiento de las consultas y de las operaciones de lectura. Una aplicacin crea entidades donde los valores de los datos se almacenan como propiedades de una determinada entidad. La aplicacin puede realizar consultas de las entidades. Todas las consultas se indexan previamente para as poder proporcionar resultados rpidos a partir de conjuntos de datos de gran volumen. Introduccin al almacn de datos Introduccin al API Java del almacn de datos Entidades y propiedades Consultas e ndices Transacciones y grupos de entidades Diferencias con SQL Estadsticas del almacn de datos Cuotas y lmites

Introduccin al almacn de datos App Engine ofrece dos opciones para almacenar datos que se diferencian por su disponibilidad y por su coherencia: El almacn de datos principal/secundario se basa en un sistema de replicacin principal-secundario que replica datos de forma asncrona al tiempo que escribes los datos en un centro de datos fsico. Dado que solo puede haber un centro de datos principal a la vez para las operaciones de escritura, esta opcin ofrece consistencia fuerte para todas las consultas y operaciones de lectura. Como contrapartida, se producen periodos de inactividad temporales por incidencias en el centro de datos o por labores de mantenimiento planificadas. No obstante, la opcin ofrece el coste ms bajo por almacenar datos y por utilizar la CPU para tal fin.

En el almacn de datos de replicacin con alta disponibilidad, los datos se replican en los centros de datos mediante un sistema basado en el algoritmo Paxos. Este tipo de almacn ofrece una gran disponibilidad para las operaciones de lectura y de escritura (como contrapartida, existe una mayor latencia de las operaciones de escritura). La mayora de las consultas son de consistencia eventual. El coste de la cuota de almacenamiento y del uso de la CPU es aproximadamente tres veces superior al que supone el almacn de datos principal/secundario.

Si quieres obtener ms informacin al respecto, consulta Eleccin de un almacn de datos. El almacn de datos de App Engine guarda objetos de datos, conocidos con el nombre de entidades. Cada entidad incluye una o varias propiedades, es decir, valores especficos de uno de los distintos tipos de datos admitidos. Por ejemplo, una propiedad puede ser una cadena, un nmero entero o incluso una referencia a otra entidad. El almacn de datos puede ejecutar varias operaciones en una misma transaccin. Por definicin, una transaccin no se lleva a cabo correctamente si alguna de las operaciones que la componen no se ejecuta. En caso de error en alguna de las operaciones, la transaccin se deshace automticamente. Esto resulta especialmente til para aplicaciones web distribuidas, donde varios usuarios acceden a los mismos datos o los utilizan simultneamente. A diferencia de las bases de datos tradicionales, el almacn de datos emplea una arquitectura distribuida con el fin de administrar automticamente el escalado para conjuntos de datos de gran volumen. La relacin entre los objetos de datos propia de los almacenes de datos es muy distinta a la relacin que se da en una base de datos relacional tpica. Dos entidades del mismo tipo pueden tener propiedades diferentes. Se admite que varias entidades tengan propiedades con el mismo nombre pero presenten tipos de valor distintos. Si bien la interfaz del almacn de datos incorpora muchas de las funciones que integran las bases de datos tradicionales, el almacn de datos cuenta con caractersticas nicas como, por ejemplo, el diseo y la administracin de los datos de forma que se pueda aprovechar la capacidad de escalado automtico. En esta documentacin se explica cmo disear la aplicacin para sacar el mximo partido a la arquitectura distribuida del almacn de datos. Introduccin al almacn de datos Java Las entidades de almacn de datos no tienen un esquema: dos entidades del mismo tipo no tienen la obligacin de tener las mismas propiedades, ni de utilizar los mismos tipos de valores para las mismas propiedades. La aplicacin es responsable de garantizar que las entidades cumplan con un esquema cuando sea necesario. El almacn de datos proporciona un API de nivel inferior que realiza operaciones simples en entidades, incluidas get, put, delete y query. Puedes usar esta API para implementar otros adaptadores de interfaz, o solo usarla de forma directa en tus aplicaciones. Marcos para modelo y persistencia de datos El SDK de Java incluye implementaciones de las interfaces de objetos de datos Java (JDO, Java Data Objects) y del API de persistencia Java (JPA, Java Persistence API) para el modelo y la persistencia de datos. Estas interfaces basadas en estndares incluyen mecanismos para la definicin de clases en los objetos de datos y para realizar consultas. Adems de los marcos estndar y del API del almacn de datos de nivel inferior, el SDK de Java admite otros marcos diseados para facilitar el uso del almacn de datos a los desarrolladores de Java. De hecho, son muchos los desarrolladores de Java que usan estos marcos. El equipo de Google App Engine los recomienda encarecidamente y te invita a echarles un vistazo. Objectify: Objectify es una interfaz muy til y simple para el almacn de datos de App Engine que elimina algunas de las complejidades de JDO/JPA y del almacn de datos de nivel inferior. TWiG: TWiG es una interfaz de persistencia de objetos configurable que mejora la integracin con las funciones de herencia, de polimorfismo y de tipos genricos. Al igual que Objectify, TWiG tambin elimina algunas de las complejidades de JDO y del almacn de datos de nivel inferior. Slim3: Slim3 es un marco Modelo Vista Controlador completo para usar una amplia gama de funciones de App Engine que no se limitan al almacn de datos (aunque puedes usarlo para tal fin).

Entidades y propiedades Los objetos de datos del almacn de datos de App Engine se denominan entidades. Las entidades contienen una o varias propiedades, es decir, valores de uno de los distintos tipos de datos, por ejemplo, nmeros enteros, valores de punto flotante, cadenas, fechas, datos binarios, etc. Por su parte, cada entidad cuenta con una clave que sirve de identificador nico. Las claves ms simples estn compuestas por un tipo y un ID de entidad que proporciona el almacn de datos. El tipo se encarga de clasificar la entidad para poder realizar consultas de esta ms fcilmente. El ID de entidad tambin puede ser una cadena que proporcione la aplicacin.

La aplicacin puede extraer una entidad del almacn de datos utilizando su clave o bien realizando una consulta que coincida con las propiedades de la entidad. La consulta puede devolver entidades o no devolver ninguna. Adems, los resultados pueden aparecer ordenados por los valores de propiedad. Tambin es posible limitar el nmero de resultados de una consulta con el fin de reducir el uso de memoria o agilizar el tiempo de ejecucin y el uso de la CPU. A diferencia de las bases de datos relacionales, en el almacn de datos de App Engine no es necesario que todas las entidades de un tipo determinado tengan las mismas propiedades. La aplicacin puede especificar y aplicar su modelo de datos mediante las bibliotecas que se incluyen en el SDK o mediante su propio cdigo. Una propiedad puede incluir uno o varios valores. Si incluye varios de ellos, estos pueden ser de distintos tipos. Las consultas que se realizan a una propiedad que dispone de varios valores comprueban si alguno de estos coincide con los criterios de la consulta. Por consiguiente, estas propiedades resultan tiles para realizar comprobaciones en el caso de miembros de grupos. Consultas e ndices Las consultas del almacn de datos de App Engine operan en cada una de las entidades de un determinado tipo (una clase de datos). Establecen varios filtros o ninguno para las claves y los valores de propiedad de la entidad y varios criterios de ordenacin o ninguno. Si una determinada entidad incluye por lo menos un valor (posiblemente nulo) para cada una de las propiedades de los filtros y de los criterios de ordenacin, y todos los valores de propiedad coinciden con los criterios de filtrado, la entidad se devuelve como resultado. Todas las consultas del almacn de datos se basan en un ndice, es decir, una tabla con los resultados de la consulta en el orden deseado. Las aplicaciones App Engine definen los ndices en un archivo de configuracin (si bien para algunos tipos de consulta, los ndices se proporcionan de forma automtica). El servidor de desarrollo web aade automticamente sugerencias a este archivo cuando detecta consultas que todava no tienen configurado el ndice. Puedes ajustar estos ndices manualmente modificando el archivo antes de subir la aplicacin. A medida que la aplicacin modifica las entidades del almacn de datos, este ltimo actualiza los ndices con los resultados correctos. Cuando la aplicacin ejecuta una consulta, el almacn de datos extrae los resultados directamente del ndice en cuestin. Este mecanismo admite varios tipos de consulta y sirve para la mayora de las aplicaciones. Sin embargo, es incompatible con algunos tipos de consulta habituales en otras tecnologas de base de datos; concretamente, no se admiten consultas de unin ni de agrupacin conjunta. Transacciones y grupos de entidades Con el almacn de datos de App Engine, cada uno de los intentos para crear, modificar o eliminar una entidad ocurre en una transaccin. La transaccin garantiza que cada cambio que se realice en la entidad se guarde en el almacn de datos. En caso de que se produzca un error, la transaccin evita que se apliquen los cambios. De esta forma, se garantiza la coherencia de los datos dentro de la entidad. Dentro de una misma transaccin, se pueden realizar varias acciones en una entidad. Para ello, debers utilizar el API de transacciones. Por ejemplo, supongamos que quieres aumentar el campo de contador en un objeto. Para ello, debers leer el valor del contador, calcular el valor nuevo y, a continuacin, almacenarlo. Si no utilizas una transaccin, otro proceso podra aumentar el contador entre el momento de leer el valor y el momento de modificarlo, lo que provocara que la aplicacin sobrescribiera el valor modificado. Reunir las operaciones de lectura, clculo y escritura en una sola transaccin garantiza que ningn otro proceso interfiera en el incremento de valor. Dentro de la misma transaccin, puedes realizar cambios en distintas entidades mediante los grupos de entidades. Al crear una entidad, se especifica el grupo al que esta pertenece. Todas las entidades que se extraigan, creen, modifiquen o eliminen con una misma transaccin, deben pertenecer al mismo grupo de entidades. Los grupos de entidades se configuran a partir de la jerarqua de relaciones que se da entre las entidades. Para crear una entidad dentro de un grupo, debes especificar que esta es una entidad secundaria de otra entidad ya incluida en el grupo. La otra entidad ser la entidad principal. Una entidad sin una entidad principal es una entidad raz. Una entidad raz sin entidades secundarias se incluye en el grupo de entidades como entidad independiente. Cada entidad tiene asociada una ruta de relaciones entre la entidad principal y la entidad secundaria, desde la entidad raz hasta la propia entidad. La ruta ms corta es cuando no existe entidad principal. Esta ruta es una parte fundamental de la clave completa de la entidad. Una clave completa se puede representar mediante el tipo y el ID (o nombre de clave) de cada una de las entidades de la ruta. El almacn de datos utiliza el control de concurrencia optimista para administrar las transacciones. Mientras que una instancia de la aplicacin introduce los cambios en las entidades en un determinado grupo de entidades, el resto de los intentos por actualizar el grupo (ya sea modificando entidades o creando otras nuevas) resultan fallidos. La aplicacin puede intentar ejecutar la transaccin de nuevo para los datos actualizados. Ten en cuenta que, dado el funcionamiento del almacn de datos, si se utilizan grupos de entidades, se ver limitado el nmero de operaciones de escritura concurrentes en cualquiera de las entidades de dicho grupo.

Diferencias con SQL Existen varias diferencias importantes entre el almacn de datos de App Engine y una base de datos relacional tpica. El almacn de datos de App Engine se puede escalar, con lo que las aplicaciones pueden seguir con un nivel de rendimiento elevado cuando reciben ms trfico. Las operaciones de escritura del almacn de datos se pueden escalar mediante la distribucin automtica de los datos, segn sea necesario. Las operaciones de lectura del almacn de datos se escalan porque las nicas consultas que se admiten son aquellas cuyo rendimiento se escala con el tamao del conjunto de resultados (a diferencia del conjunto de datos). Esto significa que el rendimiento de una consulta cuyo conjunto de resultados contiene 100 entidades es el mismo tanto si la bsqueda es en 100 entidades como en un milln. Esta propiedad es el principal motivo por el que no se admiten algunos tipos de consulta. Puesto que todas las consultas que se realizan en App Engine las proporcionan ndices creados previamente, los tipos de consulta que se pueden ejecutar son ms limitados que los admitidos en una base de datos relacional con SQL. En el almacn de datos no se pueden realizar consultas de unin, como tampoco se admite el filtrado de desigualdad en varias propiedades ni el filtrado de datos basado en los resultados de una subconsulta. A diferencia de las bases de datos relacionales tpicas, el almacn de datos de App Engine no exige que los tipos de datos tengan un conjunto de propiedades coherente (aunque, si quieres, puedes incluir este requisito en el cdigo de la aplicacin). Al realizar consultas en la base de datos, en estos momentos no es posible devolver nicamente un subconjunto de propiedades de tipo. Como respuesta a una consulta, el almacn de datos de App Engine puede devolver entidades completas o solo claves de entidad. Para obtener informacin ms detallada sobre el diseo del almacn de datos, consulta la serie de artculos Mastering the datastore (Informacin detallada sobre el almacn de datos). Estadsticas del almacn de datos El almacn de datos contiene estadsticas sobre los datos almacenados de una aplicacin como, por ejemplo, la cantidad de entidades de un determinado tipo o el espacio que utilizan los valores de propiedad de un tipo en concreto. Encontrars estas estadsticas en "Almacn de datos" > "Estadsticas" de la Consola del administrador. Tambin puedes acceder a estos valores mediante un programa desde la aplicacin. Para ello, basta con realizar una consulta de entidades especficas mediante el API del almacn de datos. Para obtener ms informacin al respecto, consulta Estadsticas del almacn de datos. Cuotas y lmites Cada una de las invocaciones del API del almacn de datos consume parte de la cuota destinada a las invocaciones del API del almacn de datos. Ten en cuenta que algunas invocaciones de la biblioteca se convierten en varias invocaciones del API, por lo que el consumo de cuota es mayor. Los datos que la aplicacin enva al almacn de datos consumen cuota destinada a los datos enviados al API (Almacn de datos). Los datos que recibe la aplicacin del almacn de datos contabilizan en el consumo de cuota de los datos recibidos del API (almacn de datos). El total de datos de la aplicacin que se guarde en el almacn de datos no puede superar la cuota destinada a datos almacenados (facturable). Esto incluye todas las propiedades y claves de entidad, as como los ndices necesarios para poder realizar consultas de estas entidades. Consulta How Entities and Indexes are Stored (Cmo se almacenan las entidades y los ndices) para obtener una descripcin detallada de los metadatos necesarios para almacenar entidades e ndices en BigTable. La cantidad de tiempo de CPU que consumen las operaciones del almacn de datos se distribuye en las cuotas siguientes: Tiempo de CPU (facturable) Tiempo de CPU del almacn de datos

Si quieres obtener ms informacin sobre cuotas, consulta Cuotas y la seccin de informacin detallada de cuotas de la Consola del administrador. Adems de las cuotas, estos son los lmites que ataen al uso del almacn de datos:

Lmite Tamao mximo de entidad Nmero mximo de valores en todos los ndices de una entidad (1) 1.

Valor 1 megabyte 5.000 valores

En todos los ndices, cada entidad utiliza un valor de un ndice para cada columna y por cada fila que haga referencia a la entidad. El nmero de valores de ndice de una entidad puede aumentar si una propiedad indexada incluye ms de un valor, lo que exigir varias filas con valores repetidos en la tabla.

Entidades, propiedades y claves Hay que entender el almacn de datos de App Engine como una base de datos de objetos. Cada registro de datos es una entidad, la cual se representa mediante cdigo en forma de objeto. Cada entidad contiene una clave que sirve de identificador para diferenciarla de las dems entidades del almacn de datos. A su vez, cada entidad contiene una o varias propiedades, que se representan como atributos del objeto. Las claves y las propiedades son la base de muchas funciones tiles que ofrece el almacn de datos. Aspectos generales Tipos, ID y nombres Grupos de entidades y rutas de ancestros Propiedades y tipos de valor Cmo guardar, obtener y eliminar entidades

Aspectos generales Los objetos que se incluyen en el almacn de datos de App Engine se denominan entidades. Cada entidad contiene una clave que sirve de identificador para diferenciarla de las dems entidades. Las claves estn compuestas por varios elementos: una ruta, un elemento opcional que determina la entidad principal de la entidad en cuestin, el tipo de entidad, as como el nombre asignado a la entidad por la aplicacin o el ID numrico asignado por el almacn de datos. Cada entidad incluye una o varias propiedades, es decir, valores de uno de los distintos tipos de datos. Estos son algunos de los tipos de datos admitidos: nmeros enteros, valores de punto flotante, cadenas, fechas, datos binarios, etc. Una propiedad puede incluir uno o varios valores. Si la propiedad contiene ms de un valor, estos pueden ser de distintos tipos. Una aplicacin puede extraer una entidad del almacn de datos mediante la clave correspondiente o bien puede realizar una consulta basada en la clave o en los valores de propiedad de la entidad. Una vez creada la entidad, no es posible cambiar la clave asociada. Si quieres obtener ms informacin sobre las consultas, dirgete a la seccin Consultas e ndices. Una aplicacin solamente puede acceder a las entidades que esta haya creado; no puede acceder a datos que pertenezcan a otras aplicaciones. El SDK de Java incluye un API Java que admite directamente las funciones del almacn de datos. Esta API se incluye en el paquete com.google.appengine.api.datastore. Puedes usarla directamente en tus aplicaciones y para crear tu propia capa de administracin de datos. Utilizaremos esta API para presentar las funciones del almacn de datos. El SDK de Java tambin admite dos interfaces estndar para el almacenamiento de datos: la interfaz de objetos de datos Java (JDO, Java Data Objects) y el API de persistencia Java (JPA, Java Persistence API). Estas interfaces te permiten disear tus objetos de datos como clases Java y facilitan la transferencia de tu aplicacin desde el almacn de datos de App Engine a otras soluciones de almacenamiento de datos. Consulta las secciones Uso de JDO y Uso de JPA para obtener ms informacin sobre estas interfaces. A continuacin, incluimos un breve ejemplo de cmo utilizar el API del almacn de datos para crear una entidad del almacn de datos:

import import import import

java.util.Date; com.google.appengine.api.datastore.DatastoreService; com.google.appengine.api.datastore.DatastoreServiceFactory; com.google.appengine.api.datastore.Entity;

// ... DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Entity employee = new Entity("Employee");

employee.setProperty("firstName", "Antonio"); employee.setProperty("lastName", "Salieri"); Date hireDate = new Date(); employee.setProperty("hireDate", hireDate); employee.setProperty("attendedHrTraining", true); datastore.put(employee); En este ejemplo se prepara una nueva entidad del almacn de datos del tipo "Employee" y se asignan distintos tipos de valor a varias propiedades. A continuacin, se solicita al almacn de datos que guarde la nueva entidad. Nota: el almacn de datos en s no aplica ninguna restriccin a la estructura de las entidades; por ejemplo, que una determinada propiedad tenga un tipo de valor en concreto. La aplicacin se encarga de ello. Puedes administrar y aplicar una estructura de entidades mediante las interfaces de JDO y de JPA. Tipos, ID y nombres Cada entidad del almacn de datos es de un determinado tipo, un nombre que asigna la aplicacin. El tipo de entidad se encarga de clasificar la entidad para las consultas. Por ejemplo, en una aplicacin de RR.HH. se podra representar a cada empleado de la organizacin por medio de una entidad del tipo "Empleado". A diferencia de las filas de una tabla, no es necesario que dos entidades del mismo tipo contengan las mismas propiedades. En caso necesario, se puede configurar esta restriccin en el modelo de datos de la aplicacin. Una parte de la clave exclusiva de cada entidad sirve de identificador para la entidad. Una aplicacin puede asignar su propio identificador (denominado nombre de clave) para utilizarlo en la clave. Otra posibilidad es que, al almacenar la entidad por primera vez, el almacn de datos le asigne un ID numrico. Puesto que el identificador forma parte de la clave, una vez creada la entidad, ya no se podr modificar el ID o el nombre asignado. En el API del almacn de datos, debes especificar el tipo de entidad al crear el objeto Entity, como un argumento del constructor Entity. En el ejemplo anterior, Entity se cre como entidad del tipo "Employee". Debes especificar si una entidad ha de usar una cadena de nombre de clave asignada por la aplicacin o un ID numrico asignado por el sistema como identificador cuando crees el objeto. Para establecer un nombre de clave, inclyelo como segundo argumento del constructor Entity: Entity employee = new Entity("Employee", "asalieri"); Para especificar que el almacn de datos debe asignar un ID numrico de forma automtica, solo tienes que omitir el argumento: Entity employee = new Entity("Employee"); Grupos de entidades y rutas de ancestros Cuando utilizas el almacn de datos de App Engine, cada uno de los intentos para crear, modificar o eliminar una entidad ocurre en una transaccin. La transaccin garantiza que cada cambio que se realice en la entidad se guarde en el almacn de datos. En caso de que se produzca un error, la transaccin evita que se apliquen los cambios. De esta forma, se garantiza la coherencia de los datos dentro de la entidad. En esta seccin se describen las transacciones muy brevemente. Para obtener informacin ms detallada, consulta la seccin especfica Transacciones. Puedes realizar una misma transaccin con varias entidades siempre y cuando estas pertenezcan al mismo grupo de entidades. Cuando disees el modelo de datos, deberas especificar las entidades que quieres procesar en la misma transaccin. Luego, al crear las entidades, bastar con indicar que pertenecen al mismo grupo que otra entidad. Con esto, se indica a App Engine que las entidades se modificarn de forma conjunta, por lo que se pueden almacenar de manera que admitan transacciones. Para definir un grupo de entidades, especifica una jerarqua entre las mismas. Todas las entidades que se extraigan, creen, modifiquen o eliminen con una misma transaccin, deben pertenecer al mismo grupo de entidades. En el almacn de datos de replicacin con alta disponibilidad, los grupos de entidades tambin son una unidad coherente. La nica forma de garantizar que los resultados de una consulta sean de consistencia fuerte es mediante una consulta de ancestro. A la hora de crear una entidad nueva en un grupo, debes especificar la entidad principal. Una entidad sin una entidad principal es una entidad raz. Una entidad raz sin entidades secundarias es un grupo de entidades por s misma. Las claves de cada entidad contienen una ruta de entidades empezando por la raz del grupo de entidades, es decir, la propia entidad, cuando esta no tiene ninguna entidad principal. Esta ruta es una parte fundamental de la clave completa de la entidad. Una clave completa se puede representar mediante el tipo y el ID (o nombre de la clave) de cada una de las entidades de la ruta.

Una entidad que sea la entidad principal de otra puede tener a su vez otra entidad principal. Una cadena de entidades principales de una entidad hasta la raz es la ruta de la entidad; los miembros de la ruta son los ancestros de la entidad. La entidad principal de una entidad se define cuando se crea la entidad y no se puede modificar posteriormente. Para especificar que una entidad se cree en un grupo de entidades existente, asigna el objeto Key de la entidad principal como argumento del constructor Entity de la nueva entidad. Puedes obtener un objeto Key invocando el mtodo getKey() en la entidad (Entity) principal. Entity employee = new Entity("Employee"); datastore.put(employee); Entity address = new Entity("Address", employee.getKey()); Si la entidad con el elemento principal tambin tiene un nombre de clave, asigna el nombre de clave (String) como segundo argumento, y la clave (Key) de la entidad principal como tercero: Entity address = new Entity("Address", "addr1", employee.getKey()); La clave completa de una entidad es la clave de la entidad principal (si existe) seguida del tipo de entidad y del nombre de la clave o del ID del sistema. La clave de la entidad principal tambin puede incluir una entidad principal. Por lo tanto, la clave completa representa la ruta completa de los ancestros desde la entidad raz del grupo hasta la propia entidad. En este ejemplo, si la clave de la entidad Employee es Employee:8261, la clave de la direccin podra ser la siguiente: Employee:8261 / Address:1 Propiedades y tipos de valor Los datos de cada entidad se almacenan en una o en varias propiedades. Cada propiedad tiene un nombre asignado y al menos un valor. Cada valor es de uno de los distintos tipos de datos admitidos como, por ejemplo, cadena Unicode, nmero entero, fecha-hora y cadena de bytes. Una propiedad puede incluir diversos valores, y cada uno de estos puede ser de un tipo distinto. Las propiedades con ms de un valor resultan tiles, por ejemplo, para realizar consultas con filtros de igualdad. Cuando se utilizan filtros de igualdad, si alguno de los valores de propiedad coincide con los criterios del filtro, la consulta devuelve la entidad. Para obtener ms informacin sobre las propiedades con ms de un valor, as como cuestiones que deberas tener en cuenta, dirgete a la seccin Consultas e ndices. En la tabla siguiente se describen los distintos tipos de valor de propiedades que admite el almacn de datos:

Tipo de valor booleano cadena de bytes (corta) cadena de bytes (larga) categora fecha y hora direccin de correo electrnico

Tipo Java boolean o java.lang.Boolean com.google.appengine.api.datastore.ShortBlob

Criterio de ordenacin false < true orden de bytes

Notas

hasta 500 bytes un valor de ms de 500 bytes genera una excepcin JDOFatalUserException hasta 1 MB (sin indexar)

com.google.appengine.api.datastore.Blob com.google.appengine.api.datastore.Category java.util.Date com.google.appengine.api.datastore.Email

no disponible Unicode cronolgico Unicode

nmero de float, java.lang.Float, double, punto flotante java.lang.Double punto geogrfico usuarios de com.google.appengine.api.datastore.GeoPt com.google.appengine.api.users.User

numrico por latitud y, a continuacin, por longitud direccin de correo

precisin doble de 64 bits, IEEE 754

Google Accounts nmero entero short, java.lang.Short, int, java.lang.Integer, long, java.lang.Long

electrnico en orden Unicode (almacenado como nmero entero largo y, a continuacin, convertido al tipo de campo); superacin del intervalo de valores

numrico

clave, almacn com.google.appengine.api.blobstore.BlobKey de blob clave, almacn com.google.appengine.api.datastore.Key o el objeto de datos referenciado (como objeto secundario) enlace controlador de mensajes nulo direccin postal puntuacin nmero de telfono cadena de texto (corta) cadena de texto (larga) com.google.appengine.api.datastore.Link com.google.appengine.api.datastore.IMHandle null com.google.appengine.api.datastore.PostalAddress com.google.appengine.api.datastore.Rating com.google.appengine.api.datastore.PhoneNumber

orden de bytes por elementos de ruta (tipo, ID o nombre, tipo, ID o nombre...) Unicode Unicode no disponible Unicode numrico Unicode hasta 500 caracteres Unicode un valor de ms de 500 caracteres genera una excepcin JDOFatalUserException hasta 1 MB (sin indexar)

java.lang.String

Unicode

com.google.appengine.api.datastore.Text

no disponible

Es posible que, para una misma propiedad, dos entidades contengan valores de distinto tipo. El almacn de datos utiliza un sistema determinista para ordenar los valores que son de distinto tipo en funcin de las representaciones internas: valores nulos nmero entero, fecha-hora y puntuacin valores booleanos cadena de bytes (corta) cadenas Unicode: cadenas de texto (cortas), categora, direccin de correo electrnico, controlador de MI, enlace, nmero de telfono, direccin postal nmeros de punto flotante puntos geogrficos usuarios de Google Accounts claves del almacn de datos claves del almacn de blob

El almacn de datos no indexa las cadenas de texto largas ni las cadenas de bytes largas y, por lo tanto, no tienen definido ningn orden. Nota: los valores enteros y los nmeros de punto flotante se consideran tipos distintos en el almacn de datos. Si las entidades utilizan una mezcla de nmeros enteros y de nmeros de punto flotante para la misma propiedad, todos los nmeros enteros se ordenarn antes de los nmeros de punto flotante: 7 < 3.2

Cadenas de texto y cadenas de bytes El almacn de datos admite dos tipos de valores para almacenar texto Unicode: cadenas de texto cortas de hasta 500 caracteres Unicode y cadenas de texto largas de hasta 1 megabyte. Las cadenas cortas se indexan y se pueden utilizar en filtros de consultas y en criterios de ordenacin. Las cadenas largas no se indexan y no se pueden utilizar en filtros de consultas ni en criterios de ordenacin. El almacn de datos admite dos tipos de valores similares para datos binarios sin codificar: cadenas de bytes cortas de hasta 500 bytes y cadenas de bytes largas de hasta 1 megabyte. Al igual que sucede con las cadenas de texto, las cadenas de bytes cortas se indexan y se pueden utilizar en filtros de consultas o en criterios de ordenacin; las cadenas de bytes largas no se indexan y no se pueden usar en filtros de consultas ni en criterios de ordenacin. Nota: en el API del almacn de datos, el tipo de cadena de bytes larga se denomina "blob". Este tipo no est relacionado con los blobs segn se utilizan en el API del almacn de blob. Cmo guardar, obtener y eliminar entidades Las aplicaciones utilizan el API del almacn de datos para crear entidades nuevas, modificar las existentes, as como obtener y eliminar entidades. Si la aplicacin conoce la clave completa de una entidad (o la puede deducir de la clave, tipo e ID de la entidad principal), dicha aplicacin puede actuar directamente sobre la entidad por medio de la clave. La aplicacin tambin puede realizar una consulta para determinar las claves de aquellas entidades cuyas propiedades cumplan con determinados criterios. Dirgete a la seccin Consultas e ndices para obtener ms informacin sobre las consultas al almacn de datos. Con el API Java del almacn de datos, puedes guardar, obtener y eliminar entidades usando mtodos de DatastoreService. Este objeto se obtiene invocando el mtodo getDatastoreService() de la clase DatastoreServiceFactory. import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; // ... DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Para crear o actualizar una entidad en el almacn de datos, invoca el mtodo put() con el objeto Entity que desees guardar. Entity employee = new Entity("Employee"); // ... set properties ... datastore.put(employee); Para obtener una entidad identificada por una clave determinada, invoca el mtodoget() con un objeto Key. // Key employeeKey = ...; Entity employee = datastore.get(employeeKey); Para actualizar una entidad existente, modifica los atributos del objeto Entity y, a continuacin, invoca el mtodo put() con el objeto. Los datos del objeto sobrescriben la entidad existente. Todo el objeto se enva al almacn de datos con cada invocacin de put(). Nota: el API del almacn de datos no distingue entre la creacin de una nueva entidad y la actualizacin de una entidad existente. Si la clave del objeto representa una entidad existente, al invocar el mtodo put() con el objeto, se sobrescribe la entidad. Puedes usar una transaccin para comprobar si una entidad con una clave determinada ya existe antes de crearla. Para eliminar una entidad identificada por una clave determinada, invoca el mtodo delete() con un objeto Key. // Key employeeKey = ...; datastore.delete(employeeKey); Creacin de claves La clase KeyFactory puede crear objetos Key a partir de componentes conocidos, como el tipo y el nombre de la clave. Si la aplicacin conoce cada elemento de la clave completa de una entidad, puede crear el objeto Key correspondiente sin el objeto. Si la entidad asociada a esta clave no tiene un grupo de entidades principal, crea la clave usando el mtodo esttico createKey() de la clase KeyFactory. Este mtodo utiliza un tipo (el nombre simple de la clase) junto con un ID de cadena asignado por la aplicacin o un ID numrico asignado por el sistema para devolver un objeto Key. En el siguiente cdigo se muestra cmo debe recrearse la clave de

una entidad del tipo "Employee" con el nombre de clave "Alfred.Smith@example.com" (y sin un grupo de entidades principal): Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com"); Para recrear la clave de una entidad del tipo "Employee" con el ID numrico asignado por el sistema 52234 (y sin un grupo de entidades principal): Key k = KeyFactory.createKey(Employee.class.getSimpleName(), 52234); Si la entidad asociada a una clave tiene un grupo de entidades principal, puedes crear la clave con la clase KeyFactory.Builder: Key k = new KeyFactory.Builder(Employee.class.getSimpleName(), 52234) .addChild(ExpenseReport.class.getSimpleName(), "A23Z79").getKey(); El mtodo addChild() de la instancia de Builder devuelve el objeto Builder, de modo que puedes encadenar invocaciones para aadir cada elemento de la ruta de la clave. Para obtener el valor de clave completo de un objeto Builder determinado, invoca el mtodo getKey() de Builder. La clase KeyFactory tambin incluye los mtodos de clase keyToString() y stringToKey() para convertir claves en representaciones de cadena y a partir de estas representaciones. Ten en cuenta que este procedimiento es distinto del mtodo toString() de la clase Key, que devuelve un valor legible por el usuario y apto para la depuracin. La cadena de un valor de clave es apta para la Web, es decir, no contiene caracteres considerados especiales ni en el cdigo HTML ni en las URL. String employeeKeyStr = KeyFactory.keyToString(employeeKey); // ... Key employeeKey = KeyFactory.stringToKey(employeeKeyStr); Entity employee = datastore.get(employeeKey); Ten en cuenta que Key.toString() no devuelve una representacin de cadena (String) de la clave que pueda interpretar una mquina, sino una cadena que cualquier usuario puede interpretar y que resulta til para fines de depuracin y registro. Si necesitas una cadena (String) que pueda convertirse en clave (Key), utiliza KeyFactory.keyToString(key). Nota: la versin de cadena de una clave no est encriptada. El usuario puede descodificar la cadena de una clave para determinar sus componentes, incluidos los tipos, y los ID de la entidad y sus ancestros. Si esta informacin debe ocultarse al usuario, pero la aplicacin debe aceptar los valores de clave de las solicitudes de los usuarios, debes encriptar la cadena de la clave antes de enviarla al usuario. Operaciones en lote Los mtodos put(), get() y delete() aceptan objetos java.lang.Iterable Entity (para objetos put()) y objetos Key (para get() y delete()). De este modo, se realiza la accin en varias entidades mediante una sola invocacin del almacn de datos. La operacin en lote agrupa todas las entidades/claves por grupo de entidades y, a continuacin, realiza operaciones en cada grupo de entidades en paralelo. Una invocacin en lote al almacn de datos es ms rpida que realizar una invocacin para cada entidad porque solo produce la sobrecarga de una invocacin del servicio. Adems, si hay varios grupos de entidades, la tarea se realiza en el servidor, de forma paralela, para cada grupo de entidades. import java.util.Arrays; import java.util.List; // ... Entity employee1 = new Entity("Employee"); Entity employee2 = new Entity("Employee"); Entity employee3 = new Entity("Employee"); // ... List<Entity> employees = Arrays.asList(employee1, employee2, employee3); datastore.put(employees); Las invocaciones en lote al almacn de datos estn sujetas a los mismos lmites que las dems invocaciones. Por ejemplo, en una invocacin en lote del mtodo put(), el tamao total de todas las entidades que se guardan no debe superar un megabyte. Del mismo modo, una invocacin en lote del mtodo get() no puede devolver ms de un megabyte de datos. Una invocacin en lote del mtodo delete() solo enva las claves de las entidades que deben eliminarse y no devuelve ningn dato. Por lo tanto, el lmite solo afecta al tamao total de las claves.

Las invocaciones en lote de put() o delete() solo pueden realizarse con determinadas entidades. Si es importante que la invocacin consiga resultados para todas las entidades o no consiga para ninguna, debes usar una transaccin para que todas las entidades en cuestin se incluyan en el mismo grupo. Una operacin en lote dentro de una transaccin con entidades o claves que pertenezcan a varios grupos de entidades genera una excepcin IllegalArgumentException.

Consultas e ndices Aspectos generales Restricciones aplicadas a las consultas Extraccin de resultados Introduccin a los ndices Entidades de gran tamao e ndices de ampliacin Cursores de consultas Configuracin de la poltica de lectura y el tiempo lmite de la llamada al almacn de datos

Aspectos generales Una consulta recupera entidades del almacn de datos cuando estas renen una serie de condiciones, segn se hayan especificado. La consulta especifica un tipo de entidad, varias condiciones o ninguna en funcin de los valores de propiedad de la entidad (en ocasiones denominados "filtros") y varias descripciones de criterios de ordenacin o ninguna. Cuando se ejecuta la consulta, esta extrae las entidades de un determinado tipo que renen todas las condiciones especificadas, segn el criterio de ordenacin indicado. El almacn de datos principal/secundario y el almacn de datos de replicacin con alta disponibilidad ofrecen garantas distintas en cuanto a la coherencia de las consultas. De forma predeterminada: El almacn de datos principal/secundario presenta una consistencia fuerte en todas las consultas. Por su parte, el almacn de datos de replicacin con alta disponibilidad ofrece consistencia fuerte de forma predeterminada en las consultas de un grupo de entidades. En este tipo de almacenes, las consultas que no son de ancestro siempre son de consistencia eventual.

Para obtener ms informacin al respecto, consulta Configuracin de la poltica de lectura y el tiempo lmite de la llamada al almacn de datos. El API de Java de nivel inferior ofrece una clase Query para crear consultas y una clase PreparedQuery para la extraccin y devolucin de entidades del almacn de datos. import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.PreparedQuery; import com.google.appengine.api.datastore.Query; // ... // Get the Datastore Service DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); // The Query interface assembles a query Query q = new Query("Person") q.addFilter("lastName", Query.FilterOperator.EQUAL, lastNameParam); q.addFilter("height", Query.FilterOperator.LESS_THAN, maxHeightParam); // PreparedQuery contains the methods for fetching query results // from the datastore PreparedQuery pq = datastore.prepare(q); for (Entity result : pq.asIterable()) { String firstName = (String) result.getProperty("firstName"); String lastName = (String) result.getProperty("lastName"); Long height = (Long) result.getProperty("height"); System.out.println(lastName + " " + firstName + ", " + height.toString() + " inches tall"); }

Un filtro contiene un nombre de propiedad, un operador de comparacin y un valor. Una entidad transmite el filtro si dispone de una propiedad del nombre determinado y su valor es comparable con el del valor determinado del filtro que describe el operador. La entidad es el resultado de la consulta si esta transmite todos sus filtros. El operador de filtro puede ser cualquiera de los siguientes: Query.FilterOperator.LESS_THAN Query.FilterOperator.LESS_THAN_OR_EQUAL Query.FilterOperator.EQUAL Query.FilterOperator.GREATER_THAN Query.FilterOperator.GREATER_THAN_OR_EQUAL Query.FilterOperator.NOT_EQUAL Query.FilterOperator.IN (igual a cualquiera de los valores de la lista proporcionada)

En realidad, el operador NOT_EQUAL realiza dos consultas: una donde todos los dems filtros son el mismo y el filtro "no igual a" se sustituye por un filtro "menor que" y otra donde el filtro "no igual a" se sustituye por un filtro "mayor que". Los resultados se fusionan en orden. Como se describe a continuacin en la seccin sobre los filtros de desigualdad, una consulta solo puede disponer de un filtro "no igual a" y no puede presentar ningn otro filtro de desigualdad. El operador IN tambin realiza varias consultas, una por cada elemento del valor de la lista proporcionada, donde los dems filtros son iguales y el filtro IN se sustituye por un filtro "igual a". Los resultados se fusionan en el orden de los elementos de la lista. Si una consulta dispone de ms de un filtro IN, esta se realiza como si se tratara de varias consultas, una por cada combinacin de valores de los filtros IN. Una sola consulta que contenga los operadores NOT_EQUAL o IN tiene un lmite de 30 subconsultas. Para obtener ms informacin sobre cmo las consultas NOT_EQUAL y IN se traducen en varias consultas en un marco JDO/JPA, consulta Queries with NOT_EQUAL and IN filters (Consultas con filtros "!=" e "IN"). Tambin es posible que una consulta devuelva solamente las claves de las entidades en lugar de las propias entidades. Query q = new Query("Person").setKeysOnly(); Consultas sin tipo Una consulta a la que no se aplican filtros de tipo, ancestro o clave devuelve todas las entidades que se encuentran en el almacn de datos de la aplicacin. Ello incluye las entidades que crean y administran otras funciones de App Engine, como las entidades con estadsticas (que existen para todas las aplicaciones) y las entidades de metadatos del almacn de blob (si existen). Las consultas sin tipo no pueden incluir filtros de propiedades, pero si pueden filtrar por clave de entidad utilizando Entity.KEY_RESERVED_PROPERTY como nombre de propiedad para el filtro. Tambin funciona el orden ascendente en Entity.KEY_RESERVED_PROPERTY. Nota: las consultas sin tipo no funcionan actualmente en el servidor de desarrollo de la aplicacin. Consultas de ancestro Puedes filtrar las consultas del almacn de datos por un ancestro determinado para que los resultados solo incluyan las entidades que contengan dicho ancestro. En otras palabras, todos los resultados incluirn el ancestro como elemento principal o como elemento principal del elemento principal, etc. Si asignas "null" como parmetro a Query.setAncestor(String ancestor), no se realizar ninguna consulta de entidades sin ancestros (en estos momentos no se admite este tipo de consulta). DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Entity person = new Entity("Person", "tom"); Entity weddingPhoto = new Entity("Photo", person.getKey()); weddingPhoto.setProperty("imageUrl", "http://domain.com/some/path/to/wedding_photo.jpg"); Entity babyPhoto = new Entity("Photo", person.getKey()); babyPhoto.setProperty("imageUrl", "http://domain.com/some/path/to/baby_photo.jpg");

Entity dancePhoto = new Entity("Photo", person.getKey()); dancePhoto.setProperty("imageUrl", "http://domain.com/some/path/to/dance_photo.jpg"); Entity campingPhoto = new Entity("Photo"); dancePhoto.setProperty("imageUrl", "http://domain.com/some/path/to/camping_photo.jpg"); datastore.put(Arrays.asList(person, weddingPhoto, babyPhoto, dancePhoto, campingPhoto)); Query userPhotosQuery = new Query("Photo"); userPhotosQuery.setAncestor(person.getKey()); // This returns weddingPhoto, babyPhoto and dancePhoto, but // not campingPhoto because tom is not an ancestor. List<Entity> results = datastore.prepare(userPhotosQuery).asList( FetchOptions.Builder.withDefaults()); Consultas de ancestro sin tipo Tambin puedes realizar consultas de entidades con un ancestro determinado independientemente del tipo. Estas consultas tambin pueden incluir filtros, de igualdad o de desigualdad, en Entity.KEY_RESERVED_PROPERTY. Las consultas sin tipo no pueden incluir filtros de propiedades. Sin embargo, pueden filtrar por clave de entidad asignando Entity.KEY_RESERVED_PROPERTY como nombre de propiedad del filtro. Tambin funciona el orden ascendente en Entity.KEY_RESERVED_PROPERTY. En concreto, una consulta de ancestro sin tipo, y sin filtros adicionales, siempre devuelve la entidad identificada de forma nica por el ancestro (siempre que exista la entidad). El trmino "ancestro" parece indicar que las consultas devuelven solo elementos secundarios de una entidad, pero, de hecho, las consultas de ancestro pueden devolver la propia entidad. Ello se demuestra en el siguiente cdigo de ejemplo. Las consultas de ancestro sin tipo no requieren ndices personalizados. Para realizar una consulta de ancestro sin tipo usando la clase Query, invoca el constructor sin incluir el tipo: Query q = new Query(); q.setAncestor(ancestorKey); q.addFilter(Entity.KEY_RESERVED_PROPERTY, Query.FilterOperator.GREATER_THAN, lastKeyParam) En el siguiente cdigo de ejemplo se muestra cmo procesar una iteracin sobre todas las entidades pertenecientes a un usuario determinado: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Entity person = new Entity("Person", "tom"); Entity weddingPhoto = new Entity("Photo", person.getKey()); weddingPhoto.setProperty("imageUrl", "http://domain.com/some/path/to/wedding_photo.jpg"); Entity weddingVideo = new Entity("Video", person.getKey()); weddingVideo.setProperty("videoUrl", "http://domain.com/some/path/to/wedding.avi"); datastore.put(Arrays.asList(person, weddingPhoto, weddingVideo)); Query userMediaQuery = new Query(); userMediaQuery.setAncestor(person.getKey()); // Ancestor queries return ancestors by default. This filter // excludes the ancestor from query results. userMediaQuery.addFilter(Entity.KEY_RESERVED_PROPERTY, Query.FilterOperator.GREATER_THAN, person.getKey()); // Returns both weddingPhoto and weddingVideo even though they are // different entity kinds.

List<Entity> results = datastore.prepare(userMediaQuery).asList( FetchOptions.Builder.withDefaults()); Restricciones aplicadas a las consultas La naturaleza del mecanismo de consulta de ndices impone ciertas restricciones en el funcionamiento de una consulta. Dichas limitaciones se describen en esta seccin. Para poder filtrar u ordenar los criterios de una propiedad, esta debe existir Si una propiedad presenta una condicin de filtro o un criterio de ordenacin de consulta, esta ltima devuelve solo las entidades del almacn de datos cuya propiedad tenga un valor (incluido un valor nulo). No es necesario que las entidades de un mismo tipo tengan asociadas las mismas propiedades. El filtro de una propiedad solo puede coincidir con una entidad cuya propiedad presente un valor. Si en una entidad, la propiedad que se utiliza en el filtro o en los criterios de ordenacin no tiene ningn valor asociado, dicha entidad se omite del ndice que se crea a partir de la consulta. Los filtros de desigualdad solo se admiten en una propiedad Las consultas solo pueden emplear filtros de desigualdad (<, <=, >=, > y !=) en una propiedad en todos sus filtros. Por ejemplo, se admitira la siguiente consulta: import com.google.appengine.api.datastore.Query; import com.google.appengine.api.datastore.Query.FilterOperator; import com.google.appengine.api.datastore.Query.SortDirection; Query q = new Query("Person"); q.addFilter("birthYear", FilterOperator.GREATER_THAN_OR_EQUAL, minBirthYearParam); q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL, maxBirthYearParam); Sin embargo, la consulta siguiente no se permitira porque emplea filtros de desigualdad en dos propiedades distintas de la misma consulta: Query q = new Query("Person"); q.addFilter("birthYear", FilterOperator.GREATER_THAN_OR_EQUAL, minBirthYearParam); // Error q.addFilter("height", FilterOperator.GREATER_THAN_OR_EQUAL, minHeightParam); Los filtros pueden combinar comparaciones equivalentes (==) para propiedades distintas en una misma consulta, incluidas las consultas con una o varias condiciones de desigualdad en una propiedad. La consulta siguiente se admitira: Query q = new Query("Person"); q.addFilter("lastName", FilterOperator.EQUAL, lastNameParam); q.addFilter("city", FilterOperator.EQUAL, cityParam); q.addFilter("birthYear", FilterOperator.GREATER_THAN_OR_EQUAL, minBirthYearParam); Para que dos consultas sean adyacentes en la tabla de ndice, el mecanismo de consulta se basa en todos los resultados. As se evita tener que revisar cada uno de los resultados de la tabla. Una sola tabla de ndice no puede representar varios filtros de desigualdad en distintas propiedades y al mismo tiempo conservar el orden consecutivo de los resultados en la tabla. Las propiedades en filtros de desigualdad deben ordenarse antes que otros criterios de ordenacin Si una consulta incluye un filtro con una comparacin de desigualdad y uno o varios criterios de ordenacin, esta deber contener un criterio de ordenacin para la propiedad que se utilice en la desigualdad. Por otro lado, el criterio de ordenacin deber aparecer antes que los criterios de ordenacin de otras propiedades. Esta consulta no es vlida porque emplea un filtro de desigualdad y no ordena por la propiedad filtrada: Query q = new Query("Person"); q.addFilter("birthYear", FilterOperator.GREATER_THAN_OR_EQUAL, minBirthYearParam); q.addSort("lastName", SortDirection.ASCENDING); // ERROR La consulta siguiente tampoco es vlida porque no ordena por la propiedad filtrada antes de ordenar por otras propiedades: Query q = new Query("Person"); q.addFilter("birthYear", FilterOperator.GREATER_THAN_OR_EQUAL, minBirthYearParam);

q.addSort("lastName", SortDirection.ASCENDING); // ERROR q.addSort("birthYear", SortDirection.ASCENDING); La consulta siguiente es vlida: Query q = new Query("Person"); q.addFilter("birthYear", FilterOperator.GREATER_THAN_OR_EQUAL, minBirthYearParam); q.addSort("birthYear", SortDirection.ASCENDING); q.addSort("lastName", SortDirection.ASCENDING); Para obtener todos los resultados que coinciden con un filtro de desigualdad, la consulta analiza la tabla de ndice y localiza la primera fila coincidente. A continuacin, devuelve todos los resultados consecutivos hasta la siguiente fila que no coincide con los criterios especificados. Para que las filas consecutivas representen el conjunto de resultados completo, el filtro de desigualdad debe ordenar las filas antes que otros criterios de ordenacin. Las propiedades con ms de un valor pueden tener un comportamiento inesperado Debido a la forma en que estn indexadas las propiedades con ms de un valor, estas interactan con los filtros y los criterios de ordenacin de la consulta de una forma concreta y, en ocasiones, inesperada. En primer lugar, si una consulta incluye varios filtros de desigualdad en una determinada propiedad, la entidad coincidir con la consulta solo si esa propiedad tiene asociado un valor que cumpla con todos los filtros de desigualdad. Por ejemplo, si una entidad presenta los valores [1, 2] para la propiedad x, no coincidir con la consulta WHERE x > 1 AND x < 2. Cada filtro coincide con uno de los valores de x, pero ninguno de los valores coincide con ambos filtros. Ten en cuenta que esto no sucede con los filtros =. Por ejemplo, la consulta WHERE x = 1 AND x = 2 devolver la entidad anterior. El operador != funciona como la frmula de comprobacin "valor distinto de". As, por ejemplo, el filtro x != 1 coincide con una entidad con un valor mayor o menor que 1. Solo en Java se puede utilizar un filtro del tipo x != 1 AND x != 2, el cual funciona de la siguiente manera: "a < 1 OR (a > 1 y a < 2) OR a > 2". Por lo tanto, [1, 2] coincide, pero [1, 2, 3], no. Del mismo modo, no son habituales los criterios de ordenacin para propiedades con distintos valores: Si las entidades se ordenan por una propiedad con ms de un valor en orden ascendente, el valor ms pequeo es el que se utiliza para la ordenacin. Si las entidades se ordenan por una propiedad con ms de un valor en orden descendente, el valor ms grande es el que se utiliza para la ordenacin. Los otros valores no afectan a los criterios de ordenacin, ni tampoco la cantidad de valores.

Esta ordenacin ocurre porque aparecen en el ndice propiedades con distintos valores para cada uno de los valores que contienen. Sin embargo, el almacn de datos elimina los valores duplicados del ndice, por lo que el primero que se detecta en el ndice determina el criterio de ordenacin. Este presenta una consecuencia inusual: [1, 9] se incluye antes que [4, 5, 6, 7] en la ordenacin ascendente y tambin en la descendente. Los criterios de ordenacin se ignoran en propiedades con filtros de igualdad Es importante tener en cuenta las consultas con un filtro de igualdad y un criterio de ordenacin en una propiedad con ms de un valor. En estas consultas, el criterio de ordenacin no se toma en consideracin. En el caso de las propiedades con un solo valor, se trata de una simple optimizacin. Todos los resultados tendran asignado el mismo valor para la propiedad, por lo que estos no debern ordenarse ms. Sin embargo, las propiedades con distintos valores podran presentar valores adicionales. Puesto que el criterio de ordenacin no se tiene en cuenta, los resultados de la consulta se podran mostrar en un orden diferente que si se aplicara el criterio de ordenacin. Restaurar el criterio de ordenacin ignorado resultara caro y se necesitaran ndices adicionales. Esta situacin es inusual, por lo que el planificador de consultas la omite. Ordenacin de los resultados de la consulta cuando no se especifica ningn criterio de ordenacin Cuando una consulta no especifica el orden, el almacn de datos devuelve los resultados en el mismo orden en que los ha recuperado. A medida que introducimos cambios en la implementacin del almacn de datos, el orden de los resultados en una consulta sin orden

especificado tambin puede cambiar. Por lo tanto, si quieres que los resultados de la consulta presenten un orden determinado, no olvides indicarlo en la consulta. De lo contrario, los resultados se mostrarn en un orden indefinido que puede variar con el tiempo. Las consultas incluidas en transacciones deben contener filtros de ancestro Las consultas tambin se permiten dentro de una transaccin, siempre y cuando incluyan un filtro de ancestro. Este debe pertenecer al mismo grupo de entidades que las dems operaciones de la transaccin. Con ello, se establece la limitacin por la que una transaccin solo puede operar en entidades de un mismo grupo de entidades. Extraccin de resultados Despus de crear la consulta, puedes especificar varias opciones de extraccin con el fin de controlar los resultados que se devuelven. Si solo quieres que se muestre una entidad que coincida con la consulta, puedes utilizar el mtodo de consulta mtodo PreparedQuery.asSingleEntity(). Esto devuelve el primer resultado que coincide con la consulta. Si existen varios resultados que coincidan con la consulta, el sistema genera una excepcin TooManyResultsException. Las consultas basadas nicamente en claves devuelven las claves de las entidades del almacn de datos que coinciden con tu consulta. Este tipo de consultas se ejecutan ms rpidamente y consumen menos CPU que las que devuelven entidades completas. Para que una consulta solo devuelva las claves de una entidad, utiliza el mtodo Query.setKeysOnly(). Puedes especificar un lmite y una desviacin para la consulta con el fin de controlar la cantidad y el intervalo de resultados que se devuelven en un lote. Si especificas un lmite representado por un nmero entero, se devolver esa cantidad mxima de resultados. Si indicas una desviacin mediante un nmero entero, se omitir esa cantidad de resultados y se devolver el resto, hasta el lmite especificado. Por ejemplo, para extraer las cinco personas ms altas del almacn de datos, la consulta debera crearse de la forma siguiente: // Construct then prepare your query List<Entity> get5TallestPeople() { DatastoreService ds = DatastoreServiceFactory.getDatastoreService(); Query q = new Query("Person"); q.addSort("height", SortDirection.DESCENDING); PreparedQuery pq = ds.prepare(q); return pq.asList(FetchOptions.Builder.withLimit(5)); } Si, en lugar de ello, quieres extraer las personas ms altas entre la sexta y la dcima posicin, deberas utilizar esta otra consulta: return pq.asList(FetchOptions.Builder.withLimit(5).offset(5))); Al iterar los resultados mediante PreparedQuery.asIterable() y PreparedQuery.asIterator(), el almacn de datos extrae los resultados en lotes. De forma predeterminada, cada lote contiene 20 resultados, pero puedes cambiar este valor mediante FetchOptions.chunkSize(int). Puedes seguir iterando resultados de consultas hasta que se devuelvan todas o hasta que se agote el tiempo de espera de la solicitud. Introduccin a los ndices Todas las consultas del almacn de datos se basan en un ndice, es decir, una tabla con los resultados de la consulta en el orden deseado. Las aplicaciones App Engine definen los ndices en un archivo de configuracin denominado datastore-indexes.xml. El servidor de desarrollo web aade automticamente sugerencias a este archivo cuando detecta consultas que todava no tienen configurado el ndice. Puedes ajustar estos ndices manualmente modificando el archivo antes de subir la aplicacin. El mecanismo de consulta basado en ndices admite la mayora de los tipos de consulta ms extendidos. Sin embargo, es incompatible con algunos tipos de consulta habituales en otras tecnologas de base de datos. A continuacin, se describen las restricciones de consulta y la correspondiente explicacin. El almacn de datos dispone de un ndice para cada consulta que intenta crear la aplicacin. A medida que la aplicacin modifica las entidades del almacn de datos, este ltimo actualiza los ndices con los resultados correctos. Cuando la aplicacin ejecuta una consulta, el almacn de datos extrae los resultados directamente del ndice en cuestin. Para ejecutar una consulta, el almacn de datos realiza los pasos siguientes: 1. El almacn de datos identifica el ndice que corresponde con el tipo, las propiedades y los operadores de filtro, as como los criterios de ordenacin de la consulta.

2. 3.

El almacn de datos empieza a analizar el ndice a partir de la primera entidad que rene todas las condiciones de los filtros mediante los valores de filtro de la consulta. El almacn de datos contina con la operacin, devolviendo cada una de las entidades, hasta que detecta una entidad que no rene las condiciones de los filtros, llega al final del ndice o recaba el nmero mximo de resultados que exija la consulta.

La tabla de ndice contiene columnas para cada una de las propiedades que se utiliza en el filtro o en los criterios de ordenacin. Las filas se ordenan segn los aspectos siguientes, por orden: ancestros valores de propiedad utilizados en filtros de igualdad valores de propiedad utilizados en filtros de desigualdad valores de propiedad utilizados en criterios de ordenacin

Con ello, los resultados de cada posible consulta que utiliza el ndice se incluyen en filas consecutivas de la tabla. El ndice incluir una nica entidad si este hace referencia a todas las propiedades de la entidad. Si el ndice no hace referencia a alguna de las propiedades de la entidad, dicha entidad no aparecer en el ndice y nunca ser un resultado de la consulta que utiliza el ndice. Ten en cuenta que el almacn de datos de App Engine distingue entre una entidad que no posee una propiedad y una entidad que posee una propiedad con un valor de null . Si quieres que las entidades de un determinado tipo puedan ser el resultado de una consulta, utiliza una clase de datos JDO o JPA, la cual siempre asigna un valor a todas las propiedades que corresponden a un campo de la clase. Propiedades no indexadas Las consultas no encontrarn los valores de aquellas propiedades que no estn indexadas, entre estas, propiedades marcadas como no indexadas o propiedades con valores del tipo de valor de texto largo (Text) o de valor binario largo (Blob). Una consulta con un filtro o con un criterio de ordenacin en una propiedad nunca coincidir con una entidad cuyo valor para dicha propiedad sea Text o Blob, o que se haya escrito con esa propiedad marcada como no indexada. Las propiedades con estos valores se comportan como si no se hubieran definido en trminos de filtros y de criterios de ordenacin. Si tienes una propiedad que sabes que no debers filtrar ni ordenar por criterios, elimina la propiedad del ndice. Con ello, las operaciones de escritura consumirn menos CPU porque el almacn de datos ya no tendr que mantener las entradas de ndice para la propiedad en cuestin. Para que no se indexe una propiedad, utiliza Entity.setUnindexedProperty(): DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Entity tom = new Entity("Person", "tom"); tom.setProperty("age", 32); Entity lucy = new Entity("Person", "lucy"); lucy.setUnindexedProperty("age", 29); datastore.put(Arrays.asList(tom, lucy)); Query query = new Query("Person"); query.addFilter("age", FilterOperator.GREATER_THAN, 25); // Returns tom but not lucy because her age is unindexed List<Entity> results = datastore.prepare(query).asList( FetchOptions.Builder.withDefaults()); Combinacin de tipos de valor Cuando dos entidades contienen propiedades con el mismo nombre, pero los tipos de valor son diferentes, el ndice de la propiedad ordena las entidades primero por tipo de valor y, a continuacin, por un criterio adecuado para el tipo. Por ejemplo, si hay dos entidades con la propiedad "edad", una con un valor de nmero entero y la otra con un valor de cadena, la primera siempre aparecer antes que la segunda cuando se ordene por la propiedad "Edad", independientemente de los valores. Hay que tener esto en cuenta en el caso de nmeros enteros y nmeros de punto flotante, ya que el almacn de datos los considera dos tipos distintos de valores. Una propiedad que presente un valor de nmero entero 38 se incluir antes que otra con un valor de punto flotante 37.5 porque, al ordenar los valores, los nmeros enteros siempre preceden a los nmeros de punto flotante.

Consultas que necesitan ndices App Engine genera ndices para diversas consultas sencillas de forma predeterminada. Para otras consultas, la aplicacin debe especificar los ndices que necesita en un archivo de configuracin con el nombre datastore-indexes.xml. Si la aplicacin que se ejecuta en App Engine intenta realizar una consulta para la que no existe ndice (tanto si se proporciona de forma predeterminada como si se describe a travs de datastore-indexes.xml), la consulta generar un error y devolver la excepcin DatastoreNeedIndexException. App Engine proporciona ndices automticos para los formatos de consulta siguientes: consultas que nicamente emplean filtros de igualdad y de ancestro, consultas que nicamente emplean filtros de desigualdad (se incluyen en una sola propiedad), consultas sin tipo que solo emplean filtros de ancestro y de clave, consultas sin filtros y un solo criterio de clasificacin en una propiedad, ya sea en orden ascendente o descendente, consultas que nicamente emplean filtros de igualdad en propiedades y filtros de desigualdad en filtros de ancestro y de clave.

Existen otros tipos de consultas cuyos ndices deben especificarse en datastore-indexes.xml como, por ejemplo: consultas con varios criterios de ordenacin, consultas con un criterio de ordenacin en claves, en orden descendente, consultas con uno o varios filtros de desigualdad en una propiedad y con uno o varios filtros de igualdad en otras propiedades, consultas con filtros de desigualdad y filtros de ancestro.

El servidor de desarrollo web permite gestionar fcilmente la configuracin de ndices. Si, por ejemplo, no se puede ejecutar una consulta porque esta necesita un ndice, en lugar de generar un error, el servidor crea la configuracin del ndice y la consulta se realiza correctamente. Si al probar una aplicacin de forma local, esta invoca todas las consultas posibles, es decir, con todas las combinaciones de filtros y de criterios de ordenacin, las entradas que se generen correspondern a un conjunto completo de ndices. Si la prueba no ejecuta todas las combinaciones de consulta posibles, revisa y ajusta la configuracin del ndice antes de subir la aplicacin. Entidades de gran tamao e ndices de ampliacin Tal y como se describe anteriormente, las propiedades (que no presenten los valores Text o Blob) de cada entidad se aaden al menos a una tabla de ndice, incluido un ndice simple que se proporciona de forma predeterminada, as como los ndices descritos en el archivo datastore-indexes.xml de la aplicacin que hagan referencia a la propiedad. Para cada entidad que incluya un valor por propiedad, App Engine almacena un valor de propiedad una vez en su ndice simple. Asimismo, almacena un valor de propiedad una vez en el ndice personalizado por cada referencia que se haga a la propiedad. Estas entradas de ndice deben actualizarse cada vez que se modifique el valor de la propiedad. Por lo tanto, cuantos ms ndices hagan referencia a la propiedad, ms tiempo necesitar la CPU del almacn de datos para actualizar la propiedad. En el almacn de datos existe un lmite del nmero de entradas de ndice por entidad. El lmite es amplio y no afecta a la mayora de las aplicaciones, pero hay casos en los que s. Por ejemplo, una entidad que incluya muchas propiedades de un solo valor podra exceder el lmite de entradas de ndice. Las propiedades que incluyen distintos valores almacenan cada uno de estos por separado en el ndice. Una entidad que incluya una sola propiedad con muchos valores podra superar el lmite de entradas. Los ndices personalizados que hacen referencia a varias propiedades con muchos valores pueden aumentar considerablemente de tamao, aunque los valores sean pocos. Para poder registrar estas propiedades, la tabla de ndice debe incluir una fila por cada cambio que se produzca en los valores de las propiedades del ndice. Por ejemplo, el ndice siguiente (descrito en la sintaxis datastore-indexes.xml) incluye las propiedades x e y para las entidades del tipo MyModel: <?xml version="1.0" encoding="utf-8"?> <datastore-indexes> <datastore-index kind="MyModel"> <property name="x" direction="asc" /> <property name="y" direction="asc" /> </datastore-index> </datastore-indexes>

El cdigo siguiente crea una entidad con dos valores para la propiedad x y dos valores para la propiedad y: Entity myModel = new Entity("Model"); myModel.setProperty("x", Arrays.asList("one", "two")); myModel.setProperty("y", Arrays.asList("three", "four")); ds.put(myModel) Para representar de forma exacta estos valores, el ndice debe almacenar 12 valores de propiedad: dos para cada uno de los valores de x y de y, y uno para cada cambio que se produzca en x y en y. Si el ndice contiene muchos valores de propiedades con distintos valores, este deber almacenar muchas entradas de ndice para una sola entidad. Un ndice que haga referencia a varias propiedades con distintos valores podra denominarse "ndice de ampliacin" porque puede aumentar considerablemente de tamao con muy pocos valores. Si se ejecuta el mtodo put() y el resultado incluye un nmero de entradas superior al lmite establecido, la invocacin genera una excepcin de error. Si creas un ndice nuevo y el nmero de entradas supera el lmite en cualquiera de las entidades, se produce un error en las consultas que se realizan al ndice y este se muestra con un estado de error en la Consola del administrador. Si no quieres que un ndice se ample, evita las consultas que requieran un ndice personalizado mediante el uso de una propiedad de lista. Tal y como se ha descrito anteriormente, esto incluye consultas con varios criterios de ordenacin, una combinacin de filtros de igualdad y de desigualdad, y filtros de ancestro. Cursores de consultas Los cursores de consultas permiten que una aplicacin realice una consulta, recupere un conjunto de resultados y, a continuacin, extraiga ms resultados para la misma consulta en una solicitud web posterior sin que la desviacin de la consulta represente un obstculo. Una vez que la aplicacin haya extrado resultados para la consulta, puede solicitar una cadena codificada que represente la ubicacin en el conjunto de resultados despus de que se haya extrado el ltimo resultado (el "cursor"). La aplicacin puede utilizar el cursor para extraer ms resultados en otro momento a partir de ese punto. Un cursor es una cadena opaca de codificacin base64 que representa la siguiente posicin de inicio de una consulta despus de una operacin de extraccin. La aplicacin puede insertar el cursor en las pginas web como parmetros HTTP GET o POST, o bien puede almacenar el cursor en el almacn de datos, en Memcache o en una carga til de tareas de una cola de tareas. Un controlador de solicitud posterior puede realizar la misma consulta e incluir el cursor en la consulta para indicar al almacn de datos que debe empezar a devolver resultados desde la ubicacin que representa el cursor. Solo la aplicacin que haya realizado la consulta original puede utilizar el cursor, y nicamente para continuar con la misma consulta. Consejo: por lo general, es seguro pasar un cursor del almacn de datos a un cliente, por ejemplo, en formato web, y aceptar el valor del cursor de un cliente. Un cliente no puede cambiar el valor del cursor para acceder a resultados que no pertenezcan a la consulta original. Sin embargo, se puede descodificar el valor de codificacin base64 para mostrar informacin sobre las entidades resultantes como, por ejemplo, la clave (ID de aplicacin, tipo, nombre de clave o ID y todas las claves de ancestro) y las propiedades que se han utilizado en la consulta (incluidos filtros y criterios de ordenacin). Si no quieres que los usuarios tengan acceso a dicha informacin, puedes encriptar el cursor o almacenarlo y mostrar al usuario una clave opaca. Modificacin de cursores y de datos La posicin del cursor es la ubicacin en la lista de resultados despus del ltimo resultado. Un cursor no es una posicin relativa en la lista (no es una desviacin), sino que es el punto desde el cual el almacn de datos puede empezar a analizar los resultados del ndice. Si los resultados de una consulta cambian entre los usos de un cursor, la consulta detecta nicamente los cambios de los resultados que se encuentran despus del cursor. Si aparece un resultado nuevo antes de la posicin del cursor de esa consulta, no se devolver cuando se extraigan los resultados despus del cursor. Del mismo modo, si una entidad ya no forma parte de los resultados de la consulta pero se haba mostrado antes del cursor, los resultados que aparecen despus del cursor no cambian. Aunque el ltimo resultado que se devuelve se elimine del conjunto de resultados, el cursor sabe cmo localizar el siguiente resultado. Un uso interesante que se le puede dar a los cursores es supervisar entidades para cambios no detectados. Si la aplicacin establece una propiedad de marca horaria con la correspondiente fecha y hora cada vez que cambia una entidad, dicha aplicacin puede utilizar una consulta ordenada de forma ascendente por propiedad de marca de tiempo, con un cursor del almacn de datos para saber cundo una entidad se coloca al final de la lista de resultados. Si se cambia la marca de tiempo de una entidad, la consulta que incluye el cursor devuelve la entidad modificada. Si no se han modificado entradas desde la ltima ejecucin de la consulta, no se devuelven resultados y el cursor no se mueve. Para recuperar los resultados de una consulta, puedes utilizar tanto un cursor de inicio como de finalizacin para devolver continuamente un grupo de resultados del almacn de datos. Cuando utilizas un cursor inicial y otro final para recuperar los resultados, no se garantiza que el tamao de estos sea el mismo que al generar los cursores. Las entidades se pueden aadir al almacn de datos o bien eliminarse entre el momento en que se generan los cursores y el momento en que se utilizan en una consulta.

En el API de Java de nivel inferior, la aplicacin puede usar cursores a travs de las interfaces QueryResultList, QueryResultIterable y QueryResultIterator, las cuales se recuperan mediante los mtodos PreparedQuery (asQueryResultList(), asQueryResultIterable() y asQueryResultIterator(), respectivamente). Estos objetos de resultado proporcionan un mtodo getCursor(), el cual devuelve el objeto Cursor. La aplicacin puede obtener una cadena apta para la Web invocando el mtodo toWebSafeString() del objeto Cursor y, adems, crear un objeto Cursor a partir de una cadena apta para web mediante el mtodo esttico Cursor.fromWebSafeString(). En el siguiente ejemplo se muestra el uso de cursores para la paginacin. import java.io.IOException; import import import import import import import import import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; com.google.appengine.api.datastore.Cursor; com.google.appengine.api.datastore.DatastoreService; com.google.appengine.api.datastore.DatastoreServiceFactory; com.google.appengine.api.datastore.Entity; com.google.appengine.api.datastore.FetchOptions; com.google.appengine.api.datastore.PreparedQuery; com.google.appengine.api.datastore.Query; com.google.appengine.api.datastore.QueryResultList;

public class ListPeopleServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Query q = new Query("Person"); PreparedQuery pq = datastore.prepare(q); int pageSize = 15; resp.setContentType("text/html"); resp.getWriter().println("<ul>"); FetchOptions fetchOptions = FetchOptions.Builder.withLimit(pageSize); String startCursor = req.getParameter("cursor"); // If this servlet is passed a cursor parameter, let's use it if (startCursor != null) { fetchOptions.startCursor(Cursor.fromWebSafeString(startCursor)); } QueryResultList<Entity> results = pq.asQueryResultList(fetchOptions); for (Entity entity : results) { resp.getWriter().println("<li>" + entity.getProperty("name") + "</li>"); } resp.getWriter().println("</ul>"); String cursor = results.getCursor().toWebSafeString(); // Assuming this servlet lives at '/people' resp.getWriter().println( "<a href='/people?cursor=" + cursor + "'>Next page</a>"); } } Limitaciones de los cursores A continuacin, te indicamos algunos aspectos que se deben tener en cuenta sobre los cursores: No se pueden utilizar cursores en consultas que emplean los operadores de filtro IN o !=.

Para usar un cursor, la aplicacin debe realizar la misma consulta que haya proporcionado el cursor, incluido el tipo, los filtros y los valores de filtro, el filtro de ancestro y el criterio de ordenacin. No hay forma de extraer los resultados mediante un cursor sin configurar la misma consulta. Una consulta con un cursor no siempre funciona segn lo previsto si la consulta incluye un filtro de desigualdad o un criterio de ordenacin en una propiedad con valores distintos. La lgica que elimina las propiedades de distintos valores duplicadas de la consulta no perdura en las consultas, por lo que podra devolver un resultado ms de una vez. Los cursores dependen de la configuracin del ndice que admite la consulta. En los casos en los que las distintas configuraciones del ndice admiten la misma consulta, si se modifica la configuracin del ndice de la consulta, los cursores dejarn de ser vlidos. Por ejemplo, una consulta nicamente con filtros de igualdad y ningn criterio de ordenacin puede ser compatible con los ndices incorporados en la mayora de los casos; tambin podra utilizar un ndice personalizado, si este se proporciona. Los cursores tambin dependen de los ajustes de implementacin, que pueden variar o anularse con las nuevas versiones de App Engine. Si una aplicacin intenta utilizar un cursor que ha dejado de ser vlido, el almacn de datos genera una excepcin. En Java, genera IllegalArgumentException (API de bajo nivel), JDOFatalUserException (JDO) o PersistenceException (JPA).

Configuracin de la poltica de lectura y el tiempo lmite de la llamada al almacn de datos Con el fin de incrementar la disponibilidad de los datos, puedes configurar la poltica de lectura de forma que las operaciones de lectura y las consultas presenten una consistencia eventual. Si bien el API tambin permite definir de forma explcita una poltica de consistencia fuerte, en el almacn de datos de replicacin con alta disponibilidad, las consultas que no sean de ancestro siempre son consistentes eventualmente. Si en este tipo de almacenes, la poltica de lectura de consultas que no son de ancestro se configura en una opcin de consistencia fuerte, esto no tendr efecto alguno. Al establecer la opcin de consistencia eventual para una consulta del almacn de datos, a los ndices que utiliza la consulta para recabar resultados tambin se accede mediante consistencia eventual. De vez en cuando, estas consultas devuelven entidades que no coinciden con los criterios de la consulta. Sin embargo, el otro tipo de consultas devuelve siempre resultados coherentes. Puedes utilizar transacciones para garantizar que el conjunto de resultados sea coherente si la consulta incluye un filtro de ancestro. Consulta Aislamiento de transacciones en App Engine para obtener ms informacin sobre cmo se modifican las entidades y los ndices. La especificacin de DatastoreServiceConfig te permite seleccionar una de dos opciones de consistencia de lectura. Las opciones son "EVENTUAL" (para lecturas con consistencia eventual) y "STRONG" (para lecturas con consistencia fuerte). El valor predeterminado es "STRONG". La seleccin de una consistencia fuerte permite obtener datos ms recientes, pero se producirn tiempos de espera con ms frecuencia que con las lecturas de consistencia eventual. En cambio, las lecturas de consistencia eventual producen tiempos de espera con menos frecuencia, aunque de vez en cuando se obtienen resultados obsoletos. Puedes crear DatastoreServiceConfig mediante la clase DatastoreServiceConfig.Builder. En el siguiente fragmento de cdigo se muestra cmo debe definirse la poltica de lectura, cmo establecer un tiempo lmite de invocacin y cmo definir ambos parmetros: double deadline = 5.0; // Set the read policy with eventual consistency. ReadPolicy policy = new ReadPolicy(ReadPolicy.Consistency.EVENTUAL); DatastoreServiceConfig eventuallyConsistentConfig = DatastoreServiceConfig.Builder.withReadPolicy(policy); // Set the call deadline. DatastoreServiceConfig deadlineConfig = DatastoreServiceConfig.Builder.withDeadline(5); // Set both the read policy and the call deadline. DatastoreServiceConfig datastoreConfig = DatastoreServiceConfig.Builder.withDeadline(deadline).readPolicy(policy); DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(datastoreConfig); El tiempo lmite de invocacin del almacn de datos es de 30 segundos de forma predeterminada. Actualmente, no es posible establecer el tiempo lmite en un valor mayor, pero puedes ajustarlo a un valor menor si quieres asegurarte de que una determinada operacin falle rpidamente.

Transacciones El almacn de datos de App Engine admite transacciones. Una transaccin es una operacin o un conjunto de operaciones que se resuelven correcta o incorrectamente en su totalidad. Una aplicacin puede realizar varias operaciones y distintos clculos en una misma transaccin. Transacciones Acciones permitidas en una transaccin Aislamiento y coherencia Usos de las transacciones Colocacin en cola de las tareas de transaccin

Transacciones Una transaccin es una operacin o un conjunto de operaciones del almacn de datos que se resuelven correcta o incorrectamente en su totalidad. Si la transaccin se resuelve correctamente, todos los efectos deseados se aplicarn al almacn de datos. Si la transaccin no se resuelve correctamente, no se aplicar ninguno de los efectos. Todas las operaciones de escritura del almacn de datos son atmicas. Es decir, los intentos de crear, modificar o eliminar una entidad se aplican o no se aplican. Una operacin puede fallar debido a una tasa de contencin demasiado elevada provocada por el hecho de que muchos usuarios estn intentando modificar una entidad al mismo tiempo. Tambin se pueden producir fallos en las operaciones si una aplicacin alcanza un lmite de cuota o si se produce un error interno en el almacn de datos. En cualquiera de los casos, los efectos de la operacin no se aplicarn y el API del almacn de datos generar una excepcin. Las transacciones son una funcin opcional del almacn de datos, es decir, no es necesario recurrir a ellas para realizar operaciones. A continuacin, se incluye un ejemplo de la actualizacin de un campo llamado vacationDays en una entidad del tipo Employee denominada Joe: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService() Transaction txn = datastore.beginTransaction(); try { Key employeeKey = KeyFactory.createKey("Employee", "Joe"); Entity employee = datastore.get(employeeKey); employee.setProperty("vacationDays", 10); datastore.put(employee); txn.commit(); } finally { if (txn.isActive()) { txn.rollback(); } } Ten en cuenta que, para ofrecerte ejemplos ms breves, a veces, omitimos el bloque finally que realiza las tareas de recuperacin en caso de que la transaccin an permanezca activa. En los cdigos de produccin es importante asegurarse de que cada transaccin se lleve a cabo o se recupere de forma explcita. Grupos de entidades Cada entidad pertenece a un grupo de entidades, un conjunto de una o varias entidades que se pueden manipular en una nica transaccin. Las relaciones de grupos de entidades indican a App Engine que almacene varias entidades de la misma parte de la red distribuida. Una transaccin configura las operaciones de almacn de datos para un grupo de entidades, tras lo cual se aplican todas las operaciones o ninguna de ellas (si se genera un error en la transaccin). Cuando la aplicacin crea una entidad, puede asignar otra entidad como elemento principal de esa nueva entidad. Al asignar una entidad principal a una entidad nueva, se coloca la entidad nueva en el mismo grupo de entidades que la entidad principal. Una entidad sin una entidad principal es una entidad raz. Una entidad que sea la entidad principal de otra puede tener a su vez otra entidad principal. Una cadena de entidades principales de una entidad hasta la raz es la ruta de la entidad; los miembros de la ruta son los ancestros de la entidad. La entidad principal de una entidad se define cuando se crea la entidad y no se puede modificar posteriormente.

Todas las entidades con una entidad raz dada como ancestro pertenecen al mismo grupo de entidades. Todas las entidades de un grupo se almacenan en el mismo nodo de almacn de datos. Una nica transaccin puede modificar varias entidades de un grupo o aadir entidades nuevas al grupo haciendo que la entidad principal de la nueva entidad sea una entidad existente en el grupo. El siguiente fragmento de cdigo incluye transacciones en varios tipos de entidad: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Entity person = new Entity("Person", "tom"); datastore.put(person); // Transactions on root entities Transaction tx = datastore.beginTransaction(); Entity tom = datastore.get(person.getKey()); tom.setProperty("age", 40); datastore.put(tom); tx.commit(); // Transactions on child entities tx = datastore.beginTransaction(); tom = datastore.get(person.getKey()); Entity photo = new Entity("Photo", tom.getKey()); // Create a Photo that is a child of the Person entity named "tom" photo.setProperty("photoUrl", "http://domain.com/path/to/photo.jpg"); datastore.put(photo); tx.commit(); // Transactions on entities in different entity groups tx = datastore.beginTransaction(); tom = datastore.get(person.getKey()); Entity photoNotAChild = new Entity("Photo"); photoNotAChild.setProperty("photoUrl", "http://domain.com/path/to/photo.jpg"); datastore.put(photoNotAChild); // Throws IllegalArgumentException because the Person entity // and the Photo entity belong to different entity groups. tx.commit(); Creacin de una entidad en un grupo de entidades especfico Cuando la aplicacin crea una nueva entidad, puedes asignarla a un grupo de entidades facilitando la clave de otra entidad. En el siguiente ejemplo se crea la clave de la entidad MessageBoard y, a continuacin, se usa dicha clave para crear y hacer persistente una entidad Message que se ubica en el mismo grupo de entidades que MessageBoard: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); String messageTitle = req.getParameter("title"); String messageText = req.getParameter("body"); Date postDate = new Date(); Transaction txn = datastore.beginTransaction(); Key messageBoardKey = KeyFactory.createKey("MessageBoard", boardName); Entity message = new Entity("Message", messageBoardKey); message.setProperty("message_title", messageTitle); message.setProperty("message_text", messageText); message.setProperty("post_date", postDate); datastore.put(message); txn.commit(); Acciones permitidas en una transaccin El almacn de datos impone una serie de restricciones respecto a las acciones que se pueden realizar en una transaccin.

Todas las operaciones del almacn de datos incluidas en una transaccin se deben ejecutar en entidades pertenecientes a un mismo grupo. Esto incluye realizar consultas de entidades por ancestro, recuperar entidades por clave, as como modificar y eliminar entidades. Ten en cuenta que cada entidad raz pertenece a un grupo de entidades distinto, por lo que no es posible crear o realizar una transaccin en ms de una entidad raz. Si bien uno de los usos es aplicar cambios a las entidades de un grupo de entidades, los dems intentos de modificar una entidad del grupo fallan al ejecutarse. Ten en cuenta que, dado este diseo, si se utilizan grupos de entidades, se ver limitado el nmero de operaciones de escritura concurrentes en cualquiera de las entidades de dicho grupo. Cuando se inicia una transaccin, App Engine utiliza el control de concurrencia optimista. Para ello, comprueba la ltima hora de modificacin de un grupo de entidades. Tras ejecutar la transaccin de un grupo de entidades, App Engine vuelve a comprobar la ltima hora de modificacin del grupo. Si esta ha cambiado desde la ltima comprobacin, se genera una excepcin. Para obtener una explicacin de los grupos de entidades, consulta la seccin Grupos de entidades. Una aplicacin puede realizar consultas durante una transaccin, pero solo si incluye un filtro de ancestro. De hecho, puedes realizar una consulta sin que incluya dicho filtro, pero los resultados no reflejarn un estado de transaccin coherente. Una aplicacin tambin puede obtener entidades del almacn de datos por clave durante una transaccin. Puedes preparar claves antes de la transaccin o bien crear claves dentro de la transaccin con nombres de clave o ID. Aislamiento y coherencia El nivel de aislamiento del almacn de datos fuera de las transacciones es el ms prximo a READ_COMMITTED. Dentro de las transacciones, por el contrario, el nivel de aislamiento es SERIALIZABLE, en concreto una forma de aislamiento de instantnea. Consulta el artculo sobre aislamiento de transacciones para obtener ms informacin sobre los niveles de aislamiento. Se garantiza que las consultas y los mtodos get dentro de una transaccin muestren una nica instantnea coherente del almacn de datos a partir del inicio de la transaccin. En concreto, las entidades y las filas de ndice del grupo de entidades de la transaccin se actualizan por completo para que las consultas devuelvan el conjunto correcto de entidades, sin falsos positivos ni falsos negativos descritos en el artculo sobre aislamiento de transacciones; estos pueden producirse en consultas fuera de las transacciones. Esta vista de instantnea global tambin incluye operaciones de lectura producidas despus de operaciones de escritura dentro de las transacciones. A diferencia de la mayora de las bases de datos, las consultas y los mtodos get que se realizan dentro de una transaccin del almacn de datos no devuelven los resultados de operaciones de escritura anteriores en esa transaccin. En concreto, si se modifica o se elimina una entidad dentro de una transaccin, la consulta o el mtodo get devuelve la versin original de la entidad del inicio de la transaccin o bien no devuelve ningn resultado si la entidad no exista en ese momento. Usos de las transacciones En este ejemplo se ilustra una de las aplicaciones de las transacciones: cambiar el valor de propiedad de una entidad por un valor nuevo relativo a su valor actual. Puesto que el API del almacn de datos no vuelve a intentar las transacciones, se puede aadir lgica para intentar la operacin de nuevo en caso de que otra solicitud modifique el mismo mensaje MessageBoard o alguno de sus mensajes al mismo tiempo. int retries = 3; while (true) { Transaction txn = datastore.beginTransaction(); try { Key boardKey = KeyFactory.createKey("MessageBoard", boardName); Entity messageBoard = datastore.get(boardKey); long count = (Long) messageBoard.getProperty("count"); ++count; messageBoard.setProperty("count", count); datastore.put(messageBoard); txn.commit(); break; } catch (ConcurrentModificationException e) { if (retries == 0) { throw e; } // Allow retry to occur --retries; } finally { if (txn.isActive()) {

txn.rollback(); } } } Advertencia En los ejemplos anteriores se muestra cmo incrementar un contador mediante transacciones nicamente para simplificar los procesos. Si la aplicacin incluye contadores que se modifican con frecuencia, no deberas incrementarlos mediante transacciones, tan siquiera en una nica entidad. A la hora de trabajar con contadores, se recomienda emplear una tcnica denominada particin horizontal de contadores. Este mtodo requiere una transaccin porque el valor lo puede modificar otro usuario despus de que este cdigo extraiga el objeto, pero antes de que guarde el objeto modificado. Si no se utiliza una transaccin, la solicitud del usuario emplea el valor de count anterior a la modificacin del otro usuario y, al guardar, se sobrescribe el valor nuevo. Con una transaccin, se informa a la aplicacin de la modificacin del otro usuario. Si la entidad se modifica durante la transaccin, esta ltima genera la excepcin de error ConcurrentModificationException. La aplicacin puede repetir la transaccin con el fin de utilizar los datos nuevos. Otro de los usos que se suele dar a las transacciones es modificar una entidad con una determinada clave o crearla si no existe: Transaction txn = datastore.beginTransaction(); try { Key boardKey = KeyFatory.createKey("MessageBoard", "Foo"); Entity messageBoard = datastore.get(boardKey); } catch (EntityNotFoundException e) { messageBoard = new Entity("MessageBoard", boardName); messageBoard.setProperty("count", 0L); boardKey = datastore.put(messageBoard); } txn.commit(); Al igual que antes, las transacciones son necesarias en caso de que un usuario intente crear o modificar una entidad con el mismo ID de cadena que otro. Sin una transaccin, si la entidad no existe y dos usuarios intentan crearla, la operacin del segundo sobrescribe la del primero sin saberlo. Si se utiliza una transaccin, el segundo intento falla de forma atmica. Si procede, la aplicacin puede volver a intentar extraer la entidad y modificarla. Cuando una transaccin no se ejecuta, la aplicacin puede volver a intentar la operacin hasta que se realice correctamente o bien dejar que los usuarios gestionen el error transfirindolo a la interfaz de usuario de la aplicacin. No es necesario crear un loop de nuevo intento para cada transaccin. Consejo: las transacciones se deben realizar lo ms rpidamente posible para evitar que las entidades que incluye cambien, lo cual provocara un error en la transaccin. En la medida de lo posible, prepara los datos fuera de la transaccin y, a continuacin, ejecuta la transaccin para realizar operaciones del almacn de datos basadas en un estado de coherencia. La aplicacin debera preparar las claves para los objetos que se utilizan fuera de la transaccin y, a continuacin, extraer las entidades de dentro de la transaccin. Por ltimo, puedes utilizar una transaccin para leer una instantnea coherente del almacn de datos. Esto puede resultar til cuando se necesitan varias operaciones de lectura para mostrar una pgina o exportar datos que deban ser coherentes. A menudo, este tipo de transacciones se denominan de solo lectura, ya que no realizan ninguna operacin de escritura. Las transacciones de solo lectura nunca fallan debido a las modificaciones concurrentes, por lo que no es necesario realizar reintentos. Ejecutar y deshacer una transaccin de solo lectura no son operaciones opcionales. DatastoreService ds = DatastoreServiceFactory.getDatastoreService(); // Display information about a message board and its first 10 messages. Key boardKey = KeyFactory.createKey("MessageBoard", boardName); Transaction txn = datastore.beginTransaction(); Entity messageBoard = datastore.get(boardKey); long count = (Long) messageBoard.getProperty("count"); Query q = new Query("Message", boardKey); // This is an ancestor query. PreparedQuery pq = datastore.prepare(q); List<Entity> messages = pq.asList(FetchOptions.Builder.withLimit(10)));

txn.commit(); Colocacin en cola de las tareas de transaccin Puedes poner en cola una tarea como parte de una transaccin del almacn de datos, por ejemplo para que solo se ponga en cola la tarea (y se garantice dicha operacin), si la transaccin se realiza correctamente. Si la transaccin se lleva a cabo, se garantiza la puesta en cola de la tarea. Una vez en cola, no se garantiza la ejecucin inmediata de la tarea y cualquier operacin realizada en la tarea se ejecutar independientemente de la transaccin original. Las tarea se procesa varias veces hasta que, finalmente, se lleva a cabo. Esto sucede cada vez que una tarea se pone en cola durante una transaccin. Las tareas de transaccin son tiles porque permiten incluir acciones no relacionadas con el almacn de datos en una transaccin del almacn de datos (como en el envo de un correo electrnico de confirmacin de una compra). Tambin puedes asociar acciones del almacn de datos a la transaccin, como la modificacin de grupos de entidades adicionales ajenos a la transaccin, solo si la transaccin se realiza correctamente. Una aplicacin no puede insertar ms de cinco tareas de transaccin en las colas de tareas durante una nica transaccin. Las tareas de transaccin no deben tener nombres especificados por el usuario. DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Queue queue = QueueFactory.getDefaultQueue(); Transaction txn = datastore.beginTransaction(); // ... queue.add(TaskOptions.Builder.url("/path/to/queue")); // ... txn.commit();

Eleccin de un almacn de datos (Java) App Engine offers two data storage options with different reliability and consistency guarantees: In the High Replication Datastore (HRD), data is replicated across multiple data centers using a system based on the Paxos algorithm. This provides the highest level of availability for reads and writes, at the cost of higher latency on writes due to the propagation of data. Most queries are eventually consistent. The Master/Slave Datastore designates one data center to hold the master copy of all data. Data written to the master data center is replicated asynchronously to all other (slave) data centers. Since only one data center is the master for writing at any given time, this option offers strong consistency for all reads and queries, at the cost of periods of temporary unavailability during data center issues or planned downtime.

This page compares the two options and describes how to specify one of them for your application. Data Storage Options Compared Selecting a Data Storage Option

Data Storage Options Compared Watch a video comparing the High Replication and Master/Slave Datastores The High Replication Datastore (HRD) is a highly available, highly reliable storage solution that remains available for reads and writes during routine maintenance and unplanned events, and is extremely resilient in the face of catastrophic failure. The Master/Slave Datastore, by contrast, is suitable only for a limited class of applications that do not require high availability of data can tolerate spikes in Datastore latency

In the HRD, all strongly consistent operations (such as get by key or put) occur within a transaction. Entity groups are a unit of consistency as well as of transactionality: queries that require strongly consistent results must contain an ancestor filter, which restricts

the results to a single entity group. Queries that span multiple entity groups are not guaranteed to return up-to-date results. For more information about using ancestor queries in this context, see Using the High Replication Datastore. The following table summarizes the differences between the High-Resolution and Master/Slave Datastores: High Replication Performance Put/delete latency Get latency Query latency Consistency Put/get/delete Most queries Ancestor queries Strong Eventual Strong No Unplanned downtime Extremely rare; no data loss. Selecting a Data Storage Option The HRD is the default for all new applications. Existing applications must migrate to the HRD by copying entities to a new application. If you prefer to use the Master/Slave Datastore, you can select it as follows: For a new application: 1. 2. 3. On the Create an Application screen, click Edit. Select the Master/Slave radio button. Click Create Application. Rare; possible to lose a small % of writes occurring near downtime (recoverable after event) Strong Strong Strong Yes 1/2x1x 1x 1x 1x 1x 1x Master/Slave

Occasional planned read-only period

Warning! This option is irreversible; if you choose the Master/Slave Datastore and later want to migrate to the HRD, you'll need to create a new application and copy your current Datastore to the new application. For an existing application: 1. 2. Create a new application using the steps above. Copy your current Datastore to the new application. Note: Datastore copy is currently available only for Python applications. Java developers will need to perform some additional configuration steps. See A Note for Java Developers for more information.

Uso del almacn de datos de duplicacin con alta disponibilidad El almacn de datos de replicacin con alta disponibilidad ofrece mayor disponibilidad para las operaciones de lectura y de escritura porque almacena datos de forma sincronizada en varios centros de datos. Se producen cambios en el servidor, pero no en el API. Utilizars las mismas interfaces de programacin independientemente del almacn de datos que emplees. El almacn de datos de replicacin con alta disponibilidad tiene un coste superior por la replicacin adicional (consulta la pgina sobre facturacin para conocer los precios). Debido a que este tipo de almacn es ms caro, se recomienda principalmente para aquellos desarrolladores que se dispongan a crear aplicaciones App Engine clave que precisen de la mxima disponibilidad. Sin embargo, en el almacn de datos de replicacin con alta disponibilidad, las consultas que se realizan en grupos de entidades (en otras palabras, consultas que no sean de ancestros) pueden devolver resultados antiguos. Para devolver resultados de consistencia fuerte en el entorno del almacn de datos de replicacin con alta disponibilidad, la consulta debe realizarse a un nico grupo de entidades. Este tipo de consulta se denomina consulta de ancestro.

Las consultas de ancestro funcionan porque los grupos de entidades son una unidad de coherencia: las operaciones se aplican al grupo en su totalidad. Las consultas de ancestro no devuelven datos hasta que el grupo de entidades completo est actualizado. Por lo tanto, los datos que devuelven las consultas de ancestro de los grupos de entidades son de consistencia fuerte. Si tu aplicacin se basa en resultados de consistencia fuerte para determinadas consultas, es posible que debas cambiar la forma en que esta almacena las entidades. En esta pgina se ofrecen recomendaciones para trabajar con datos guardados en el almacn de datos de replicacin con alta disponibilidad. Veamos cmo funciona mediante las aplicaciones de libro de visitas de muestra para el almacn de datos principal/secundario y el almacn de datos de replicacin con alta disponibilidad, respectivamente. Almacn de datos principal/secundario En el almacn de datos principal/secundario, la aplicacin de aplicacin de libro de visitas de muestra (disponible en /google/apphosting/demos/guestbook/src/guestbook/) crea una entidad raz nueva para cada entrada del libro: Entity greeting = new Entity("Greeting"); // No parent key specified, so the Entity is a root. greeting.setProperty("user", user); greeting.setProperty("date", date); greeting.setProperty("content", content); A continuacin, se realiza una consulta de las diez ltimas entradas del libro de visitas: 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)); Este esquema es adecuado porque, de forma predeterminada, el almacn de datos principal/secundario devuelve resultados de consistencia fuerte para todas las consultas. Esto se debe a que, de forma predeterminada, el almacn de datos principal/secundario realiza las operaciones de lectura y de escritura nicamente desde la replicacin principal. Si pruebas a realizar esta consulta en el almacn de datos de replicacin con alta disponibilidad, es posible que el centro de datos que ejecuta la consulta no detecte la aplicacin Greeting nueva en el momento de la operacin. Almacn de datos de replicacin con alta disponibilidad En el almacn de datos de replicacin con alta disponibilidad, la aplicacin de libro de visitas de muestra utiliza una clave principal para el tipo Guestbook con guestbookName como nombre de clave y guarda las entradas subsiguientes del libro en el grupo de entidades que ha definido la clave principal. String guestbookName = req.getParameter("guestbookName"); Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName); String content = req.getParameter("content"); Date date = new Date(); // Places the greeting in the same entity group as the guestbook Entity greeting = new Entity("Greeting", guestbookKey); greeting.setProperty("user", user); greeting.setProperty("date", date); greeting.setProperty("content", content); Las consultas de las entradas del libro utilizan la clave principal Guestbook para realizar una consulta de ancestro, la cual nicamente encontrar entradas Greetings aadidas al libro de visitas en cuestin: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Key guestbookKey = KeyFactory.createKey("Guestbook", guestbookName); Query query = new Query("Greeting", guestbookKey).addSort("date", Query.SortDirection.DESCENDING); query.setAncestor(guestbookKey); List<Entity> greetings = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(10)); Notas sobre el uso El cdigo de muestra del almacn de datos de replicacin con alta disponibilidad anterior permite realizar operaciones de escritura en un nico grupo de entidades para cada libro de visitas. As pues, los resultados de las consultas que se realizan a un nico libro de visitas son de consistencia fuerte. No obstante, la posibilidad de modificaciones en el libro de visitas se reduce a una operacin de escritura por segundo (el lmite para grupos de entidades). Por lo tanto, las escrituras a un nico grupo de entidades por libro de visitas no es lo mejor

para aquellas aplicaciones de uso intensivo. Si es probable que tu aplicacin reciba un volumen considerable de operaciones de escritura, te recomendamos que utilices otro mtodo. Por ejemplo, podras incluir las publicaciones recientes en Memcache con una fecha de caducidad y mostrar una combinacin de publicaciones recientes de Memcache y de publicaciones del almacn de datos. Con la consistencia eventual, ms del 99,9% de las operaciones de escritura estn disponibles para recibir consultas al cabo de unos segundos. El objetivo es encontrar una solucin de memoria cach para la aplicacin que proporcione datos al usuario durante el tiempo que ests publicando en ella. Esta solucin quizs sea un servicio Memcache, una memoria cach en una cookie, algn tipo de estado en la URL o un mtodo completamente distinto. La cuestin es que si la solucin proporciona datos al usuario en el contexto de sus publicaciones, probablemente sea suficiente para que la consistencia eventual del almacn de datos de replicacin con alta disponibilidad sea totalmente aceptable. Recuerda que si realizas una operacin get(), put() o ejecutas una transaccin, vers los ltimos datos que se hayan escrito.

Consultas de metadatos Las consultas de metadatos permiten crear expresiones fcilmente que devuelvan metadatos del almacn de datos sobre espacios de nombre, tipos y propiedades. Las consultas devuelven los metadatos en entidades generadas de forma dinmica. Estos metadatos estn actualizados segn el momento de la consulta. El uso ms extendido de los metadatos es la implementacin de funciones administrativas de servidor, entornos de metaprogramacin, etc. Por ejemplo, podras utilizar las consultas de metadatos para crear una Consola del administrador personalizada para tu aplicacin. Tambin encontrars metadatos sobre la aplicacin en la pestaa de administracin del almacn de datos de tu Consola del administrador. Sin embargo, los datos que se muestran en dicha pestaa presentan diferencias importantes con respecto a los datos que devuelven las consultas de metadatos: Datos actualizados: las consultas de metadatos devuelven datos actualizados segn el momento de la consulta. Los datos de la pestaa de administracin del almacn de datos solo se actualizan una vez al da. Datos exhaustivos: los datos de la pestaa de administracin del almacn de datos son ms exhaustivos que los datos que devuelven las consultas de metadatos. Coste: las consultas de metadatos utilizan ms cuota que las consultas normales.

En esta documentacin se describen los siguientes aspectos de las consultas de metadatos: Consultas de metadatos en Java Consultas de espacio de nombre Consultas de tipo Consultas de propiedades Consultas de representacin de propiedades Utilizacin de cuota

Consultas de metadatos en Java El paquete com.google.appengine.api.datastore.Query proporciona tres tipos especiales que se reservan en el almacn de datos para las consultas de metadatos. Estos tipos no entrarn en conflicto con los tipos personalizados del mismo nombre que ya existan en tu aplicacin. Los tipos reservados para las consultas de metadatos son los siguientes: __namespace__ __kind__ __property__

Si realizas consultas utilizando estos tipos, se devuelven entidades que contienen los metadatos solicitados. Estas entidades se generan dinmicamente en funcin del estado del almacn de datos en ese momento. Si bien puedes crear objetos com.google.appengine.api.datastore.Entity locales con tipos __namespace__, __kind__ o __property__, no podrs guardarlos en el almacn de datos. La forma ms fcil de acceder a estos tipos especiales es mediante el API del almacn de datos de nivel inferior. Por ejemplo, el cdigo siguiente imprime todos los espacios de nombre de una aplicacin: import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.Entity;

import com.google.appengine.api.datastore.Query; void printAllNamespaces(DatastoreService ds, PrintWriter writer) { Query q = new Query(Query.NAMESPACE_METADATA_KIND); for (Entity e : ds.prepare(q).asIterable()) { if (e.getKey().getId() != 0) { writer.println(""); } else { writer.println(e.getKey().getName()); } }

Las consultas de metadatos admiten funciones de filtrado limitado por intervalos de nombres de espacios de nombre (__namespace__), intervalos de nombres de tipo (__kind__ y __property__) e intervalos de pares de nombres de tipo o de propiedad (__property__). Consultas de espacio de nombre Si tu aplicacin utiliza el API de espacios de nombre, puedes usar una consulta de metadatos para buscar todos los espacios de nombre que aparecen en los registros de la entidad. De este modo, podrs realizar actividades entre espacios de nombre con mayor facilidad como, por ejemplo, funciones administrativas. Las consultas de espacio de nombre devuelven entidades cuya clave es el nombre del espacio de nombre, excepto en el caso del espacio de nombre vaco, el cual tiene una clave con un ID numrico 1 (la cadena vaca no es un nombre de clave vlido). Estas entidades no tienen propiedades. As, pues, las consultas que solo incluyen claves y las que no devuelven la misma informacin. Las consultas de espacio de nombre solo permiten filtrar por intervalos de __key__. La sencilla funcin que se muestra a continuacin devuelve una lista de espacios de nombre de una aplicacin situados entre dos nombres opcionales: import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.Query; import java.util.ArrayList; import java.util.List; Key makeNamespaceKey(String namespace) { return KeyFactory.createKey(Query.NAMESPACE_METADATA_KIND, namespace); } List<String> getNamespaces(DatastoreService ds, String startNamespace, String endNamespace) { Query q = new Query(Query.NAMESPACE_METADATA_KIND); // Limit query to specified namespaces if (startNamespace != null) { q.addFilter(Entity.KEY_RESERVED_PROPERTY, Query.FilterOperator.GREATER_THAN_OR_EQUAL, makeNamespaceKey(startNamespace)); } if (endNamespace != null) { q.addFilter(Entity.KEY_RESERVED_PROPERTY, Query.FilterOperator.LESS_THAN_OR_EQUAL, makeNamespaceKey(endNamespace)); } // Build namespace list List<String> results = new ArrayList<String>(); for (Entity e : ds.prepare(q).asIterable()) { // A non-zero numeric id is used for the default namespace if (e.getKey().getId() != 0) { results.add("");

} else { results.add(e.getKey().getName()); } } return results; } Consultas de tipo Al igual que ocurre con las consultas de espacio de nombre, las consultas __kind__ devuelven entidades cuya clave es el nombre del tipo, sin propiedades (por lo tanto, las consultas que solo incluyen claves y las que no devuelven la misma informacin). Del mismo modo, las consultas de tipo solo permiten filtrar por intervalos de __key__. Las consultas de tipo se limitan automticamente al espacio de nombre existente. A modo de ejemplo, el cdigo siguiente imprime los tipos cuyo nombre empieza con una letra minscula: import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.Query; import java.io.PrintWriter; Key makeKindKey(String kind) { return KeyFactory.createKey(Query.KIND_METADATA_KIND, kind); } void printSomeKinds(PrintWriter writer, DatastoreService ds) { Query q = new Query(Query.KIND_METADATA_KIND); q.addFilter(Entity.KEY_RESERVED_PROPERTY, Query.FilterOperator.GREATER_THAN_OR_EQUAL, makeKindKey("a")); q.addFilter(Entity.KEY_RESERVED_PROPERTY, Query.FilterOperator.LESS_THAN, makeKindKey("{")); // { is character after z PrintWriter writer = response.getWriter(); writer.println("lower-case kinds:"); for (Entity e : ds.prepare(q).asIterable()) { writer.println(" " + e.getKey().getName()); } } Consultas de propiedad Las consultas de propiedad te permiten devolver todas las propiedades asociadas a un tipo, pero son ms complejas que las consultas __namespace__ y __kind__. En primer lugar, describiremos las consultas __property__ ms sencillas, que solo contienen claves: Estas consultas devuelven una clave por cada propiedad P del tipo K, del modo siguiente: o La clave tiene el tipo __property__ y el nombre P. o La clave tiene una clave principal con el tipo __kind__ y el nombre K. Las propiedades no indexadas no se devuelven en las consultas __property__. Las consultas __property__ admiten consultas de ancestro, donde el ancestro es una clave __kind__ o __property__, que limita la consulta a un nico tipo o propiedad. Las consultas __property__ admite filtros __key__, donde las claves son claves __kind__ o __property__. Las consultas __property__ estn implcitamente limitadas al espacio de nombre vigente.

El tipo __key__ filtra por pares tipo-propiedad. Por ejemplo, si tienes: el tipo A con las propiedades m, q, z, el tipo B con las propiedades c, d, el tipo C con las propiedades a, b.

Entonces, esta consulta: import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; import com.google.appengine.api.datastore.Query; import java.io.PrintWriter; Key makePropertyKey(String kind, String property) { return KeyFactory.createKey(makeKindKey(kind), Query.PROPERTY_METADATA_KIND, property); } void printPropertyRange(PrintWriter writer, DatastoreService ds) { q = new Query(Query.PROPERTY_METADATA_KIND); q.addFilter(Entity.KEY_RESERVED_PROPERTY, Query.FilterOperator.GREATER_THAN_OR_EQUAL, makePropertyKey("A", "p")); q.addFilter(Entity.KEY_RESERVED_PROPERTY, Query.FilterOperator.LESS_THAN_OR_EQUAL, makePropertyKey("C", "a")); for (Entity e : ds.prepare(q).asIterable()) { writer.println(e.getKey().getParent().getName() + ": " + e.getKey().getName()); } } imprime lo siguiente: A: z B: c B: d C: a No puedes realizar una sola consulta que devuelva todas las propiedades cuyo nombre est entre S1 y S2 en algn tipo. El uso de consultas de ancestro facilita la obtencin de todas las propiedades de un tipo concreto, como se muestra en la funcin siguiente: import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Query; import java.util.ArrayList; import java.util.List; List propertiesOfKind(DatastoreService ds, String kind) { Query q = new Query(Query.PROPERTY_METADATA_KIND); q.setAncestor(makeKindKey(kind)); ArrayList results = new ArrayList(); for (Entity e : ds.prepare(q).asIterable()) { results.add(e.getKey().getName()); } return results; } Consultas de representacin de propiedades Una consulta __property__ que no contenga solo claves devuelve informacin adicional sobre las representaciones que utiliza cada par de tipo-propiedades. Las representaciones no son clases de propiedades: varias clases de propiedades se asignan a la misma representacin (p. ej., com.google.appengine.api.datastore.Blob y java.lang.String utilizan la representacin "STRING"). En la tabla siguiente se indican las asignaciones de las clases Property a las representaciones:

java.lang.String java.lang.Byte java.lang.Short java.lang.Integer java.lang.Long java.lang.Float java.lang.Double java.lang.Boolean com.google.appengine.api.users.User com.google.appengine.api.datastore.Key com.google.appengine.api.datastore.Blob com.google.appengine.api.datastore.Text java.util.Date com.google.appengine.api.datastore.Link com.google.appengine.api.datastore.ShortBlob com.google.appengine.api.datastore.GeoPt com.google.appengine.api.datastore.Category com.google.appengine.api.datastore.Rating

STRING INT64 INT64 INT64 INT64 DOUBLE DOUBLE BOOLEAN USER REFERENCE STRING STRING INT64 STRING STRING POINT STRING STRING

com.google.appengine.api.datastore.PhoneNumber STRING com.google.appengine.api.datastore.PostalAddress STRING com.google.appengine.api.datastore.Email com.google.appengine.api.datastore.IMHandle com.google.appengine.api.blobstore.BlobKey java.util.Collection STRING STRING STRING representacin de T

Las entidades que devuelve una consulta __property__ que no contiene solo claves para la propiedad P de tipo K tienen: la misma clave que la consulta que solo incluye claves, una propiedad property_representation que es java.util.Collection<String>, que contiene una representacin String para dicha propiedad en una entidad del tipo.

La sencilla funcin que se muestra a continuacin busca todas las representaciones de una propiedad concreta en un tipo: import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Query; import java.util.Collection; Collection representationsOf(DatastoreService ds, String kind, String property) { Query q = new Query(Query.PROPERTY_METADATA_KIND); q.setAncestor(makePropertyKey(kind, property)); Entity propInfo = ds.prepare(q).asSingleEntity(); return (Collection) propInfo.getProperty("property_representation"); } Utilizacin de cuota Las consultas de metadatos utilizan ms cuota que las consultas normales. Como normal general, una consulta de metadatos que devuelve N entidades tendr aproximadamente el mismo coste que N consultas que devuelvan una entidad cada una de ellas. Adems, las consultas de representacin de propiedades (consultas __property__ no solo basadas en claves) resultan ms costosas que las consultas __property__ solo basadas en claves. En general, las consultas de metadatos son ms lentas que las normales.

Estadsticas del almacn de datos de Java El almacn de datos contiene estadsticas sobre los datos almacenados de una aplicacin como, por ejemplo, la cantidad de entidades de un determinado tipo o el espacio que utilizan los valores de propiedad de un tipo en concreto. Encontrars estas estadsticas en "Almacn de datos" > "Estadsticas" de la Consola del administrador. Tambin puedes acceder a estos valores mediante un programa desde la aplicacin. Para ello, basta con realizar una consulta de entidades especficas mediante el API del almacn de datos. A cada una de las estadsticas se accede como entidad cuyo nombre de tipo empieza y termina con dos caracteres de subrayado. Por ejemplo, cada aplicacin incluye exactamente una entidad del tipo __Stat_Total__ que representa estadsticas de todas las entidades del almacn de datos. Las entidades con estadsticas incluyen las propiedades siguientes: count, el nmero de elementos que determina la estadstica (nmero entero largo), bytes, el tamao total de los elementos de esta estadstica (nmero entero largo), timestamp, la ltima hora en que se ha modificado la estadstica (valor de fecha-hora).

Algunos tipos de estadsticas incluyen tambin propiedades adicionales, las cuales se enumeran a continuacin. Una aplicacin Java puede acceder a las entidades con estadsticas mediante el API del almacn de datos de nivel inferior. Por ejemplo: import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Query; // ... DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Entity globalStat = datastore.prepare(new Query("__Stat_Total__")).asSingleEntity(); Long totalBytes = (Long) globalStat.getProperty("bytes"); Long totalEntities = (Long) globalStat.getProperty("count"); Cuando el sistema de estadsticas crea nuevas entidades con estadsticas, no elimina las antiguas en seguida. La mejor forma de obtener una visin global de las estadsticas es realizar una consulta de la entidad __Stat_Total__ con la marca de tiempo (timestamp) ms reciente y, a continuacin, utilizar el valor de esta como filtro a la hora de extraer otras entidades con estadsticas. Las entidades con estadsticas se incluyen en los valores de estadsticas que se calculan. Estas entidades ocupan espacio relativo al nmero de tipos y de nombres de propiedad que utiliza la aplicacin. A continuacin, se incluye la lista completa de estadsticas disponibles: Estadstica Todas las entidades Tipo de entidad con estadstica __Stat_Total__ Descripcin Todas las entidades Entidades de un tipo; una entidad con estadsticas para cada tipo de entidad almacenado Propiedades adicionales: kind_name, el nombre del tipo representado (cadena)

Entidades de un tipo

__Stat_Kind__

Entidades raz de un tipo

__Stat_Kind_IsRootEntity__

Entidades de un tipo que son entidades raz del grupo de entidades (sin entidad principal de ancestro); una entidad con estadsticas para cada tipo de entidad almacenado Propiedades adicionales: kind_name, el nombre del tipo representado (cadena)

Entidades de un tipo sin raz

__Stat_Kind_NotRootEntity__

Entidades de un tipo que no son entidades raz del grupo de entidades (con entidad principal de ancestro); una entidad con estadsticas para cada tipo de entidad almacenado Propiedades adicionales: kind_name, el nombre del tipo representado (cadena)

Propiedades de un tipo __Stat_PropertyType__

Propiedades de un tipo de valor en todas las entidades; una entidad con estadsticas por tipo de valor Propiedades adicionales: property_type, el nombre del tipo de valor (cadena)

Propiedades de un tipo __Stat_PropertyType_Kind__ por tipo

Propiedades de un tipo de valor en entidades del tipo especificado; una entidad con estadsticas por cada combinacin de tipo de propiedad y de tipo Propiedades adicionales: property_type, el nombre del tipo de valor (cadena) kind_name, el nombre del tipo representado (cadena)

Propiedades con un nombre

Propiedades con un determinado nombre en entidades de un tipo especfico; una entidad con estadsticas por combinacin de nombre de propiedad y tipo exclusivos Propiedades adicionales: __Stat_PropertyName_Kind__ property_name, el nombre de la propiedad (cadena) kind_name, el nombre del tipo representado (cadena)

Propiedades con un determinado nombre y de un tipo de valor especfico en entidades de un tipo determinado; una entidad con estadsticas por combinacin de nombre de propiedad, tipo de valor y tipo incluido en el almacn de datos Propiedades adicionales: Propiedades de un tipo __Stat_PropertyType_PropertyName_Kind__ y con un nombre property_type, el nombre del tipo de valor (cadena) property_name, el nombre de la propiedad (cadena) kind_name, el nombre del tipo representado (cadena) Algunas estadsticas hacen referencia a los tipos de valor de propiedad del almacn de datos por nombre, en forma de cadenas. Estos son los nombres: "Blob" "Boolean" "ByteString" "Category" "Date/Time" "Email" "Float" "GeoPt" "Integer" "Key" "Link" "NULL" "PhoneNumber" "PostalAddress" "Rating" "String" "Text" "User"

API de servicio asncrono del almacn de datos El API de servicio asncrono del almacn de datos permite realizar varias invocaciones a la vez, sin que se bloqueen entre s, al almacn de datos y obtener los resultados de dichas invocaciones ms adelante, cuando se procese la solicitud. En este documento se describen los siguientes aspectos del API de servicio asncrono del almacn de datos: Uso del servicio asncrono del almacn de datos Uso de transacciones asncronas Uso del mtodo Future Cundo realizar invocaciones del almacn de datos usando el servicio asncrono Consultas asncronas

Uso del servicio asncrono del almacn de datos A travs del API de servicio asncrono del almacn de datos, puedes realizar invocaciones al almacn de datos usando los mtodos de la interfaz de AsyncDatastoreService. Este objeto se obtiene invocando el mtodo getAsyncDatastoreService() de la clase DatastoreServiceFactory. import com.google.appengine.api.datastore.AsyncDatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; // ... AsyncDatastoreService datastore = DatastoreServiceFactory.getAsyncDatastoreService(); AsyncDatastoreService admite las mismas operaciones que DatastoreService, con la excepcin de que la mayora de los mtodos devuelve de forma inmediata un mtodo Future cuyo resultado puedes bloquear posteriormente. Por ejemplo, DatastoreService.get() devuelve Entity, pero AsyncDatastoreService.get() devuelve Future<Entity>. // ... Key key = KeyFactory.createKey("Employee", "Max"); // Async call returns immediately Future<Entity> entityFuture = datastore.get(key); // Do other stuff while the get operation runs in the background... // Blocks if the get operation has not finished, otherwise returns instantly Entity entity = entityFuture.get(); Si tienes un servicio AsyncDatastoreService pero debes ejecutar una operacin de forma sincronizada, invoca el mtodo AsyncDatastoreService correspondiente y, acto seguido, bloquea el resultado: // ... Entity entity = new Employee("Employee", "Alfred"); // ... populate entity properties // Make a sync call via the async interface Key key = datastore.put(key).get(); Uso de transacciones asncronas Las invocaciones del API de servicio asncrono del almacn de datos pueden participar en las transacciones, al igual que las invocaciones sincronizadas. A continuacin, te indicamos una funcin que sirve para ajustar el salario de un empleado (Employee) y escribe una entidad SalaryAdjustment adicional en el mismo grupo de entidades que Employee; todo en una sola transaccin. void giveRaise(AsyncDatastoreService datastore, Key employeeKey, long raiseAmount) throws Exception { Future<Transaction> txn = datastore.beginTransaction(); // Async call to lookup the Employee entity Future<Entity> employeeEntityFuture = datastore.get(employeeKey); // Create and put a SalaryAdjustment entity in parallel with the lookup Entity adjustmentEntity = new Entity("SalaryAdjustment", employeeKey); adjustmentEntity.setProperty("adjustment", raiseAmount); adjustmentEntity.setProperty("adjustmentDate", new Date());

Future<Key> = datastore.put(adjustmentEntity); // Fetch the result of our lookup to make the salary adjustment Entity employeeEntity = employeeEntityFuture.get(); long salary = (Long) employeeEntity.getProperty("salary"); employeeEntity.setProperty("salary", salary + raiseAmount); // Re-put the Employee entity with the adjusted salary. datastore.put(employeeEntity); txn.commit(); // could also call txn.commitAsync() here } En este ejemplo se muestra una diferencia importante entre las invocaciones asncronas sin transacciones y aquellas con transacciones. Cuando no se utilice una transaccin, la nica forma de asegurarse de que una invocacin asncrona determinada se haya realizado es extraer el valor del mtodo Future que se devolvi al realizar la invocacin. Pero si utilizas una transaccin, al invocar Transaction.commit(), se bloquea el resultado de todas las invocaciones asncronas realizadas, ya que la transaccin empez antes de ejecutarse. Por lo tanto, en el ejemplo anterior, aunque es posible que la invocacin asncrona para incluir la entidad SalaryAdjustment an quede pendiente cuando invoques txn.commit(), esta ltima invocacin se realizar una vez que dicha entidad se haya incluido. Asimismo, si decides invocar txn.commitAsync() en lugar de txn.commit() invocando get() con la cadena Future que devuelve commitAsync(), se obtendrn resultados cuando todas las invocaciones asncronas hayan finalizado. Nota: las transacciones van asociadas a un subproceso determinado, no a una instancia concreta de DatastoreService o de AsyncDatastoreService. Esto significa que si inicias una transaccin con DatastoreService y, adems, realizas una invocacin asncrona con AsyncDatastoreService, esta invocacin forma parte de la transaccin. O, dicho de modo ms resumido, DatastoreService.getCurrentTransaction() y AsyncDatastoreService.getCurrentTransaction() siempre devuelven la misma transaccin (Transaction). Uso del mtodo Future En la referencia de Java sobre Future se explica casi todo lo que debes saber para usar el mtodo Future que devuelve el API de servicio asncrono del almacn de datos. Aun as, tambin debes tener en cuenta los siguientes aspectos relacionados con App Engine: Cuando invoques Future.get(long timeout, TimeUnit unit), el tiempo de espera es independiente de cualquier tiempo lmite que se haya especificado para las invocaciones a procedimientos remotos al crear AsyncDatastoreService. Para obtener ms informacin, consulta la documentacin sobre tiempos lmite para las invocaciones a procedimientos remotos del almacn de datos. Cuando invoques Future.cancel(boolean mayInterruptIfRunning) y obtengas true como resultado, el estado de tu almacn de datos no tiene por qu seguir siendo el mismo. Dicho de otro modo, la cancelacin de Future no es lo mismo que la cancelacin de una transaccin.

Cundo realizar invocaciones del almacn de datos usando el servicio asncrono Las operaciones de la interfaz de DatastoreService son sincronizadas. Por ejemplo, al invocar DatastoreService.get(), el cdigo se bloquea hasta que la invocacin del almacn de datos finaliza. Si lo nico que necesita hacer tu aplicacin es mostrar el resultado de get() en HTML, es lgico que se produzca dicho bloqueo. Si, por el contrario, tu aplicacin necesita los resultados de get() y de Query para mostrar la respuesta y, adems, get() y Query no tienen datos asociados, entonces la espera hasta que get() finalice para iniciar Query no sera necesaria. A continuacin, incluimos un ejemplo con cdigo que puede mejorarse mediante el API de servicio asncrono: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Key empKey = KeyFactory.createKey("Employee", "Max"); // Read employee data from the Datastore Entity employee = datastore.get(empKey); // Blocking for no good reason! // Fetch payment history Query query = new Query("PaymentHistory"); PreparedQuery pq = datastore.prepare(query); List<Entity> result = pq.asList(FetchOptions.Builder.withLimit(10)); renderHtml(employee, result);

En lugar de esperar a que get() se complete, utiliza una instancia de AsyncDatastoreService para realizar la invocacin de forma asncrona: AsyncDatastoreService datastore = DatastoreServiceFactory.getAsyncDatastoreService(); Key empKey = KeyFactory.createKey("Employee", "Max"); // Read employee data from the Datastore Future<Entity> employeeFuture = datastore.get(empKey); // Returns immediately! // Fetch payment history for the employee Query query = new Query("PaymentHistory", empKey); PreparedQuery pq = datastore.prepare(query); // Run the query while the employee is being fetched List<Entity> result = pq.asList(FetchOptions.Builder.withLimit(10)); Entity employee = employeeFuture.get(); // Blocking! renderHtml(employee, result); Las versiones sincronizadas y asncronas de este cdigo usan una cantidad de CPU similar (despus de todo, ambas llevan a cabo la misma cantidad de trabajo). Sin embargo, dado que la versin asncrona permite que se ejecuten las dos operaciones del almacn de datos al mismo tiempo, la versin asncrona tiene una latencia menor. Por lo general, si debes realizar varias operaciones en el almacn de datos que no estn asociadas a ningn dato, el servicio AsyncDatastoreService puede aumentar la latencia significativamente. Consultas asncronas Actualmente no disponemos de un API de servicio asncrono especfica para las consultas. Sin embargo, cuando invoquesPreparedQuery.asIterable() o PreparedQuery.asIterator(), tanto DatastoreService como AsyncDatastoreService iniciarn la consulta en segundo plano con una invocacin asncrona y se obtendrn resultados inmediatamente. De este modo, la aplicacin puede trabajar en paralelo mientras se extrae el primer conjunto de resultados de la consulta. // ... Query q1 = new Query("Salesperson"); q1.addFilter("dateOfHire", FilterOperator.LESS_THAN, oneMonthAgo); // Returns instantly, query is executing in the background. Iterable<Entity> recentHires = datastore.prepare(q1).asIterable(); Query q2 = new Query("Customer"); q2.addFilter("lastContact", FilterOperator.GREATER_THAN, oneYearAgo); // Also returns instantly, query is executing in the background. Iterable<Entity> needsFollowup = datastore.prepare(q2).asIterable(); schedulePhoneCall(recentHires, needsFollowUp); Lamentablemente, PreparedQuery.asList() no funciona de la misma manera. Esperamos poder solucionar esto en una prxima versin. Uso de JDO En esta seccin se describe la implementacin de objetos de datos Java (JDO, Java Data Objects) en App Engine. Presenta la siguiente estructura: Aspectos generales Definicin de clases de datos con JDO Creacin, obtencin y eliminacin de datos en JDO Relaciones de entidad en JDO Consultas en JDO

Uso de JDO con App Engine La interfaz de objetos de datos de Java (JDO) es una interfaz estndar para almacenar objetos con datos en una base de datos. El estndar permite que las interfaces realicen tareas de anotacin de objetos Java, de recuperacin de objetos con consultas y de interaccin con una base de datos mediante transacciones. Una aplicacin que utilice la interfaz de JDO puede funcionar con distintos tipos de bases de datos

sin usar ningn cdigo especfico para estas, incluidas las bases de datos relacionales, jerrquicas y de objetos. Al igual que otros estndares de interfaz, JDO simplifica la transferencia de tu aplicacin entre diferentes soluciones de almacenamiento. El SDK de Java de App Engine incluye JDO 2.3 para el almacn de datos de App Engine. Esta implementacin se basa en DataNucleus Access Platform, la implementacin de referencia de cdigo abierto para JDO 2.3. Consulta la documentacin de Access Platform 1.1 para obtener ms informacin sobre JDO. En concreto, consulta las secciones "JDO Mapping" (Asignacin de JDO) y "JDO API" (API de JDO). Configuracin de JDO Mejora de las clases de datos Obtencin de una instancia de PersistenceManager Funciones no disponibles en JDO Inhabilitacin de transacciones y transferencia de aplicaciones de JDO existentes

Configuracin de JDO Si quieres usar JDO para acceder al almacn de datos, necesitas lo siguiente para la aplicacin de App Engine: Los archivos de JDO y del plug-in DataNucleus de App Engine deben ubicarse en el directorio war/WEB-INF/lib/ de la aplicacin. Debe haber un archivo de configuracin llamado jdoconfig.xml en el directorio war/WEB-INF/classes/METAINF/ de la aplicacin cuya configuracin solicite a JDO el uso del almacn de datos de App Engine. El proceso de creacin del proyecto requiere una mejora posterior a la compilacin en las clases de datos compilados para asociarlas con la implementacin JDO.

Si utilizas Google Plugin for Eclipse, el plug-in realiza estos tres pasos por ti. El asistente de nuevos proyectos coloca los archivos JAR de JDO y del plug-in DataNucleus de App Engine en la ubicacin correcta y, a continuacin, crea el archivo jdoconfig.xml. Durante el proceso de creacin se realiza la mejora de forma automtica. Si utilizas Apache Ant para crear tu proyecto, puedes utilizar una tarea de Ant incluida con el SDK para realizar la mejora. Debes copiar los archivos JAR y crear el archivo de configuracin cuando configures tu proyecto. Consulta la seccin Uso de Apache Ant para obtener ms informacin sobre dicha tarea. Copia de los archivos JAR Los archivos JAR de JDO y del almacn de datos se incluyen en el SDK de Java de App Engine; los encontrars en el directorio appengine-java-sdk/lib/user/orm/. Copia los archivos JAR en el directorio war/WEB-INF/lib/ de tu aplicacin. Asegrate de que appengine-api.jar tambin se encuentre en el directorio war/WEB-INF/lib/ (es posible que ya lo hayas copiado al crear el proyecto). El plug-in DataNucleus de App Engine utiliza este archivo JAR para acceder al almacn de datos. Creacin del archivo jdoconfig.xml. La interfaz de JDO requiere un archivo de configuracin llamado jdoconfig.xml en el directorio war/WEBINF/classes/META-INF/ de la aplicacin. Puedes crear directamente este archivo en dicha ubicacin o hacer que se copie desde un directorio determinado durante el proceso de creacin. Debes crear el archivo con el siguiente contenido: <?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>

<property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> </persistence-manager-factory> </jdoconfig> Configuracin de la poltica de lectura y el tiempo lmite de la llamada al almacn de datos Tal como se describe en los aspectos generales, puedes configurar la poltica de lectura (consistencia fuerte o consistencia eventual) y el tiempo lmite para la invocacin al almacn de datos para PersistenceManagerFactory en el archivo jdoconfig.xml. Esta configuracin se incluye en el elemento <persistence-manager-factory>. Todas las invocaciones realizadas con una instancia de PersistenceManager determinada usan la configuracin especificada cuando PersistenceManagerFactory cre el gestor. Tambin puedes saltarte estas opciones cuando se trate de un objeto Query nico. Para establecer una poltica de lectura para PersistenceManagerFactory, incluye una propiedad denominada datanucleus.appengine.datastoreReadConsistency. Esta admite los valores EVENTUAL (para lecturas con consistencia eventual) y STRONG (para lecturas con consistencia fuerte). Si no se especifica, el valor predeterminado es STRONG. <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" /> Puedes definir distintos tiempos lmite en las invocaciones del almacn de datos tanto para procesos de lectura como de escritura. Para procesos de lectura, usa la propiedad estndar de JDO javax.jdo.option.DatastoreReadTimeoutMillis; y para procesos de escritura, usa javax.jdo.option.DatastoreWriteTimeoutMillis. El valor es una cantidad de tiempo expresada en milisegundos. <property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" /> <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" /> Puedes tener varios elementos <persistence-manager-factory> en el mismo archivo jdoconfig.xml, con distintos atributos name, para usar instancias de PersistenceManager con diferentes configuraciones en la misma aplicacin. Por ejemplo, el siguiente archivo jdoconfig.xml establece dos conjuntos de configuracin; uno se denomina "transactions-optional", y el otro "eventual-reads-short-deadlines": <?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> </persistence-manager-factory> <persistence-manager-factory name="eventual-reads-short-deadlines"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" /> <property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" /> <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" /> </persistence-manager-factory> </jdoconfig>

Consulta la seccin Obtencin de una instancia de PersistenceManager a continuacin para obtener informacin de cmo crear PersistenceManager seleccionando el nombre de la configuracin deseada. Mejora de las clases de datos JDO realiza una mejora tras la compilacin durante el proceso de creacin para asociar las clases de datos a la implementacin de JDO. Si utilizas Eclipse, Google Plugin for Eclipse lleva a cabo esta operacin de forma automtica durante la creacin. Si usas Apache Ant, el SDK incluye una tarea de Ant para realizar este paso. Consulta la seccin Uso de Apache Ant para obtener ms informacin sobre dicha tarea. Puedes realizar la operacin de mejora en clases compiladas desde la lnea de comandos con el siguiente comando: java -cp classpath com.google.appengine.tools.enhancer.Enhance class-files classpath debe contener el archivo JAR appengine-tools-api.jar del directorio appengine-java-sdk/lib/, as como todas tus clases de datos. Para obtener ms informacin sobre el programa de mejora en bytecode DataNucleus, consultala documentacin de DataNucleus. Obtencin de una instancia de PersistenceManager Una aplicacin interacta con JDO utilizando una instancia de la clase PersistenceManager. Esta instancia se obtiene al reproducir e invocar un mtodo en una instancia de la clase PersistenceManagerFactory. La fbrica utiliza la configuracin de JDO para crear instancias de PersistenceManager. Dado que la instancia de PersistenceManagerFactory tarda un tiempo en iniciarse, las aplicaciones deberan reutilizar siempre la misma instancia. Para garantizar que esto suceda, se genera una excepcin cuando la aplicacin crea ms de una instancia de PersistenceManagerFactory (con el mismo nombre de configuracin). Una forma sencilla de gestionar la instancia de PersistenceManagerFactory es crear una clase envoltorio nica con una instancia esttica. Por ejemplo: PMF.java import javax.jdo.JDOHelper; import javax.jdo.PersistenceManagerFactory; public final class PMF { private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional"); private PMF() {} public static PersistenceManagerFactory get() { return pmfInstance; } } Consejo: "transactions-optional" hace referencia al nombre de la configuracin en el archivo jdoconfig.xml. Si tu aplicacin usa varias configuraciones, debes ampliar este cdigo para invocar JDOHelper.getPersistenceManagerFactory() como quieras. El cdigo debera incluir en la memoria cach una instancia nica de cada PersistenceManagerFactory. La aplicacin utiliza la instancia de la fbrica para crear una instancia de PersistenceManager por cada solicitud que acceda al almacn de datos. import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; import PMF; // ... PersistenceManager pm = PMF.get().getPersistenceManager();

PersistenceManager se usa para almacenar, actualizar y eliminar objetos de datos, as como para realizar consultas al almacn de datos. Cuando acabes con la instancia de PersistenceManager, debes invocar su mtodo close(). Sera un error usar la instancia de PersistenceManager despus de invocar su mtodo close(). try { // ... do stuff with pm ... } finally { pm.close(); } Funciones no disponibles en JDO Las siguientes funciones de la interfaz de JDO no se encuentran disponibles en la implementacin de App Engine: Relaciones sin propiedad. Puedes implementar relaciones sin propiedad mediante valores Key explcitos. Es posible que la sintaxis de JDO para relaciones sin propiedad se incluya en prximas versiones. Relaciones de propiedad multidireccionales. Consultas "Join". No puedes usar el campo de una entidad secundaria en un filtro al realizar una consulta del tipo principal. Ten en cuenta que puedes probar el campo de relacin del elemento principal directamente en las consultas mediante una clave. Consultas de agrupacin JDOQL y otras consultas de agrupacin conjunta. Consultas polimrficas. No puedes realizar consultas de una clase para obtener instancias de una subclase. Cada clase se representa mediante un tipo de entidad independiente en el almacn de datos. IdentityType.DATASTORE para la anotacin @PersistenceCapable. Solo se admite IdentityType.APPLICATION. Actualmente, existe un error que evita que haya relaciones de propiedad de uno a varios cuando el elemento principal y el secundario pertenecen a la misma clase, lo cual dificulta el modelo de las estructuras en rbol. Esto se solucionar en futuras versiones. Como solucin provisional, puedes almacenar los valores de Key explcitos para el elemento principal o para el secundario.

Inhabilitacin de transacciones y transferencia de aplicaciones de JDO existentes La configuracin de JDO que recomendamos define una propiedad llamada datanucleus.appengine.autoCreateDatastoreTxns con el valor true. Se trata de una propiedad especfica de App Engine que ordena a la implementacin de JDO que asocie las transacciones del almacn de datos a las transacciones de JDO que se administran en el cdigo de la aplicacin. Si creas una nueva aplicacin desde el principio, probablemente sea esto lo que buscas. Sin embargo, si tienes una aplicacin basada en JDO que quieras ejecutar en App Engine, te recomendamos que uses una configuracin de persistencia alternativa que asigne el valor false a esta propiedad: <?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="false"/> </persistence-manager-factory> </jdoconfig> Si quieres saber por qu esta opcin puede resultarte til, recuerda que solo puedes realizar operaciones sobre objetos que pertenezcan al mismo grupo de entidades dentro de una transaccin. Las aplicaciones creadas usando bases de datos tradicionales normalmente requieren el uso de transacciones globales, lo cual te permite actualizar cualquier conjunto de datos dentro de una transaccin. Dado que no se puede realizar transacciones globales en el almacn de datos de App Engine, el programa genera excepciones cada vez que el cdigo requiera transacciones globales. En lugar de acceder a la base de cdigo (que puede ser enorme) y eliminar todo el cdigo relativo a la administracin de transacciones, simplemente puedes inhabilitar las transacciones del almacn de datos. Con esta solucin, el cdigo seguir gestionando la atomicidad de las modificaciones multiregistro del mismo modo. No obstante, la aplicacin seguir funcionando para que puedas centrarte en la refactorizacin gradual del cdigo de transacciones segn sea necesario en lugar de hacerlo todo a la vez.

Definicin de clases de datos con JDO Puedes usar JDO para almacenar objetos de datos simples de Java (a veces denominados Plain Old Java Objects o POJO) en el almacn de datos. Cada objeto que se haga persistente con PersistenceManager se convertir en una entidad del almacn de datos. Debes usar anotaciones para informar a JDO cmo almacenar y recrear instancias de tus clases de datos. Nota: versiones anteriores de JDO usan archivos XML .jdo en lugar de anotaciones de Java. Estas an funcionan con JDO 2.3. En este documento solo se tratan las anotaciones de Java con las clases de datos. Anotaciones de clase y de campo Tipos de valor principales Objetos serializables Objetos secundarios y relaciones Clases insertadas Colecciones Campos de objeto y propiedades de entidad Herencias

Anotaciones de clase y de campo Cada objeto que guarda JDO se convierte en una entidad del almacn de datos de App Engine. El tipo de la entidad se obtiene a partir del nombre simple de la clase (las clases internas usan la ruta $ sin el nombre del paquete). Cada campo persistente de la clase representa una propiedad de la entidad, con un nombre de propiedad idntico al nombre del campo (con distincin de maysculas y minsculas). Para declarar una clase Java como almacenable y recuperable en el almacn de datos con JDO, asigna una anotacin @PersistenceCapable a la clase. Por ejemplo: import javax.jdo.annotations.PersistenceCapable; @PersistenceCapable public class Employee { // ... } Los campos de la clase de datos que deben guardarse en el almacn de datos han de declararse como campos persistentes. Para declarar un campo como persistente, asgnale una anotacin @Persistent: import java.util.Date; import javax.jdo.annotations.Persistent; // ... @Persistent private Date hireDate; En cambio, para declarar un campo como no persistente (es decir, que no se guarda en el almacn de datos ni puede recuperarse al obtener el objeto), asgnale una anotacin @NotPersistent. Consejo: JDO especifica que los campos de determinados tipos son persistentes de forma predeterminada cuando no se determinan las anotaciones @Persistent y @NotPersistent, y que los campos de los dems tipos no son persistentes. Consulta la documentacin de DataNucleus para obtener una descripcin detallada de este comportamiento. Dado que no todos los tipos de valor principales del almacn de datos de App Engine son persistentes de forma predeterminada segn se especifica en JDO, te recomendamos que anotes los campos como @Persistent o @NotPersistent de forma explcita para que quede claro. A continuacin, se indican los tipos de campo existentes. Encontrars una descripcin detallada de estos ms abajo: uno de los tipos principales admitidos por el almacn de datos, una coleccin (como java.util.List<...>) o un conjunto de valores de un tipo principal del almacn de datos, una instancia o una coleccin de instancias de una clase @PersistenceCapable, una instancia o una coleccin de instancias de una clase serializable, una clase insertada, almacenada como propiedades en la entidad.

Una clase de datos debe tener un campo dedicado al almacenamiento de la clave principal de la entidad correspondiente en el almacn de datos. Puedes elegir entre cuatro tipos de campo de clave distintos, cada uno de los cuales cuenta con un tipo de valor y anotaciones

diferentes. Consulta la seccin Creacin de datos: claves para obtener ms informacin. El tipo de campo de clave ms flexible es el objeto Key, que JDO rellena automticamente con un valor nico respecto al resto de las instancias de la clase cuando el objeto se guarda en el almacn de datos por primera vez. Las claves principales del tipo Key requieren anotaciones @PrimaryKey y @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY): Consejo: haz que todos tus campos sean privados (private) o protegidos (protected), o con proteccin de paquete, y solo proporciona acceso pblico a travs de mtodos de acceso. Es posible que el acceso directo a un campo persistente desde otra clase pueda evitar la mejora de clases de JDO, aunque tambin puedes crear otras clases @PersistenceAware. Consulta la documentacin de DataNucleus para obtener ms informacin. import com.google.appengine.api.datastore.Key; import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.PrimaryKey; // ... @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; A continuacin, te ofrecemos un ejemplo de clase de datos: import com.google.appengine.api.datastore.Key; import import import import import java.util.Date; javax.jdo.annotations.IdGeneratorStrategy; javax.jdo.annotations.PersistenceCapable; javax.jdo.annotations.Persistent; javax.jdo.annotations.PrimaryKey;

@PersistenceCapable public class Employee { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private String firstName; @Persistent private String lastName; @Persistent private Date hireDate; public Employee(String firstName, String lastName, Date hireDate) { this.firstName = firstName; this.lastName = lastName; this.hireDate = hireDate; } // Accessors for the fields. public Key getKey() { return key; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } JDO doesn't use these, but your application does.

public void setLastName(String lastName) { this.lastName = lastName; } public Date getHireDate() { return hireDate; } public void setHireDate(Date hireDate) { this.hireDate = hireDate; } } Tipos de valor principales Para representar una propiedad que contenga un nico valor de un tipo principal, incluye un campo del tipo Java y usa la anotacin @Persistent: import java.util.Date; import javax.jdo.annotations.Persistent; // ... @Persistent private Date hireDate; Objetos serializables Un valor de campo puede contener una instancia de una clase serializable y almacenar el valor serializado de la instancia en un nico valor de propiedad del tipo blob. Para que JDO serialice el valor, el campo utiliza la anotacin @Persistent(serialized=true). Los valores blob no se indexan y no se pueden utilizar en criterios de ordenacin ni en filtros de consultas. A continuacin, ofrecemos un ejemplo de una clase Serializable simple que representa un archivo que incluye el contenido y el nombre de este, as como un tipo MIME. No se trata de una clase de datos JDO, por lo que no hay anotaciones de persistencia. import java.io.Serializable; public class DownloadableFile implements Serializable { private byte[] content; private String filename; private String mimeType; // ... accessors ... } Para almacenar una instancia de una clase Serializable como valor blob en una propiedad, incluye un campo cuyo tipo coincida con la clase y usa la anotacin @Persistent(serialized = "true"): import javax.jdo.annotations.Persistent; import DownloadableFile; // ... @Persistent(serialized = "true") private DownloadableFile file; Objetos secundarios y relaciones Un valor de campo que sea una instancia de una clase @PersistenceCapable crear una relacin de propiedad de uno a uno entre dos objetos. Sin embargo, un campo que sea una coleccin de estas referencias crear una relacin de propiedad de uno a varios objetos. Importante: las relaciones de propiedad influyen en las transacciones, en los grupos de entidades y en las eliminaciones en cascada. Consulta las secciones Transacciones y Relaciones para obtener ms informacin. A continuacin, presentamos un ejemplo de relacin de propiedad de uno a uno entre dos objetos, Employee y ContactInfo: ContactInfo.java

import com.google.appengine.api.datastore.Key; // ... imports ... @PersistenceCapable public class ContactInfo { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private String streetAddress; @Persistent private String city; @Persistent private String stateOrProvince; @Persistent private String zipCode; // ... accessors ... } Employee.java import ContactInfo; // ... imports ... @PersistenceCapable public class Employee { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private ContactInfo myContactInfo; // ... accessors ... } En este ejemplo, si la aplicacin crea una instancia de Employee, se rellena el campo myContactInfo con una nueva instancia de ContactInfo. A continuacin, se guarda la instancia Employee con pm.makePersistent(...) y el almacn de datos crea dos entidades. Una del tipo "ContactInfo", que representa la instancia de ContactInfo; y la otra, del tipo "Employee". La clave de la entidad ContactInfo tiene la clave de la entidad Employee como grupo de entidades principal. Clases insertadas Las clases insertadas te permiten configurar un valor de campo mediante una clase sin necesidad de crear una nueva entidad en el almacn de datos y de establecer una relacin. Los campos del valor del objeto se guardan directamente en la entidad del almacn de datos cuando se trata de un objeto contenido. Cualquier clase de datos @PersistenceCapable puede usarse como objeto insertado en otra clase de datos. Los campos @Persistent de la clase se insertan en el objeto. Si asignas la anotacin @EmbeddedOnly a la clase que debe insertarse, la clase solo puede usarse como clase insertada. La clase insertada no requiere un campo de clave principal porque no se almacena como una entidad separada. A continuacin, presentamos un ejemplo de clase insertada. En este ejemplo se convierte la clase insertada en una clase interna de la clase de datos que la usa. Es algo til, aunque no es necesario para que una clase pueda insertarse. import javax.jdo.annotations.Embedded; import javax.jdo.annotations.EmbeddedOnly; // ... imports ... @PersistenceCapable public class EmployeeContacts {

@PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) Key key; @PersistenceCapable @EmbeddedOnly public static class ContactInfo { @Persistent private String streetAddress; @Persistent private String city; @Persistent private String stateOrProvince; @Persistent private String zipCode; // ... accessors ... } @Persistent @Embedded private ContactInfo homeContactInfo; } Los campos de una clase insertada se almacenan como propiedades en la entidad usando el nombre de cada campo y el nombre de la propiedad correspondiente. Si tienes ms de un campo en el objeto cuyo tipo es una clase insertada, debes cambiar el nombre de los campos de uno de los campos. De este modo, no entran en conflicto entre s. Debes especificar nuevos nombres de campo usando argumentos con la anotacin @Embedded. Por ejemplo: @Persistent @Embedded private ContactInfo homeContactInfo; @Persistent @Embedded(members = { @Persistent(name="streetAddress", columns=@Column(name="workStreetAddress")), @Persistent(name="city", columns=@Column(name="workCity")), @Persistent(name="stateOrProvince", columns=@Column(name="workStateOrProvince")), @Persistent(name="zipCode", columns=@Column(name="workZipCode")), }) private ContactInfo workContactInfo; Asimismo, los campos de los objetos no deben llamarse igual que los campos de las clases insertadas, salvo que se cambie el nombre de los campos insertados. Dado que las propiedades persistentes de la clase insertada se guardan en la misma entidad que los otros campos, puedes usar campos persistentes de la clase insertada para filtros y criterios de ordenacin de consultas en JDOQL. Puedes hacer referencia al campo insertado usando el nombre del campo externo, un punto (.) y el nombre del campo insertado. Esto funciona independientemente de si se han cambiado los nombres de propiedad de los campos insertados mediante anotaciones @Column. select from EmployeeContacts where workContactInfo.zipCode == "98105" Colecciones Una propiedad del almacn de datos puede tener ms de un valor. En JDO, esto se representa mediante un nico campo del tipo coleccin en el que la coleccin es de uno de los tipos de valor principales o de una clase serializable. Se admiten los siguientes tipos de coleccin: java.util.ArrayList<...> java.util.HashSet<...> java.util.LinkedHashSet<...> java.util.LinkedList<...> java.util.List<...> java.util.Set<...> java.util.SortedSet<...>

java.util.Stack<...> java.util.TreeSet<...> java.util.Vector<...>

Si un campo se declara como List, los objetos que devuelve el almacn de datos tendrn un valor ArrayList. Si un campo se declara como Set, el almacn de datos devuelve un valor HashSet. Si un campo se declara como SortedSet, el almacn de datos devuelve un valor TreeSet. Por ejemplo, un campo del tipo List<String> puede guardarse sin valores de cadena o con ellos, uno por cada valor de la lista (List). import java.util.List; // ... imports ... // ... @Persistent List<String> favoriteFoods; Una coleccin de objetos secundarios (de clases @PersistenceCapable) crea varias entidades con una relacin de uno a varios. Consulta la seccin Relaciones. Las propiedades del almacn de datos con ms de un valor se comportan de forma especial para los filtros y los criterios de ordenacin de consultas. Puedes obtener informacin detallada en Consultas e ndices: criterios de ordenacin y propiedades con varios valores. Campos de objeto y propiedades de entidad El almacn de datos de App Engine distingue entre entidades sin propiedad asignada y entidades con valor null como propiedad. En cambio, JDO no hace esta distincin; cada campo de objeto tiene un valor, posiblemente null. Si un campo con un tipo de valor anulable (distinto de un tipo integrado como int o boolean) se configura como null, la propiedad de la entidad resultante tendr un valor "null" al guardar el objeto. Si, al cargar una entidad del almacn de datos en un objeto, uno de los campos del objeto no tiene una propiedad y el tipo del campo es un valor nico anulable, el campo quedar configurado como null. Cuando el objeto se guarde de nuevo en el almacn de datos, la propiedad null pasar a establecerse con valor "null" en el almacn de datos. Si el tipo de campo no es de valor anulable, se generar una excepcin al cargar una entidad sin la propiedad correspondiente. Esto no sucede si la entidad se cre a partir de la misma clase de JDO utilizada para volver a crear la instancia, pero s cuando la clase de JDO cambia o si la entidad se cre usando el API de nivel inferior en lugar de JDO. Si el tipo de un campo es una coleccin de tipo de datos principal o una clase Serializable y, adems, la propiedad de la entidad no contiene ningn valor, la coleccin vaca queda representada en el almacn de datos con un valor nulo nico en la propiedad. Si el campo es de tipo Array, se le asigna un conjunto sin ningn elemento. Si el objeto se carga y la propiedad no tiene ningn valor, se le asigna al campo una coleccin vaca del tipo que corresponda. El almacn de datos detecta la diferencia entre una coleccin vaca y una coleccin con un nico valor nulo. Si la entidad tiene una propiedad sin un campo correspondiente en el objeto, no se podr acceder a dicha propiedad desde el objeto. Si el objeto se vuelve a guardar en el almacn de datos, la propiedad adicional se elimina. Si una entidad tiene una propiedad cuyo valor es de un tipo distinto al del campo correspondiente en el objeto, JDO intenta asignar dicho valor al tipo de campo. Si el valor no pudiera asignarse, JDO generara una excepcin ClassCastException. En los casos en que haya nmeros (enteros largos y flotantes de ancho doble), no se asigna el valor, sino que se convierte. Si el valor de la propiedad numrica es mayor que el del tipo de campo, la conversin se lleva a cabo sin que se generen excepciones. Herencia La creacin de clases de datos que utilizan la herencia es algo natural y que puede hacerse con JDO. Antes de seguir leyendo cmo funciona el sistema de herencia de JDO en App Engine, te recomendamos que primero leas la documentacin de DataNucleus al respecto. Ya lo has hecho? Muy bien. La herencia de JDO en App Engine funciona tal como se describe en la documentacin de DataNucleus, pero con algunas restricciones adicionales. Te hablaremos de estas restricciones y, a continuacin, te ofreceremos ejemplos concretos. El mtodo de herencia "new-table" te permite dividir los datos de un nico objeto de datos en varias tablas. Sin embargo, el almacn de datos de App Engine no admite la unin de datos, por lo que esta operacin requiere una invocacin a procedimiento remoto para cada

nivel de herencia. Esto puede llegar a ser muy poco eficaz, por lo que dicho mtodo no es compatible con las clases de datos que no se encuentren en el nivel superior de las jerarquas de herencia. En segundo lugar, la herencia "superclass-table" te permite almacenar los datos de un objeto de datos en la tabla de su superclase. Aunque se trate de una tcnica eficaz en s misma, no es compatible con App Engine. No obstante, es posible que sea compatible en prximas versiones. Sin embargo, hay buenas noticias: las tcnicas "subclass-table" y "complete-table" funcionan tal como se describe en la documentacin de DataNucleus. Adems, puedes usar tambin "new-table" para los objetos de datos que se encuentren en el nivel superior de la jerarqua de herencia. Te ofrecemos un ejemplo a continuacin: Worker.java import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.Inheritance; import javax.jdo.annotations.InheritanceStrategy; import javax.jdo.annotations.PersistenceCapable; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; @PersistenceCapable @Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE) public abstract class Worker { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private String department; } Employee.java // ... imports ... @PersistenceCapable public class Employee extends Worker { @Persistent private int salary; } Intern.java import java.util.Date; // ... imports ... @PersistenceCapable public class Intern extends Worker { @Persistent private Date internshipEndDate; } En este ejemplo, hemos aadido una anotacin @Inheritance a la declaracin de la clase Worker con su atributo strategy> definido como InheritanceStrategy.SUBCLASS_TABLE. Esto hace que JDO guarde todos los campos persistentes de Worker en las entidades del almacn de datos de sus subclases. La entidad del almacn de datos creada como resultado de la invocacin de makePersistent() con una instancia de Employee tiene dos propiedades denominadas "department" y "salary". En cambio, la entidad del almacn de datos creada como resultado de la invocacin de makePersistent() con una instancia de Intern tiene dos propiedades denominadas "department" e "internshipEndDate". El almacn de datos no contiene ninguna entidad del tipo "Worker". Ahora, veamos algo un poco ms interesante. Supn que, adems de tener Employee e Intern, tambin queremos que una especializacin de Employee describa a los empleados que se han marchado de la empresa: FormerEmployee.java import java.util.Date; // ... imports ...

@PersistenceCapable @Inheritance(customStrategy = "complete-table") public class FormerEmployee extends Employee { @Persistent private Date lastDay; } En este ejemplo, hemos aadido una anotacin @Inheritance a la declaracin de la clase FormerEmployee con su atributo custom-strategy> definido como "complete-table". Esto hace que JDO guarde todos los campos persistentes de FormerEmployee y sus superclases en las entidades del almacn de datos que correspondan a las instancias de FormerEmployee. La entidad del almacn de datos creada como resultado de la invocacin de makePersistent() con una instancia de FormerEmployee tiene tres propiedades denominadas "department", "salary" y "lastDay". No hay entidades del tipo "Employee" que se correspondan con FormerEmployee. Sin embargo, si invocas makePersistent() con un objeto cuyo tipo de tiempo de ejecucin sea Employee, crears una entidad del tipo "Employee". La combinacin de relaciones y de herencia es eficaz siempre que los tipos declarados de los campos de tu relacin coincidan con los tipos de tiempo de ejecucin de los objetos que asignas a dichos campos. Consulta la seccin Relaciones polimrficas para obtener ms informacin.

Creacin, obtencin y eliminacin de datos en JDO Para guardar un objeto de datos JDO en el almacn de datos solo tienes que invocar el mtodo makePersistent() de la instancia de PersistenceManager. La implementacin de JDO en App Engine utiliza el campo de clave principal del objeto para realizar un seguimiento de qu entidad del almacn de datos corresponde al objeto de datos. Adems, JDO puede generar claves para objetos nuevos de forma automtica. Puedes usar claves para recuperar entidades rpidamente y crear claves a partir de valores conocidos (como el identificador de una cuenta). Cmo crear objetos persistentes Claves Obtencin de un objeto mediante una clave Actualizacin de un objeto Eliminacin de un objeto

Cmo crear objetos persistentes Para guardar un objeto de datos simple en el almacn de datos, debes invocar el mtodo makePersistent() de PersistenceManager y transmitirle la instancia. PersistenceManager pm = PMF.get().getPersistenceManager(); Employee e = new Employee("Alfred", "Smith", new Date()); try { pm.makePersistent(e); } finally { pm.close(); } La invocacin de makePersistent() se lleva a cabo de forma sincronizada y devolver resultados cuando el objeto se haya guardado y los ndices se hayan actualizado. Para guardar varios objetos en JDO, invoca el mtodo makePersistentAll(...) con una coleccin de objetos. Este mtodo usar una operacin de guardado en lotes de bajo nivel ms eficiente que una serie de invocaciones de makePersistent(...) individuales. Nota: si alguno de los campos persistentes de los objetos de datos hace referencia a otro objeto de datos persistente, y si nunca se ha guardado ni modificado ninguno de esos objetos desde su subida, los objetos a los que se hace referencia tambin se guardan en el almacn de datos. Consulta la seccinRelaciones. Claves

Cada entidad cuenta con una clave que es nica entre todas las entidades de App Engine. Una clave completa incluye varios datos, como el ID de aplicacin, el tipo y un ID de entidad. Las claves tambin contienen informacin sobre grupos de entidades. Consulta la seccin Transacciones para obtener ms informacin. La clave de un objeto se guarda en un campo de la instancia. El campo de clave principal se identifica mediante la anotacin @PrimaryKey. La aplicacin puede facilitar el fragmento de ID de la clave como cadena cuando se crea el objeto o permitir que el almacn de datos genere un ID numrico de forma automtica. La clave completa debe ser nica entre todas las entidades del almacn de datos. Es decir, un objeto debe tener un ID que sea nico entre todos los objetos de un mismo tipo y con el mismo grupo de entidades principal (si procede). Puedes decidir el comportamiento de la clave mediante el tipo de campo y las anotaciones. Si la clase se utiliza como clase secundaria en una relacin, el tipo de campo de la clave debe ser capaz de representar a un grupo de entidades principal, ya sea la instancia de una clave o el valor de una clave codificado como cadena. Consulta las secciones Transacciones para obtener ms informacin sobre los grupos de entidades y Relaciones para obtener ms informacin sobre relaciones. Consejo: si la aplicacin crea un objeto nuevo y le asigna el mismo ID de cadena de otro objeto del mismo tipo (y perteneciente al mismo grupo de entidades principal), cuando guardes el objeto nuevo, este sobrescribir al otro objeto en el almacn de datos. Puedes comprobar si un ID de cadena ya est en uso antes de crear un objeto nuevo. Para ello, usa una transaccin para obtener una entidad con un ID determinado y, luego, crea el ID si este no existe. Consulta la seccin Transacciones. Existen cuatro tipos de campos de clave principal: Long Un nmero entero largo (java.lang.Long), un ID de entidad que genera el almacn de datos de forma automtica. salo para objetos sin grupos de entidades principales cuyos ID deba generarlos automticamente el almacn de datos. El campo de clave largo de una instancia se rellena al guardar dicha instancia. import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; // ... @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; String (cadena sin codificar) Una cadena (java.lang.String), un ID de entidad ("nombre de clave") que facilita la aplicacin cuando se crea el objeto. salo para objetos sin grupos de entidades principales cuyos ID deba facilitarlos la aplicacin. La aplicacin asigna el ID deseado a este campo antes del guardado. import javax.jdo.annotations.PrimaryKey; // ... @PrimaryKey private String name; Key Una instancia de Key (com.google.appengine.api.datastore.Key). El valor de la clave incluye la clave del grupo de entidades principal (si procede), as como el ID de cadena asignado por la aplicacin o el ID numrico generado por el sistema. Para crear un objeto con un ID de cadena asignado por la aplicacin, debes crear el valor de Key con dicho ID y asignar el valor al campo. Para crear el objeto con un ID numrico asignado por el sistema, debes dejar el campo de la clave como "null". Para obtener ms informacin sobre cmo usar grupos de entidades principales, consulta la seccin Transacciones. import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; import com.google.appengine.api.datastore.Key; // ... @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; public void setKey(Key key) {

this.key = key; } La aplicacin puede crear una instancia de Key mediante la clase KeyFactory: import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; // ... Key key = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com"); Employee e = new Employee(); e.setKey(key); pm.makePersistent(e); Key (cadena codificada) Es similar a Key, con la excepcin de que el valor es la clave en forma de cadena codificada. Las claves de cadena codificada permiten escribir tu aplicacin de forma que pueda transferirse y seguir contando con las ventajas de los grupos de entidades del almacn de datos de App Engine. import javax.jdo.annotations.Extension; import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; // ... @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true") private String encodedKey; La aplicacin puede rellenar este valor antes del guardado mediante una clave con un nombre o puede dejarlo como "null". Si el campo de la clave codificada es "null", el sistema genera una clave para el campo al guardar el objeto. Las instancias de la clave pueden convertirse en representaciones de cadena codificada, o partir de estas representaciones, con los mtodos keyToString() y stringToKey() de KeyFactory. Al usar cadenas de clave codificadas, puedes facilitar el acceso a la cadena de un objeto o al ID numrico con un campo adicional: @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true") private String encodedKey; @Persistent @Extension(vendorName="datanucleus", key="gae.pk-name", value="true") private String keyName; // OR: @Persistent @Extension(vendorName="datanucleus", key="gae.pk-id", value="true") private Long keyId; Puedes asignarle un nombre de clave a un campo "gae.pk-name" antes de guardar el objeto. Cuando el objeto se haya guardado, el campo de la clave codificada se rellenar con la clave completa, que incluye el nombre de la clave. Debe ser del tipo String. El campo "gae.pk-id" se rellena al guardar el objeto y no puede modificarse. Debe ser del tipo Long. Cuando se crea un nuevo objeto con una clave generada (un campo de clave que use valueStrategy = IdGeneratorStrategy.IDENTITY), el valor de su clave empieza como null. El campo de la clave se rellena cuando el objeto se escribe en el almacn de datos. Si usas una transaccin, el objeto se escribe cuando esta se produce. De lo contrario, el objeto se escribe cuando se invoca el mtodo makePersistent() durante la creacin del objeto, o cuando se invoca el mtodo close() de la instancia de PersistenceManager durante su actualizacin.

Para obtener ms informacin sobre la creacin de claves, consulta Entidades, propiedades y claves. Obtencin de un objeto mediante una clave Para recuperar un objeto a partir de su clave, utiliza el mtodo getObjectById() de PersistenceManager. El mtodo obtiene la clase del objeto y la clave: Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com"); Employee e = pm.getObjectById(Employee.class, k); Si la clase utiliza un campo de clave que es un ID de cadena sin codificar (String) o un ID numrico (Long), getObjectByID() puede considerar el valor simple como parmetro de la clave: Employee e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com"); El argumento de la clave puede ser de cualquiera de los tipos de campos de clave admitidos (ID de cadena, ID numrico, valor de clave, cadena de clave codificada) y de un tipo distinto al campo de clave de la clase. App Engine debe ser capaz de obtener la clave completa a partir del nombre de la clase y del valor proporcionado. Los ID numricos y de cadena son exclusivos, por lo que una invocacin que utilice un ID numrico nunca devuelve una entidad con un ID de cadena. Si se emplea un valor de Key o una cadena de clave codificada, la clave debe hacer referencia a una entidad cuyo tipo quede representado por la clase. Actualizacin de un objeto Un modo de actualizar un objeto con JDO es extraer el objeto y, a continuacin, modificarlo mientras la interfaz PersistenceManager que ha devuelto el objeto sigue abierta. Los cambios persisten una vez que PersistenceManager se cierre. Por ejemplo: public void updateEmployeeTitle(User user, String newTitle) { PersistenceManager pm = PMF.get().getPersistenceManager(); try { Employee e = pm.getObjectById(Employee.class, user.getEmail()); if (titleChangeIsAuthorized(e, newTitle) { e.setTitle(newTitle); } else { throw new UnauthorizedTitleChangeException(e, newTitle); } } finally { pm.close(); } } Dado que PersistenceManager ha devuelto la instancia de Employee, conoce todas las modificaciones realizadas en los campos persistentes de Employee y actualiza automticamente el almacn de datos con estas modificaciones al cerrarse. Esto se debe a que la instancia de Employee se encuentra ligada a PersistenceManager. Puedes modificar un objeto despus de que PersistenceManager se haya cerrado declarando la clase como independiente. Para ello, aade el atributo detachable a la anotacin @PersistenceCapable: import javax.jdo.annotations.PersistenceCapable; @PersistenceCapable(detachable="true") public class Employee { // ... } De este modo puedes leer y escribir en los campos de un objeto Employee despus de que la interfaz PersistenceManager que lo ha cargado se cierre. En el siguiente ejemplo se muestra la utilidad de un objeto independiente: public Employee getEmployee(User user) { PersistenceManager pm = PMF.get().getPersistenceManager(); Employee employee, detached = null; try { employee = pm.getObjectById(Employee.class, "Alfred.Smith@example.com"); // If you're using transactions, you can call // pm.setDetachAllOnCommit(true) before committing to automatically // detach all objects without calls to detachCopy or detachCopyAll.

detached = pm.detachCopy(employee); } finally { pm.close(); } return detached; } public void updateEmployeeTitle(Employee e, String newTitle) { if (titleChangeIsAuthorized(e, newTitle) { e.setTitle(newTitle); PersistenceManager pm = PMF.get().getPersistenceManager(); try { pm.makePersistent(e); } finally { pm.close(); } } else { throw new UnauthorizedTitleChangeException(e, newTitle); } } Los objetos independientes son una buena alternativa a la creacin de objetos de transferencia de datos. Para obtener ms informacin sobre los objetos independientes, consulta la documentacin de DataNucleus. Eliminacin de un objeto Para eliminar un objeto del almacn de datos, invoca el mtodo deletePersistent() de PersistenceManager con el objeto: pm.deletePersistent(e); Para eliminar varios objetos en JDO, invoca el mtodo deletePersistentAll(...) con una coleccin de objetos. Este mtodo usar una operacin de eliminacin en lotes de bajo nivel ms eficiente que una serie de invocaciones de deletePersistent(...) individuales. Si un objeto tiene campos que contengan objetos secundarios que tambin sean persistentes, tambin se eliminan los objetos secundarios. Consulta la seccin Relaciones para obtener ms informacin. Para eliminar todos los objetos que coincidan con una consulta, puedes usar la funcin de eliminacin mediante consulta de JDOQL. Consulta la seccin Eliminacin de entidades mediante consulta para obtener ms informacin.

Relaciones de entidad en JDO Puedes modelar relaciones entre objetos persistentes mediante campos de tipos de objeto. Una relacin entre objetos persistentes puede definirse como una relacin de propiedad cuando uno de los objetos no puede existir sin el otro, o como relacin sin propiedad cuando ambos objetos pueden existir independientemente de la relacin que tengan. La implementacin de la interfaz de JDO en App Engine puede modelar relaciones de propiedad de uno a uno y relaciones de propiedad de uno a varios, tanto unidireccional como bidireccionalmente. An no se admiten las relaciones sin propiedad con una sintaxis natural, pero puedes administrarlas t mismo guardando directamente claves del almacn de datos en los campos. App Engine crea entidades relacionadas en grupos de entidades de forma automtica para hacer posible la actualizacin de varios objetos relacionados, aunque la aplicacin se encarga de decidir cundo debe usar las transacciones del almacn de datos. Relaciones de propiedad de uno a uno Relaciones de propiedad de uno a varios Relaciones sin propiedad Relaciones, grupos de entidades y transacciones Eliminacin de elementos secundarios dependientes y en cascada Relaciones polimrficas

Relaciones de propiedad de uno a uno

Se crea una relacin unidireccional de propiedad de uno a uno entre dos objetos persistentes mediante un campo cuyo tipo es la clase de la clase relacionada. En el siguiente ejemplo se define una clase de datos ContactInfo y una clase de datos Employee, con una relacin de uno a uno de Employee con ContactInfo. ContactInfo.java import com.google.appengine.api.datastore.Key; // ... imports ... @PersistenceCapable public class ContactInfo { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private String streetAddress; // ... } Employee.java import ContactInfo; // ... imports ... @PersistenceCapable public class Employee { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private ContactInfo contactInfo; ContactInfo getContactInfo() { return contactInfo; } void setContactInfo(ContactInfo contactInfo) { this.contactInfo = contactInfo; } // ... } Los objetos persistentes se representan como dos entidades distintas en el almacn de datos, con dos tipos diferentes. La relacin se representa mediante una relacin de grupo de entidades: la clave del elemento secundario utiliza la clave del elemento principal como su grupo de entidades principal. Cuando la aplicacin accede al objeto secundario usando el campo del objeto principal, la implementacin de JDO solicita el grupo de entidades principal para obtener el elemento secundario. La clase secundaria debe tener un campo de clave cuyo tipo pueda contener la informacin de la clave principal: ya sea Key, o un valor de Key codificado como cadena. Consulta Creacin de datos: claves para obtener informacin sobre los tipos de campo de clave. Se crea una relacin bidireccional de uno a uno mediante campos en ambas clases, con una anotacin en el campo de la clase secundaria para declarar que los campos representan una relacin bidireccional. El campo de la clase secundaria debe tener una anotacin @Persistent con el argumento mappedBy = "...", donde el valor es el nombre del campo en la clase principal. Al rellenar el campo de un objeto, el campo de la referencia correspondiente del otro objeto tambin se rellena automticamente. ContactInfo.java import Employee; // ... @Persistent(mappedBy = "contactInfo") private Employee employee;

Los objetos secundarios se cargan desde el almacn de datos cuando se accede a ellos por primera vez. Si no accedes al objeto secundario en un objeto principal, la entidad del objeto secundario nunca se carga. Si quieres cargar el elemento secundario, puedes "tocarlo" antes cerrando PersistenceManager (p. ej., invocando getContactInfo() en el ejemplo anterior) o puedes aadir el campo secundario de forma explcita en el grupo de extraccin predeterminado para que se obtenga y se cargue con el elemento principal: Employee.java import ContactInfo; // ... @Persistent(defaultFetchGroup = "true") private ContactInfo contactInfo; Relaciones de propiedad de uno a varios Para crear una relacin de uno a varios entre objetos de una clase y varios objetos de otra, usa una coleccin de la clase relacionada: Employee.java import java.util.List; // ... @Persistent private List<ContactInfo> contactInfoSets; Una relacin bidireccional de uno a varios es similar a una relacin de uno a uno, con un campo en la clase principal que usa la anotacin @Persistent(mappedBy = "..."), en la que el valor es el nombre del campo de la clase secundaria: Employee.java import java.util.List; // ... @Persistent(mappedBy = "employee") private List<ContactInfo> contactInfoSets; ContactInfo.java import Employee; // ... @Persistent private Employee employee; Los tipos de coleccin enumerados en la seccin Definicin de clases de datos: colecciones admiten las relaciones de uno a varios. Sin embargo, no se admiten conjuntos para relaciones de uno a varios. App Engine no es compatible con consultas de unin. Es decir, no puedes solicitar una entidad principal usando el atributo de una entidad secundaria. Sin embargo, puedes solicitar la propiedad de una clase insertada, ya que estas clases almacenan propiedades en la entidad principal. Consulta Definicin de clases de datos: clases insertadas. Mantenimiento del orden en colecciones ordenadas Las colecciones ordenadas, como List<...>, mantienen el orden de los objetos cuando se guarda el objeto principal. JDO requiere que las bases de datos conserven este orden almacenando la posicin de cada objeto como una propiedad de este. App Engine almacena esto como una propiedad de la entidad correspondiente, usando un nombre de propiedad igual que el nombre del campo principal seguido de _INTEGER_IDX. Las propiedades de posicin no son eficientes. Si se aade, se elimina o se mueve un elemento en la coleccin, todas las entidades dependientes de la ubicacin modificada en la coleccin deben actualizarse. Puede ser un proceso lento y pueden producirse errores si no se realiza en una transaccin. Si no necesitas conservar un orden arbitrario en una coleccin pero debes usar un tipo de coleccin ordenado, puedes especificar un orden basado en las propiedades de los elementos mediante una anotacin, una extensin a JDO que ofrece DataNucleus: import java.util.List; import javax.jdo.annotations.Extension; import javax.jdo.annotations.Order; import javax.jdo.annotations.Persistent;

// ... @Persistent @Order(extensions = @Extension(vendorName="datanucleus", key="list-ordering", value="state asc, city asc")) private List<ContactInfo> contactInfoSets = new List<ContactInfo>(); La anotacin @Order (usando la extensin list-ordering) especifica el orden de elementos deseado en la coleccin en forma de clusula de orden JDOQL. Para la ordenacin se utilizan los valores de propiedad de los elementos. Como sucede con las consultas, todos los elementos de una coleccin deben tener valores para las propiedades usadas en la clusula de ordenacin. Al acceder a una coleccin se realiza una consulta. Si la clusula de ordenacin de un campo usa ms de un criterio de ordenacin, la consulta requiere un ndice de almacn de datos. Consulta la seccin Consultas e ndices para obtener ms informacin sobre los ndices. Por motivos de eficiencia, utiliza una clusula de ordenacin explcita para relaciones de tipos de coleccin de uno a varios siempre que sea posible. Relaciones sin propiedad Adems de las relaciones de propiedad, el API de JDO tambin ofrece una solucin para la administracin de relaciones sin propiedad. La implementacin de JDO en App Engine an no incluye esta solucin, pero no te preocupes, an puedes administrar estas relaciones con valores Key en lugar de instancias (o colecciones de instancias) de tus objetos modelo. Imagina que el almacenamiento de objetos Key es como modelar una clave externa arbitraria entre dos objetos. El almacn de datos no garantiza la integridad referencial con estas referencias de Key, pero el uso de Key facilita mucho el modelado (y, por tanto, la extraccin) de cualquier relacin entre dos objetos. Sin embargo, si eliges esta opcin, debes tener en cuenta algo ms. En primer lugar, la aplicacin se encarga de garantizar que las claves sean del tipo correcto. JDO y el compilador no comprueban los tipos de Key de forma automtica. En segundo lugar, todos los objetos deben pertenecer al mismo grupo de entidades para realizar una actualizacin atmica de los objetos en ambas partes de la relacin. Consejo: en algunos casos, sera recomendable modelar una relacin de propiedad como si no tuviera propiedad. Esto se debe a que todos los objetos incluidos en una relacin de propiedad se ubican automticamente en el mismo grupo de entidades, y un grupo de entidades solo puede admitir de una a diez operaciones de escritura por segundo. Por lo tanto, si un objeto principal y un objeto secundario reciben 0,75 operaciones de escritura por segundo, lo ms lgico sera modelar esta relacin como relacin sin propiedad para que tanto el objeto principal como el secundario permanecieran en su grupo de entidades independiente. Relaciones sin propiedad de uno a uno Supongamos que el modelo sea la relacin entre personas y comida. En ella, una persona solo puede tener una comida favorita, pero esta no pertenece a la persona, ya que tambin puede ser la comida favorita de otras personas: Person.java // ... imports ... @PersistenceCapable public class Person { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private Key favoriteFood; // ... } Food.java import Person; // ... imports ... @PersistenceCapable public class Food { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)

private Key key; // ... } En este ejemplo, en lugar de asignar a una persona (Person) un miembro del tipo Food en representacin de la comida favorita de esa persona, asignamos a Person un miembro de tipo Key en el que Key es el nico identificador de un objeto de comida (Food). Ten en cuenta que, a menos que las instancias de Person y de Food a las que Person.favoriteFood hace referencia estn en el mismo grupo de entidades, no es posible actualizar la persona y la comida favorita de esa persona en una sola transaccin. Relaciones sin propiedad de uno a varios Ahora queremos que una persona tenga varias comidas favoritas. Como en el caso anterior, la comida favorita no pertenece a la persona porque puede ser la comida favorita de un determinado nmero de personas: Person.java // ... imports ... @PersistenceCapable public class Person { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private Set<Key> favoriteFoods; // ... } En este ejemplo, en lugar de asignar a una persona un miembro del tipo Set<Food> en representacin de las comidas favoritas de esa persona, le asignamos un miembro de tipo Set<Key> en el que el conjunto contiene los identificadores nicos de los objetos de comida Food. Ten en cuenta que, a menos que una instancia de Person y una instancia de Food incluidas en Person.favoriteFoods estn en el mismo grupo de entidades, no ser posible actualizar la persona y la comida favorita en cuestin en una sola transaccin. Relaciones de varios a varios Podemos modelar una relacin de varios a varios manteniendo colecciones de claves en ambas partes de la relacin. Adaptemos nuestro ejemplo para que Food haga un seguimiento de las personas que la consideren como comida favorita: Person.java import java.util.Set; import com.google.appengine.api.datastore.Key; // ... @Persistent private Set<Key> favoriteFoods; Food.java import java.util.Set; import com.google.appengine.api.datastore.Key; // ... @Persistent private Set<Key> foodFans; En este ejemplo, Person mantiene un conjunto de valores Key que identifican exclusivamente a los objetos Food que son favoritos, y Food mantiene un conjunto de valores Key que identifican exclusivamente a los objetos Person que la consideran como comida favorita. Cuando uses valores Key en el modelado de relaciones de varios a varios, ten en cuenta que la aplicacin se encarga de mantener ambas partes de la relacin:

Album.java // ... public void addFavoriteFood(Food food) { favoriteFoods.add(food.getKey()); food.getFoodFans().add(getKey()); } public void removeFavoriteFood(Food food) { favoriteFoods.remove(food.getKey()); food.getFoodFans().remove(getKey()); } Recuerda que, a menos que una instancia de Person y una instancia de Food incluidas en Person.favoriteFoods estn en el mismo grupo de entidades, no es posible actualizar la persona y la comida favorita en cuestin en una sola transaccin. Si no es posible alojar los objetos en el mismo grupo de entidades, la aplicacin debe tener en cuenta que las comidas favoritas de una persona pueden actualizarse sin la actualizacin correspondiente del conjunto de personas a las que les gusta la comida. Tambin puede suceder lo contrario, que se actualice el conjunto de personas y no el de comidas favoritas. Relaciones, grupos de entidades y transacciones Cuando la aplicacin guarda un objeto con relaciones de propiedad en el almacn de datos, todos los dems objetos al alcance mediante relaciones y que deban guardarse (son nuevos o se han modificado desde la ltima vez que se cargaron) se guardan de forma automtica. Esto afecta a las transacciones y a los grupos de entidades de manera importante. Mira el siguiente ejemplo en el que se usa una relacin unidireccional entre las clases Employee y ContactInfo anteriores: Employee e = new Employee(); ContactInfo ci = new ContactInfo(); e.setContactInfo(ci); pm.makePersistent(e); Cuando el nuevo objeto Employee se guarda con el mtodo pm.makePersistent(), el nuevo objeto ContactInfo relacionado se guarda de forma automtica. Dado que ambos objetos son nuevos, App Engine crea dos nuevas entidades en el mismo grupo de entidades usando la entidad Employee como elemento principal de la entidad ContactInfo. Del mismo modo, si el objeto Employee ya se ha guardado y el objeto ContactInfo relacionado es nuevo, App Engine crea la entidad ContactInfo usando la entidad Employee existente como elemento principal. Sin embargo, puedes observar que la invocacin de pm.makePersistent() en el ejemplo no utiliza ninguna transaccin. Sin una transaccin explcita, ambas entidades se crean mediante acciones atmicas distintas. En ese caso, es posible que se cree la entidad Employee, pero tambin que la creacin de la entidad ContactInfo falle. Para garantizar que ambas entidades se creen, o que ninguna lo haga, debes usar una transaccin: Employee e = new Employee(); ContactInfo ci = new ContactInfo(); e.setContactInfo(ci); try { Transaction tx = pm.currentTransaction(); tx.begin(); pm.makePersistent(e); tx.commit(); } finally { if (tx.isActive()) { tx.rollback(); } } Si ambos objetos se guardaron antes de que la relacin se estableciera, App Engine no podr mover la entidad ContactInfo existente al grupo de entidades de la entidad Employee porque solo pueden asignarse estos grupos cuando se crean las entidades. App Engine puede establecer la relacin con una referencia, pero las entidades relacionadas no estarn en el mismo grupo. En ese caso, las dos entidades no pueden actualizarse ni eliminarse en la misma transaccin. Si intentas actualizar o eliminar entidades de distintos grupos en la misma transaccin, se generar una excepcin JDOFatalUserException.

El hecho de guardar un objeto principal cuyos objetos secundarios se hayan modificado hace que tambin se guarden los cambios en los objetos secundarios. Resulta conveniente que los objetos principales mantengan la persistencia de todos los objetos secundarios relacionados de este modo, y utilicen transacciones al guardar los cambios. Eliminacin de elementos secundarios dependientes y en cascada Una relacin de propiedad puede ser dependiente; es decir, que el elemento secundario no exista sin su elemento principal. Si se elimina el objeto principal de una relacin dependiente, tambin se eliminarn todos los objetos secundarios. Si rompes una relacin de propiedad dependiente asignando un valor nuevo al campo dependiente del elemento principal, tambin se elimina el elemento secundario anterior. Puedes declarar una relacin de propiedad de uno a uno como dependiente aadiendo dependent="true" a la anotacin Persistent del campo en el objeto principal que hace referencia al objeto secundario: // ... @Persistent(dependent = "true") private ContactInfo contactInfo; Asimismo, puedes declarar una relacin de propiedad de uno a varios como dependiente aadiendo una anotacin @Element(dependent = "true") al campo en el objeto principal que hace referencia a la coleccin secundaria: import javax.jdo.annotations.Element; // ... @Persistent @Element(dependent = "true") private List contactInfos;

Tal como sucede al crear y actualizar objetos, si quieres que cada operacin de un proceso de eliminacin en cascada se produzca en una sola accin atmica, debes llevar a cabo la eliminacin en una transaccin. Nota: la implementacin de JDO es la que elimina los objetos secundarios dependientes, no el almacn de datos. Si eliminas una entidad principal usando el API de nivel inferior o la Consola del administrador, no se eliminarn los objetos secundarios relacionados. Relaciones polimrficas Aunque la especificacin de JDO permita relaciones polimrficas, estas an no pueden establecerse con la implementacin de JDO en App Engine. Esperamos eliminar esta limitacin en prximas versiones del producto. Si necesitas hacer referencia a varios tipos de objetos a travs de una clase base comn, recomendamos la misma estrategia usada para implementar relaciones sin propiedad: el almacenamiento de una referencia Key. Por ejemplo, si tienes una clase base Recipe con especializaciones Appetizer, Entree y Dessert, y quieres modelar la receta favorita (Recipe) de un chef (Chef), puedes hacerlo como se indica a continuacin: Recipe.java import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.Inheritance; import javax.jdo.annotations.InheritanceStrategy; import javax.jdo.annotations.PersistenceCapable; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; @PersistenceCapable @Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE) public abstract class Recipe { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private int prepTime; } Appetizer.java // ... imports ... @PersistenceCapable public class Appetizer extends Recipe {

// ... appetizer-specific fields } Entree.java // ... imports ... @PersistenceCapable public class Entree extends Recipe { // ... entree-specific fields } Dessert.java // ... imports ... @PersistenceCapable public class Dessert extends Recipe { // ... dessert-specific fields } Chef.java // ... imports ... @PersistenceCapable public class Chef { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent(dependent = "true") private Recipe favoriteRecipe; } Lamentablemente, si creas una instancia de Entree y la asignas a Chef.favoriteRecipe, obtendrs UnsupportedOperationException cuando intentes que el objeto Chef persista. Esto se debe a que el tipo de tiempo de ejecucin del objeto, Entree, no coincide con el tipo del campo de relacin declarado, Recipe. Una solucin provisional sera cambiar el tipo de Chef.favoriteRecipe de Recipe a Key: Chef.java // ... imports ... @PersistenceCapable public class Chef { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent private Key favoriteRecipe; } Dado que Chef.favoriteRecipe ha dejado de ser un campo de relacin, puede hacer referencia a un objeto de cualquier tipo. El inconveniente es que, al igual que sucede con una relacin sin propiedad, debes gestionar esta relacin de forma manual.

Consultas en JDO Este documento se centra en el uso de consultas con el marco de persistencia de objetos de datos Java (JDO). Para obtener informacin ms general sobre las consultas en App Engine, accede a la pgina de consultas. Cada consulta del almacn de datos usa un ndice, una tabla que contiene los resultados de la consulta en el orden deseado. Los ndices de las aplicaciones de App Engine vienen definidos en un archivo de configuracin llamado datastore-indexes.xml. El servidor web

de desarrollo genera sugerencias para este archivo de forma automtica si encuentra consultas que todava no tienen configurado ningn ndice. El mecanismo de consultas basadas en el ndice admite los tipos de consultas ms comunes, pero no algunas de las consultas que puedas haber realizado desde otras bases de datos de distinta tecnologa. Para obtener ms informacin sobre los ndices de App Engine, consulta la seccin Introduccin a los ndices. A continuacin, se describe de forma detallada las restricciones de las consultas. Introduccin a las consultas en JDO Consulta de claves de entidad Eliminacin de entidades mediante consulta Uso de cursores de consultas en JDO Configuracin de la poltica de lectura y el tiempo lmite de la llamada al almacn de datos

Introduccin a las consultas en JDO Mediante una consulta se obtienen entidades del almacn de datos que renan ciertas condiciones. La consulta especifica un tipo de entidad, varias condiciones o ninguna en funcin de los valores de propiedad de entidades (en ocasiones denominados "filtros") y varias descripciones de criterios de ordenacin o ninguna. Cuando la consulta se ejecuta, esta extrae las entidades de un determinado tipo que renen todas las condiciones especificadas, segn el criterio de ordenacin definido. Tambin es posible que una consulta devuelva solamente las claves de las entidades en lugar de las propias entidades. JDO puede realizar consultas para entidades que renan unos criterios determinados. Tambin puedes utilizar Extent en JDO para representar la coleccin de cada entidad de un tipo (cada objeto de una clase guardado). Consultas con JDOQL JDO incluye un lenguaje de consultas para recuperar objetos que renan ciertos criterios. Este lenguaje, denominado JDOQL, hace referencia directamente a las clases de datos y a los campos de JDO. Adems, incluye una funcin de comprobacin de tipos para parmetros y resultados de consulta. JDOQL es similar a SQL, aunque resulta ms apropiada para las bases de datos orientadas a objetos, como el almacn de datos de App Engine (el almacn de datos de App Engine no admite consultas SQL con la interfaz de JDO). El API de consultas admite varios tipos de invocacin. Puedes especificar una consulta completa en una cadena mediante la sintaxis de cadenas JDOQL. Tambin puedes especificar algunas o todas las partes de una consulta invocando mtodos en el objeto de la consulta. A continuacin, presentamos un ejemplo simple de una consulta que usa la invocacin como mtodo, con un filtro y un criterio de ordenacin, mediante la sustitucin de parmetros por el valor usado en el filtro. El mtodo execute() del objeto de la consulta se invoca con los valores que deben sustituirse en la consulta, en el orden que se haya especificado. import java.util.List; import javax.jdo.Query; // ... Query query = pm.newQuery(Employee.class); query.setFilter("lastName == lastNameParam"); query.setOrdering("hireDate desc"); query.declareParameters("String lastNameParam"); try { List<Employee> results = (List<Employee>) query.execute("Smith"); if (!results.isEmpty()) { for (Employee e : results) { // ... } } else { // ... no results ... } } finally { query.closeAll(); } A continuacin, incluimos la misma consulta usando la sintaxis de cadenas:

Query query = pm.newQuery("select from Employee " + "where lastName == lastNameParam " + "parameters String lastNameParam " + "order by hireDate desc"); List<Employee> results = (List<Employee>) query.execute("Smith"); Se pueden combinar estos estilos de definicin de consultas. Por ejemplo: Query query = pm.newQuery(Employee.class, "lastName == lastNameParam order by hireDate desc"); query.declareParameters("String lastNameParam"); List<Employee> results = (List<Employee>) query.execute("Smith"); Puedes reutilizar una nica instancia de Query con los distintos valores que se hayan sustituido por los parmetros invocando el mtodo execute() varias veces. Cada invocacin realiza la consulta y devuelve los resultados en forma de coleccin. La sintaxis de cadenas JDOQL admite valores literales en las cadenas de valores numricos y de cadena. Incluye las cadenas entre comillas simples (') o entre comillas dobles ("). El resto de tipos de valor debe recurrir a la sustitucin de parmetros. A continuacin, incluimos un ejemplo en el que se usa un valor literal de cadena: Query query = pm.newQuery(Employee.class, "lastName == 'Smith' order by hireDate desc"); Filtros de consultas Un filtro especifica un nombre de campo, un operador y un valor. El valor lo debe facilitar la aplicacin; no puede hacer referencia a otro campo ni calcularse con trminos de otros campos. El operador de filtro puede ser cualquiera de los siguientes: < menor que <= menor que o igual a = igual a > mayor que >= mayor que o igual a != no igual a

Tambin se pueden usar los filtros contains() (normalmente conocidos como filtros IN en SQL) con la siguiente sintaxis: // Give me all employees with lastName equal to Smith or Jones Query query = pm.newQuery(Employee.class, ":p.contains(lastName)"); query.execute(Arrays.asList("Smith", "Jones")); En realidad, el operador != realiza dos consultas: una donde todos los dems filtros son el mismo y el filtro "no igual a" se sustituye por un filtro "menor que", y otra donde el filtro "no igual a" se sustituye por un filtro "mayor que". Los resultados se fusionan en orden. Como se describe a continuacin en la seccin sobre los filtros de desigualdad, una consulta solo puede disponer de un filtro "no igual a" y no puede presentar ningn otro filtro de desigualdad. El operador contains() tambin realiza varias consultas, una por cada elemento del valor de la lista proporcionada, donde los dems filtros son iguales y el filtro contains() se sustituye por un filtro "igual a". Los resultados se fusionan en el orden de los elementos de la lista. Si una consulta dispone de ms de un filtro contains(), esta se realiza como si se tratara de varias consultas, una por cada combinacin de valores de los filtros contains(). Una sola consulta que contenga los operadores != o contains() tiene un lmite de 30 subconsultas. Para obtener ms informacin, consulta Queries with != and IN filters (Consultas con filtros "!=" e "IN"). Debido al modo en que el almacn de datos de App Engine ejecuta las consultas, una sola consulta no puede usar filtros de desigualdad (< <= >= > !=) en ms de una propiedad. Se permiten varios filtros de desigualdad en la misma propiedad (por ejemplo, al realizar consultas para un intervalo de valores determinado).

query.setFilter("lastName == 'Smith' && hireDate > hireDateMinimum"); query.declareParameters("Date hireDateMinimum"); Una entidad debe coincidir con todos los filtros para ser un resultado. En la sintaxis de cadena JDOQL, puedes separar varios filtros can con || ("or" lgico) y && ("and" lgico). Sin embargo, recuerda que || solo puede usarse cuando separa filtros con el mismo nombre de campo. En otras palabras, || solo es legal en situaciones en las que los filtros que separa pueden combinarse en un nico filtro contains(): // legal, all filters separated by || are on the same field Query query = pm.newQuery(Employee.class, "(lastName == 'Smith' || lastName == 'Jones')" + " && firstName == 'Harold'"); // not legal, filters separated by || are on different fields Query query = pm.newQuery(Employee.class, "lastName == 'Smith' || firstName == 'Harold'"); No se admite la negacin ("not" lgico). Criterios de ordenacin de consultas Un criterio de ordenacin especifica una propiedad y una direccin, ya sea ascendente o descendente. Los resultados se devuelven ordenados segn un criterio determinado, en el orden especificado. Si no se especifica ningn criterio para la consulta, la ordenacin de resultados queda sin definir y estos se devuelven segn vayan obtenindose del almacn de datos. Debido al modo en que el almacn de datos de App Engine ejecuta las consultas, si una consulta especifica filtros de desigualdad en una propiedad y criterios de ordenacin en otras propiedades, la propiedad con filtros de desigualdad debe ordenarse antes que el resto de propiedades. query.setOrdering("hireDate desc, firstName asc"); Intervalos de consultas Una solicitud puede especificar que se devuelva un intervalo de resultados en la aplicacin. El intervalo especifica qu resultados deben devolverse primero, y cules en ltimo lugar, mediante ndices numricos en los que un cero corresponde al primer resultado. Por ejemplo, un intervalo de 5, 10 devuelve los resultados sexto, sptimo, octavo, noveno y dcimo. El resultado inicial influye en el rendimiento, ya que el almacn de datos debe recuperar y, a continuacin, descartar todos los resultados anteriores a dicho resultado. Por ejemplo, una consulta con un intervalo de 5, 10 devuelve diez resultados del almacn de datos y, a continuacin, descarta los primeros cinco y devuelve los cinco restantes en la aplicacin. query.setRange(5, 10); En cambio, si quieres usar la paginacin en el almacn de datos de App Engine, deberas usar las tcnicas indicadas en este artculo. Los ejemplos de este artculo estn en lenguaje Python, pero tambin son vlidos para Java. Elementos Extent En JDO, Extent representa cada objeto de una clase concreta en el almacn de datos. Puedes iniciar Extent mediante el mtodo getExtent() de PersistenceManager y, a continuacin, utilizarlo con la clase de datos. La clase Extent implementa la interfaz iterable para acceder a los resultados. Cuando hayas accedido a los resultados, invoca el mtodo closeAll(). En el siguiente ejemplo se procesa una iteracin sobre cada objeto Employee del almacn de datos: import java.util.Iterator; import javax.jdo.Extent; // ... Extent extent = pm.getExtent(Employee.class, false); for (Employee e : extent) { // ... } extent.closeAll();

Con Extent se recuperan resultados en lote, segn sea necesario. Consulta de claves de entidad Las claves de entidad pueden estar sujetas a un filtro o al criterio de ordenacin de una consulta. En JDO, se hace referencia a la clave de la entidad en la consulta mediante el campo de clave principal del objeto. El almacn de datos considera el valor de clave completo para esas consultas, incluida la ruta de la entidad principal de la entidad, el tipo y la cadena de nombre de clave asignada por la aplicacin o el ID numrico asignado por el sistema. Una consulta puede devolver claves de entidades en lugar de entidades completas. Para ello, solo tienes que seleccionar el campo @PrimaryKey en tu consulta: Query q = pm.newQuery("select id from " + Person.class.getName()); List ids = (List) q.execute();

Nota: las consultas que devuelven claves son ms rpidas y consumen menos CPU que las consultas que devuelven entidades ya que las claves en s ya se encuentran en el ndice. Por lo tanto, la consulta no necesita extraer las entidades. Si solo necesitas las claves de los resultados de tu consulta, te recomendamos que realices una consulta de solo claves. Dado que una clave de entidad es nica entre todas las entidades del sistema, las consultas de claves facilitan la recuperacin en lote de entidades de un tipo determinado, como en el caso de un volcado en lote del contenido del almacn de datos. Al contrario que los intervalos JDOQL, esto funciona de forma eficaz independientemente del nmero de entidades. Las claves se ordenan primero por ruta principal y luego por tipo y por nombre o ID. Los tipos y los nombres de clave son cadenas y se ordenan por valor de byte. Los ID son nmeros enteros y se ordenan numricamente. Si las entidades que pertenecen a la misma entidad principal y al mismo tipo emplean una combinacin de cadenas de nombre de clave y de ID numricos, las entidades con ID numricos se consideran inferiores a las entidades con cadenas de nombre de clave. Los elementos de la ruta principal se ordenan de forma parecida: primero por tipo (cadena) y luego por nombre de clave (cadena) o por ID (nmero). Las consultas de claves utilizan los ndices igual que las consultas de propiedades. Las consultas de claves necesitan disponer de ndices personalizados en los mismos casos que las consultas de propiedades, con un par de salvedades: no se necesita un ndice personalizado para filtros de desigualdad ni para un criterio de ordenacin ascendente en Entity.KEY_RESERVED_PROPERTY, pero s se requiere un ndice de este tipo para un criterio de ordenacin descendente en Entity.KEY_RESERVED_PROPERTY. Al igual que sucede con todas las consultas, el servidor de desarrollo web crea entradas de configuracin adecuadas en este archivo cuando se prueba una consulta que requiere un ndice personalizado. Eliminacin de entidades mediante consulta Si realizas una consulta con el fin de eliminar todas las entidades que coincidan con el filtro de la consulta, puedes escribir mucho menos cdigo si utilizas la funcin de eliminacin mediante consulta de JDO. El cdigo siguiente elimina a todas las personas que sean de una determinada altura: Query query = pm.newQuery(Person.class); query.setFilter("height > maxHeightParam"); query.declareParameters("int maxHeightParam"); query.deletePersistentAll(maxHeight); Puedes comprobar que la nica diferencia es que, en lugar de invocar query.execute(...), invocamos query.deletePersistentAll(...). Todas las reglas y restricciones relacionadas con filtros, criterios de ordenacin e ndices descritas anteriormente se aplican a las consultas, tanto si seleccionas como si eliminas el conjunto de resultados. Sin embargo, ten en cuenta que, del mismo modo que has eliminado las entidades de persona ("Person") con pm.deletePersistent(...), tambin se eliminan todos los elementos secundarios dependientes de las entidades eliminadas con la consulta. Para obtener ms informacin sobre elementos secundarios dependientes, consulta Eliminacin de elementos secundarios dependientes y en cascada. Uso de cursores de consultas en JDO En JDO puedes usar una extensin y la clase JDOCursorHelper para utilizar cursores con las consultas de JDO. Los cursores se utilizan para extraer resultados en forma de lista o mediante un iterador. Para obtener un cursor (Cursor), debes dirigir el resultado List o Iterator al mtodo esttico JDOCursorHelper.getCursor(): import java.util.HashMap; import java.util.List; import java.util.Map; import javax.jdo.Query;

import com.google.appengine.api.datastore.Cursor; import org.datanucleus.store.appengine.query.JDOCursorHelper; // ... Query query = pm.newQuery(Employee.class); query.setRange(0, 20); // ... List<Employee> results = (List<Employee>) query.execute(); // Use the first 20 results... Cursor cursor = JDOCursorHelper.getCursor(results); String cursorString = cursor.toWebSafeString(); // Store the cursorString... // ... // Query query = the same query that produced the cursor // String cursorString = the string from storage Cursor cursor = Cursor.fromWebSafeString(cursorString); Map<String, Object> extensionMap = new HashMap<String, Object>(); extensionMap.put(JDOCursorHelper.CURSOR_EXTENSION, cursor); query.setExtensions(extensionMap); query.setRange(0, 20); List<Employee> results = (List<Employee>) query.execute(); // Use the next 20 results... Para obtener ms informacin, consulta la seccin Cursores de consultas. Configuracin de la poltica de lectura y el tiempo lmite de la llamada al almacn de datos Mediante la configuracin puedes establecer la poltica de lectura (consistencia fuerte o consistencia eventual) y el tiempo lmite de todas las invocaciones del almacn de datos realizadas por una instancia de PersistenceManager. Tambin puedes anular estas opciones para un objeto de consulta especfico. Para anular la poltica de lectura de una nica consulta (Query), invoca su mtodo addExtension() tal como se indica a continuacin: Query q = pm.newQuery(Employee.class); q.addExtension("datanucleus.appengine.datastoreReadConsistency", "EVENTUAL"); Los valores admitidos son "EVENTUAL" (para lecturas con consistencia eventual) y "STRONG" (para lecturas con consistencia fuerte). El valor predeterminado es "STRONG", salvo que se haya especificado otro valor en la configuracin del archivo jdoconfig.xml. Para anular el tiempo lmite de las invocaciones del almacn de datos para una sola consulta (Query), invoca su mtodo setTimeoutMillis(): q.setTimeoutMillis(3000); El valor es una cantidad de tiempo expresada en milisegundos. No hay forma de anular la configuracin de estas opciones al extraer entidades por clave. Si seleccionas la consistencia eventual en una consulta del almacn de datos, los ndices que emplea esta consulta para obtener resultados tambin permiten el acceso con consistencia eventual. A veces, las consultas devuelven entidades que no coinciden con los criterios de la consulta, aunque tambin sucede con una poltica de lectura de consistencia fuerte. Puedes usar las transacciones para garantizar un conjunto de resultados coherente si la consulta usa un filtro de ancestro. Consulta la seccin Aislamiento de transacciones en App Engine para obtener ms informacin sobre cmo se actualizan las entidades y los ndices.

Uso de JPA con App Engine La interfaz del API de persistencia Java (JPA) es una interfaz estndar para almacenar objetos con datos en una base de datos relacional. El estndar permite que las interfaces realicen tareas de anotacin de objetos Java, de recuperacin de objetos con consultas y de interaccin con una base de datos mediante transacciones. Una aplicacin que emplee la interfaz de JPA puede funcionar con distintas bases de datos sin necesidad de usar el cdigo de base de datos del proveedor. JPA simplifica la transferencia de tu aplicacin entre los diferentes proveedores de bases de datos. El SDK de Java de App Engine incluye JPA 1.0 para el almacn de datos de App Engine. La implementacin se basa en DataNucleus Access Platform. JPA presenta una interfaz estndar para la interaccin con bases de datos relacionales, aunque el almacn de datos de App Engine no sea una base de datos relacional. Por lo tanto, hay funciones de JPA que la implementacin de App Engine, sencillamente, no admite. Hemos hecho todo lo posible por hacer referencia a dichas funciones donde corresponde. Consulta la documentacin de Access Platform 1.1 para obtener ms informacin sobre JPA. En concreto, consulta las secciones "JPA Mapping" (Asignacin de JPA) y "JPA API" (API de JPA). Configuracin de JPA Mejora de las clases de datos Obtencin de una instancia de EntityManager Anotaciones de clases y de campos Herencias Funciones no disponibles en JPA

Configuracin de JPA Si quieres usar JPA para acceder al almacn de datos, necesitas lo siguiente para la aplicacin de App Engine: Los archivos JAR de JPA y del almacn de datos deben encontrarse en el directorio war/WEB-INF/lib/ de la aplicacin. Debe haber un archivo de configuracin llamado persistence.xml en el directorio war/WEB-INF/classes/METAINF/ de la aplicacin cuya configuracin solicita a JPA el uso del almacn de datos de App Engine. El proceso de creacin del proyecto requiere una mejora posterior a la compilacin en las clases de datos compilados para asociarlas a la implementacin JPA.

Si utilizas Google Plugin for Eclipse, el plug-in realiza el primer y el tercer paso por ti. El asistente de nuevos proyectos coloca los archivos JAR de JPA y del almacn de datos en la ubicacin correcta y, a continuacin, realiza la mejora de forma automtica durante el proceso de creacin. Aun as, debes crear el archivo persistence.xml manualmente y guardarlo en war/WEBINF/classes/META-INF/. El plug-in se actualizar en breve para realizar tambin este paso de forma automtica. Si utilizas Apache Ant para crear tu proyecto, puedes utilizar una tarea de Ant incluida con el SDK para realizar la mejora. Debes copiar los archivos JAR y crear el archivo de configuracin cuando configures el proyecto. Consulta la seccin Uso de Apache Ant para obtener ms informacin sobre dicha tarea. Copia de los archivos JAR Los archivos JAR de JPA y del almacn de datos se incluyen en el SDK de Java de App Engine; los encontrars en el directorio appengine-java-sdk/lib/user/orm/. Copia los archivos JAR en el directorio war/WEB-INF/lib/ de tu aplicacin. Asegrate de que appengine-api.jar tambin se encuentre en el directorio war/WEB-INF/lib/ (es posible que ya lo hayas copiado al crear el proyecto). El plug-in DataNucleus de App Engine utiliza este archivo JAR para acceder al almacn de datos. Creacin del archivo persistence.xml La interfaz de JPA requiere un archivo de configuracin llamado persistence.xml en el directorio war/WEBINF/classes/META-INF/ de la aplicacin. Puedes crear directamente este archivo en dicha ubicacin o hacer que se copie desde un directorio determinado durante el proceso de creacin. Debes crear el archivo con el siguiente contenido:

<?xml version="1.0" encoding="UTF-8" ?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="transactions-optional"> <provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider> <properties> <property name="datanucleus.NontransactionalRead" value="true"/> <property name="datanucleus.NontransactionalWrite" value="true"/> <property name="datanucleus.ConnectionURL" value="appengine"/> </properties> </persistence-unit> </persistence> Configuracin de la poltica de lectura y el tiempo lmite de la llamada al almacn de datos Tal como se describe en la documentacin sobre consultas, puedes establecer la poltica de lectura (consistencia fuerte o consistencia eventual) y el tiempo lmite para la invocacin del almacn de datos para EntityManagerFactory en el archivo persistence.xml. Esta configuracin se incluye en el elemento <persistence-unit>. Todas las invocaciones realizadas con una instancia de EntityManager determinada usan la configuracin especificada cuando EntityManagerFactory cre el gestor. Tambin puedes anular estas opciones para una consulta (Query) determinada, tal como se describe ms abajo. Para establecer una poltica de lectura, incluye una propiedad denominada datanucleus.appengine.datastoreReadConsistency. Las opciones son EVENTUAL (para lecturas con consistencia eventual) y STRONG (para lecturas con consistencia fuerte). Si no se especifica, el valor predeterminado es STRONG. <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" /> Puedes definir distintos tiempos lmite en las invocaciones del almacn de datos tanto para procesos de lectura como de escritura. Para procesos de lectura, usa la propiedad estndar de JPA javax.persistence.query.timeout; y para procesos de escritura, usa datanucleus.datastoreWriteTimeout. El valor es una cantidad de tiempo expresada en milisegundos. <property name="javax.persistence.query.timeout" value="5000" /> <property name="datanucleus.datastoreWriteTimeout" value="10000" /> Puedes tener varios elementos <persistence-unit> en el mismo archivo persistence.xml, con distintos atributos name, para usar instancias de EntityManager con diferentes configuraciones en la misma aplicacin. Por ejemplo, el siguiente archivo persistence.xml establece dos configuraciones; una se denomina "transactions-optional", y la otra "eventualreads-short-deadlines": <?xml version="1.0" encoding="UTF-8" ?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="transactions-optional"> <provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider> <properties> <property name="datanucleus.NontransactionalRead" value="true"/> <property name="datanucleus.NontransactionalWrite" value="true"/> <property name="datanucleus.ConnectionURL" value="appengine"/> </properties> </persistence-unit> <persistence-unit name="eventual-reads-short-deadlines"> <provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider> <properties> <property name="datanucleus.NontransactionalRead" value="true"/> <property name="datanucleus.NontransactionalWrite" value="true"/>

<property name="datanucleus.ConnectionURL" value="appengine"/> <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" /> <property name="javax.persistence.query.timeout" value="5000" /> <property name="datanucleus.datastoreWriteTimeout" value="10000" /> </properties> </persistence-unit> </persistence> Para obtener ms informacin, consulta la documentacin sobre consultas. Consulta la seccin Obtencin de una instancia de EntityManager a continuacin para obtener informacin de cmo crear EntityManager seleccionando la configuracin deseada. Puedes anular la poltica de lectura y el tiempo lmite de las invocaciones para objetos Query determinados. Para anular la poltica de lectura de una consulta (Query), invoca su mtodo setHint() tal como se indica a continuacin: Query q = em.createQuery("select from " + Book.class.getName()); q.setHint("datanucleus.appengine.datastoreReadConsistency", "EVENTUAL"); Como antes, los valores que admite son "EVENTUAL" y "STRONG". Para anular el tiempo de espera de lectura, invoca setHint() como se indica a continuacin: q.setHint("javax.persistence.query.timeout", 3000); No hay forma de anular la configuracin de estas opciones al extraer entidades por clave. Mejora de las clases de datos La implementacin de DataNucleus en JPA realiza una mejora despus de la compilacin, durante el proceso de creacin, para asociar las clases de datos a la implementacin de JPA. Si usas Apache Ant, el SDK incluye una tarea de Ant para realizar este paso. Consulta la seccin Uso de Apache Ant para obtener ms informacin sobre dicha tarea. Puedes realizar la mejora en clases compiladas desde la lnea de comandos utilizando el siguiente comando: java -cp classpath org.datanucleus.enhancer.DataNucleusEnhancer class-files classpath debe contener los archivos JAR datanucleus-core-*.jar, datanucleus-jpa-*, datanucleus-enhancer*.jar, asm-*.jar y geronimo-jpa-*.jar (* es el nmero de versin correspondiente a cada JAR) del directorio appenginejava-sdk/lib/tools/, as como todas tus clases de datos. Para obtener ms informacin sobre el programa de mejora en bytecode DataNucleus, consultala documentacin de DataNucleus. Obtencin de una instancia de EntityManager Una aplicacin interacta con JPA utilizando una instancia de la clase EntityManager. Esta instancia se obtiene al reproducir e invocar un mtodo en una instancia de la clase EntityManagerFactory. La fbrica utiliza la configuracin JPA (identificada como "transactions-optional") para crear instancias de EntityManager. Dado que una instancia de EntityManagerFactory tarda en iniciarse, lo ideal sera reutilizar la misma instancia tantas veces como sea posible. Una forma sencilla de hacerlo es crear una clase envoltorio nica con una instancia esttica. Por ejemplo: EMF.java import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; public final class EMF { private static final EntityManagerFactory emfInstance = Persistence.createEntityManagerFactory("transactions-optional");

private EMF() {} public static EntityManagerFactory get() { return emfInstance; } } Consejo: "transactions-optional" hace referencia al nombre de la configuracin en el archivo persistence.xml. Si tu aplicacin usa varias configuraciones, debes ampliar este cdigo para invocar Persistence.createEntityManagerFactory() como quieras. El cdigo debera incluir en la memoria cach una instancia nica de cada EntityManagerFactory. La aplicacin utiliza la instancia de la fbrica para crear una instancia de EntityManager por cada solicitud que acceda al almacn de datos. import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import EMF; // ... EntityManager em = EMF.get().createEntityManager(); EntityManager se usa para almacenar, actualizar y eliminar objetos de datos, as como para realizar consultas al almacn de datos. Cuando acabes con la instancia de EntityManager, debes invocar su mtodo close(). Sera un error usar la instancia de EntityManager despus de invocar su mtodo close(). try { // ... do stuff with em ... } finally { em.close(); } Anotaciones de clase y de campo Cada objeto que guarda JPA se convierte en una entidad del almacn de datos de App Engine. El tipo de la entidad se obtiene a partir del nombre simple de la clase (sin el nombre del paquete). Cada campo persistente de la clase representa una propiedad de la entidad, con un nombre de propiedad idntico al nombre del campo (con distincin de maysculas y minsculas). Para declarar una clase Java como almacenable y recuperable en el almacn de datos con JPA, asigna una anotacin @Entity a la clase. Por ejemplo: import javax.persistence.Entity; @Entity public class Employee { // ... } Los campos de la clase de datos que se vayan a guardar en el almacn de datos deben ser de un tipo que persista de forma predeterminada o que se declare explcitamente como persistente. El comportamiento de persistencia predeterminado de JPA queda reflejado de forma detallada en esta tabla del sitio web de DataNucleus. Para declarar un campo como persistente, asgnale una anotacin @Basic: import java.util.Date; import javax.persistence.Enumerated; import com.google.appengine.api.datastore.ShortBlob; // ... @Basic private ShortBlob data; A continuacin, se indican los tipos de campo existentes. uno de los tipos principales admitidos por el almacn de datos,

una coleccin (como java.util.List<...>) de valores de un tipo principal del almacn de datos, una instancia o una coleccin de instancias de una clase @Entity, una clase insertada, almacenada como propiedades en la entidad.

Una clase de datos debe tener un constructor predeterminado, pblico o protegido, y un campo dedicado al almacenamiento de la clave principal de la entidad del almacn de datos correspondiente. Puedes elegir entre cuatro tipos de campo de clave distintos, cada uno de los cuales cuenta con un tipo de valor y anotaciones diferentes. Consulta la seccin Creacin de datos: claves para obtener ms informacin. El campo de clave ms simple es el valor de nmero entero largo, que JPA rellena automticamente con un valor nico respecto al resto de las instancias de la clase cuando el objeto se guarda en el almacn de datos por primera vez. Las claves de nmeros enteros largos usan anotaciones @Id y @GeneratedValue(strategy = GenerationType.IDENTITY): import com.google.appengine.api.datastore.Key; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; // ... @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Key key; A continuacin, te ofrecemos un ejemplo de clase de datos: import com.google.appengine.api.datastore.Key; import import import import import java.util.Date; javax.persistence.Entity; javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Id;

@Entity public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Key key; private String firstName; private String lastName; private Date hireDate; // Accessors for the fields. JPA doesn't use these, but your application does. public Key getKey() { return key; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getHireDate() { return hireDate; }

public void setHireDate(Date hireDate) { this.hireDate = hireDate; } } Herencia JPA admite la creacin de clases de datos que utilicen herencia. Antes de seguir leyendo cmo funciona el sistema de herencia de JPA en App Engine, te recomendamos que leas la documentacin de DataNucleus al respecto. Ya lo has hecho? Muy bien. La herencia de JPA en App Engine funciona tal como se describe en la documentacin de DataNucleus, pero con algunas restricciones adicionales. Te hablaremos de estas restricciones y, a continuacin, te ofreceremos ejemplos concretos. El mtodo de herencia "JOINED" te permite dividir los datos de un nico objeto de datos en varias tablas. Sin embargo, el almacn de datos de App Engine no es compatible con la unin de datos, por lo que esta operacin requiere una invocacin a procedimiento remoto para cada nivel de herencia. Esto resulta muy poco eficiente, ya que el mtodo de herencia "JOINED" no es compatible con las clases de datos. En segundo lugar, el mtodo de herencia "SINGLE_TABLE" te permite almacenar los datos de un objeto de datos en una sola tabla asociada a la clase persistente en el nivel superior de la jerarqua de herencia. Aunque se trate de una tcnica eficaz en s misma, no es compatible con App Engine. No obstante, es posible que sea compatible en prximas versiones. Pero la buena noticia es que los mtodos "TABLE_PER_CLASS" y "MAPPED_SUPERCLASS" funcionan como se indica en la documentacin de DataNucleus. Te ofrecemos un ejemplo a continuacin: Worker.java import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; @Entity @MappedSuperclass public abstract class Worker { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Key key; private String department; } Employee.java // ... imports ... @Entity public class Employee extends Worker { private int salary; } Intern.java import java.util.Date; // ... imports ... @Entity public class Intern extends Worker { private Date internshipEndDate; } En este ejemplo, hemos aadido una anotacin @MappedSuperclass a la declaracin de la clase Worker. Esto hace que JPA guarde todos los campos persistentes de Worker en las entidades del almacn de datos de sus subclases. La entidad del almacn de datos creada como resultado de la invocacin de persist() con una instancia de Employee tiene dos propiedades denominadas "department" y "salary". La entidad del almacn de datos creada como resultado de la invocacin de persist() con una instancia de Intern tiene dos propiedades denominadas "department" e "inernshipEndDate". No habr ninguna entidad del tipo "Worker" en el almacn de datos.

Ahora, veamos algo un poco ms interesante. Supn que, adems de tener Employee e Intern, tambin queremos que una especializacin de Employee describa a los empleados que se han marchado de la empresa: FormerEmployee.java import java.util.Date; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; // ... imports ... @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class FormerEmployee extends Employee { private Date lastDay; } En este ejemplo, hemos aadido una anotacin @Inheritance a la declaracin de la clase FormerEmployee con su atributo strategy definido como InheritanceType.TABLE_PER_CLASS. Esto hace que JPA guarde todos los campos persistentes de FormerEmployee y sus superclases en las entidades del almacn de datos que correspondan a las instancias de FormerEmployee. La entidad del almacn de datos creada como resultado de la invocacin de persist() con una instancia de FormerEmployee tiene tres propiedades denominadas "department", "salary" y "lastDay". Nunca habr una entidad del tipo "Employee" correspondiente a FormerEmployee, pero si invocas persist() con un objeto cuyo tipo de tiempo de ejecucin sea Employee, crears una entidad del tipo Employee. La combinacin de relaciones y de herencia es eficaz siempre que los tipos declarados de los campos de tu relacin coincidan con los tipos de tiempo de ejecucin de los objetos que asignas a dichos campos. Consulta la seccin Relaciones polimrficas para obtener ms informacin. Esta seccin contiene ejemplos de JDO, pero los conceptos y las restricciones son las mismas para JPA. Funciones no disponibles en JPA Las siguientes funciones de la interfaz de JPA no se encuentran disponibles en la implementacin de App Engine: Las relaciones de propiedad multidireccionales y las relaciones sin propiedad. Puedes implementar relaciones sin propiedad mediante valores Key explcitos, aunque la comprobacin del tipo no se realiza en el API. Consultas "Join". No puedes usar el campo de una entidad secundaria en un filtro al realizar una consulta del tipo principal. Ten en cuenta que puedes probar el campo de relacin del elemento principal directamente en las consultas mediante una clave. Consultas de agrupacin conjunta (group by, having, sum, avg, max, min). Consultas polimrficas. No puedes realizar consultas de una clase para obtener instancias de una subclase. Cada clase se representa mediante un tipo de entidad independiente en el almacn de datos.