Google Code disponible en: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
Puedes utilizar JDO para almacenar objetos de datos Java simples (a veces denominados "objetos Java antiguos y simples" o "POJO") en el almacén de datos. Cada objeto que se vuelve persistente con PersistenceManager se convierte en una entidad en el almacén de datos. El usuario utiliza anotaciones para indicar a JDO cómo almacenar y volver a crear instancias de las clases de datos.
Nota: las primeras versiones de JDO utilizan archivos XML .jdo en lugar de anotaciones Java. Estas versiones siguen siendo compatibles con JDO 2.3. Esta documentación sólo trata el uso de las anotaciones Java en clases de datos.
Cada objeto guardado por JDO se convierte en una entidad en el almacén de datos de App Engine. Este tipo de entidad deriva del nombre sencillo de la clase (las clases internas utilizan la ruta $ sin el nombre de paquete). Cada campo persistente de la clase representa una propiedad de la entidad, en la que el nombre de la propiedad es igual al nombre del campo (respetando mayúsculas y minúsculas).
Para declarar que una clase Java puede ser almacenada y recuperada del almacén de datos a través de JDO, asigna a la clase una anotación @PersistenceCapable. Por ejemplo:
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
// ...
}
Los campos de la clase de datos que están almacenados en el almacén de datos deben declararse como campos persistentes. Para declarar un campo como persistente, asígnale una anotación @Persistent:
import java.util.Date;
import javax.jdo.annotations.Persistent;
// ...
@Persistent
private Date hireDate;
Para declarar un campo como no persistente (no se almacena en el almacén de datos y no se restablece al recuperar el objeto), asígnale una anotación @NotPersistent.
Sugerencia: JDO especifica que determinados tipos de campos son persistentes de forma predeterminada si no se especifican las anotaciones @Persistent o @NotPersistent y si el resto de tipos de campos no son persistentes de forma predeterminada. Para obtener una descripción completa de este comportamiento, consulta la documentación de DataNucleus. Dado que no todos los tipos de valores principales del almacén de datos de App Engine son persistentes de forma predeterminada de acuerdo con la especificación JDO, recomendamos de forma explícita la anotación de los campos como @Persistent o @NotPersistent para que quede más claro.
El tipo de campo puede ser cualquiera de los que se describen a continuación:
java.util.List<...>) o un conjunto de valores de tipo principal del almacén de datos,@PersistenceCapable,Una clase de datos debe contener un campo dedicado a almacenar la clave principal de la entidad del almacén de datos correspondiente. Puedes elegir entre 4 tipos diferentes de campos de clave, cada uno de los cuales utiliza un tipo de valor y unas anotaciones diferentes. (Para obtener más información, consulta Creación de datos: claves). El campo de clave más sencillo consiste en un valor entero largo que JDO rellena de forma automática con un valor exclusivo para todas las demás instancias de la clase al guardar el objeto en el almacén de datos por primera vez. Las claves enteras largas utilizan una anotación @PrimaryKey y una anotación @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY):
Sugerencia: convierte todos los campos persistentes en private o protected (o paquete protegido) y autoriza el acceso únicamente a través de métodos de acceso. El acceso directo a un campo persistente desde otra clase puede omitir la mejora de la clase JDO. También puedes crear otras clases @PersistenceAware. Para obtener más información, consulta la documentación de DataNucleus.
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PrimaryKey;
// ...
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
A continuación se muestra un ejemplo de una clase de datos:
import java.util.Date;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
@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. JDO doesn't use these, but your application does.
public Long getId() {
return id;
}
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;
}
}
El almacén de datos es compatible con los siguientes tipos de valores principales:
| Tipo | Clase Java | Orden | Notas |
|---|---|---|---|
| cadena de texto corta, < 500 bytes | java.lang.String |
Unicode | Un valor superior a 500 bytes genera una excepción JDOFatalUserException. |
| cadena de bytes corta, < 500 bytes | com.google.appengine.api.datastore.ShortBlob |
orden de bytes | Un valor superior a 500 bytes genera una excepción JDOFatalUserException. |
| Valor booleano | boolean o java.lang.Boolean |
false < true |
|
| número entero | short, java.lang.Short, int, java.lang.Integer, long, java.lang.Long |
Numérico | Almacenado como un número entero largo y, a continuación, convertido en el tipo de campo. Desbordamiento de valores fuera del intervalo. |
| número de punto flotante | float, java.lang.Float, double, java.lang.Double |
Numérico | Almacenado como flotante de doble ancho y, a continuación, convertido en el tipo de campo. Desbordamiento de valores fuera del intervalo. |
| fecha-hora | java.util.Date |
Cronológico | |
| cuenta de Google | com.google.appengine.api.users.User |
Por dirección de correo electrónico (Unicode) | |
| cadena de texto larga | com.google.appengine.api.datastore.Text |
(no ordenable) | no indexada |
| cadena de bytes larga | com.google.appengine.api.datastore.Blob |
(no ordenable) | no indexada |
| clave de entidades | com.google.appengine.api.datastore.Key o el objeto de referencia (como secundario) |
Por elementos de ruta (tipo, ID o nombre, tipo, ID o nombre...) | |
| una URL | com.google.appengine.api.datastore.Link |
Unicode |
Nota: el almacén de datos admite varios tipos de valores principales adicionales que aún no se han implementado en el API del almacén de datos Java, pero que sí se han implementado en el API Python. Si una propiedad contiene un valor con alguno de estos tipos y la entidad se carga en un objeto con un campo para la propiedad, JDO se comportará como si la propiedad no existiera: establecerá el campo en null o generará una excepción NullPointerException si el tipo de campo no admite valores nulos. Los tipos no admitidos son: categoría, correo electrónico, MI, número de teléfono, dirección postal, puntuación y GeoPt.
Para representar una propiedad que contenga un valor único de tipo principal, declara un campo del tipo Java y utiliza la anotación @Persistent:
import java.util.Date;
import javax.jdo.annotations.Persistent;
// ...
@Persistent
private Date hireDate;
Un valor de campo puede contener una instancia de una clase serializable que almacena el valor serializable de la instancia en un valor de propiedad único del tipo Blob. Para indicar a JDO que serialice el valor, el campo utiliza la anotación @Persistent(serialized=true). Los valores Blob no se indexan y no se pueden utilizar con fines de ordenación o filtrado de consultas.
A continuación se muestra un ejemplo de una clase serializable sencilla que representa un archivo que incluye el contenido del archivo, un nombre de archivo y un tipo MIME. Esta no es una clase de datos JDO; por lo tanto, no aparecen 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 un valor Blob en una propiedad, declara un campo cuyo tipo sea la clase y utiliza la anotación @Persistent(serialized = "true"):
import javax.jdo.annotations.Persistent;
import DownloadableFile;
// ...
@Persistent(serialized = "true")
private DownloadableFile file;
Un valor de campo, que es una instancia de una clase @PersistenceCapable, crea una relación de propiedad uno a uno entre dos objetos. Un campo, que es una colección de esas referencias, crea una relación de propiedad uno a varios.
Importante: las relaciones de propiedad podrían afectar a las transacciones, grupos de entidades y eliminaciones en cascada. Para obtener más información, consulta Transacciones y Relaciones.
A continuación se muestra un ejemplo sencillo de una relación de propiedad uno a uno entre un objeto Employee y un objeto ContactInfo:
ContactInfo.java
import com.google.appengine.api.datastore.Key;
// ... imports ...
@PersistenceCapable(identityType = IdentityType.APPLICATION)
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(identityType = IdentityType.APPLICATION)
public class Employee {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
@Persistent
private ContactInfo myContactInfo;
// ... accessors ...
}
En este ejemplo, si la aplicación crea una instancia Employee, rellena su campo myContactInfo con una nueva instancia ContactInfo y, a continuación, guarda la instancia Employee con pm.makePersistent(...), el almacén de datos creará dos entidades. Una es del tipo "ContactInfo" y representa la instancia ContactInfo. La otra es del tipo "Employee". La clave de la entidad ContactInfo contiene la clave de la entidad Employee como entidad principal del grupo de entidades.
Las clases insertadas te permiten generar un valor de campo a través de una clase sin crear una nueva entidad del almacén de datos ni formar una relación. Los campos del valor de objeto se almacenan directamente en la entidad del almacén de datos del objeto contenedor.
Se puede utilizar cualquier clase de datos @PersistenceCapable como un objeto insertado en otra clase de datos. Los campos de la clase @Persistent se insertan en el objeto. Si asignas la clase para insertar la anotación @EmbeddedOnly, la clase se utilizará únicamente como una clase insertada. La clase insertada no necesita un campo de clave principal, ya que no se almacena como una entidad independiente.
A continuación se muestra un ejemplo de una clase insertada. Este ejemplo convierte la clase insertada en una clase interna de la clase de datos que la utiliza; esto resulta útil, aunque no es necesario para convertir una clase en insertable.
import javax.jdo.annotations.Embedded;
import javax.jdo.annotations.EmbeddedOnly;
// ... imports ...
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class EmployeeContacts {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Long id;
@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 de una entidad y utilizan el nombre de cada campo y el nombre de la propiedad correspondiente. Si el objeto cuyo tipo es una clase insertada contiene más de un campo, deberás renombrar los campos de uno para que no entren en conflicto con los de otro. Especifica los nuevos nombres de campos a través de argumentos a la anotación @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;
Del mismo modo, los campos del objeto no deben utilizar nombres que entren en conflicto con los campos de las clases insertadas, a menos que se renombren los campos insertados.
Debido a que las propiedades persistentes de la clase insertada se almacenan en la misma entidad que la de los demás campos, puedes utilizar los campos persistentes de la clase insertada en la ordenación y el filtrado de la consulta JDOQL. Puedes consultar el campo insertado mediante el nombre del campo exterior, un punto (.) y el nombre del campo insertado. Esto funciona independientemente de si los nombres de las propiedades de los campos insertados se han modificado mediante anotaciones @Column.
select from EmployeeContacts where workContactInfo.zipCode == "98105"
Una propiedad del almacén de datos puede contener más de un valor. En JDO, esto se representa mediante un campo único que contiene un tipo de colección, en el que esta es uno de los tipos de valores principales o una clase serializable. Se admiten los siguientes tipos de colecciones:
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 lista (List), los objetos devueltos por el almacén de datos contendrán un valor ArrayList. Si un campo se declara como conjunto (Set), el almacén de datos devolverá un conjunto de hash (HashSet). Si un campo se declara como un conjunto ordenado (SortedSet), el almacén de datos devolverá un conjunto de árbol (TreeSet).
Por ejemplo, un campo cuyo tipo sea List<String> se almacena como cero o más valores de cadena para la propiedad, uno para cada valor de List.
import java.util.List;
// ... imports ...
// ...
@Persistent
List<String> favoriteFoods;
Una colección de objetos secundarios (de clases @PersistenceCapable) crea múltiples entidades que contienen una relación uno a varios. Consulta Relaciones.
Las propiedades del almacén de datos, que contienen más de un valor, tienen un comportamiento especial en lo que respecta al filtrado de consultas y a la ordenación. Para obtener más información, consulta Consultas e índices: criterios de ordenación y propiedades con varios valores.
El almacén de datos de App Engine distingue entre una entidad sin una propiedad determinada y una entidad con un valor null para una propiedad. JDO no admite esta distinción: todos los campos de un objeto tienen un valor, posiblemente null. Si un campo que contiene un tipo de valor nulo (diferente del tipo integrado como, por ejemplo, int o boolean), se establece en null, al guardar el objeto, la propiedad de la entidad tendrá establecido un valor nulo.
Si una entidad de almacén de datos se carga en un objeto, uno de los campos del objeto no contiene una propiedad y el tipo de campo es de valor nulo único, el campo se establecerá en null. Al volver a guardar el objeto en el almacén de datos, la propiedad null se establecerá en el valor nulo en el almacén de datos. Si el tipo de campo no es de valor nulo, al cargar una entidad sin la propiedad correspondiente, se generará una excepción. Esto no ocurrirá si la entidad se creó desde la misma clase JDO utilizada para volver a crear la instancia, aunque puede ocurrir si la clase JDO se modifica o si la entidad se creó mediante un API de nivel inferior en lugar de JDO.
Si un tipo de campo es una colección de un tipo de dato principal o una clase serializable y no existen valores para la propiedad de la entidad, la colección vacía se representará en el almacén de datos mediante el establecimiento de la propiedad en un valor nulo único. Si el tipo de campo es un conjunto, se asignará un conjunto de 0 elementos. Si se carga el objeto y la propiedad no contiene ningún valor, se asignará al campo una colección vacía del tipo apropiado. Internamente, el almacén de datos está al tanto de la diferencia entre una colección vacía y una colección que contiene un valor nulo.
Si la entidad contiene una propiedad sin un campo correspondiente en el objeto, el objeto no podrá acceder a esa propiedad. Si el objeto se vuelve a guardar en el almacén de datos, se eliminará la propiedad adicional.
Si una entidad contiene una propiedad cuyo tipo de valor es diferente del del campo correspondiente del objeto, JDO intentará asignar el valor al tipo de campo. Si no se puede asignar el valor al tipo de campo, JDO generará una excepción ClassCastException. En el caso de los números (números enteros largos y flotantes de doble ancho), el valor se convierte, no se asigna. Si el valor numérico de la propiedad es superior al tipo de campo, la conversión se desbordará sin generar una excepción.