Google Code предлагается на следующих языках: English – Español – 日本語 – 한국어 – Português – Pусский – 中文(简体) – 中文(繁體)
Сохранение объекта данных JDO в хранилище данных состоит попросту в вызове метода makePersistent() экземпляра PersistenceManager. JDO-реализация App Engine использует поле первичного ключа объекта, чтобы отслеживать, какому объекту в хранилище соответствует объект данных, и может создавать ключи для новых объектов автоматически. Ключи позволяют быстро получать объекты. Создавать ключи можно из известных значений (например, идентификаторов аккаунтов).
Чтобы сохранить простой объект данных в хранилище данных, следует вызвать метод makePersistent() класса PersistenceManager и передать ему экземпляр объекта.
PersistenceManager pm = PMF.get().getPersistenceManager();
Employee e = new Employee("Alfred", "Smith", new Date());
try {
pm.makePersistent(e);
} finally {
pm.close();
}
Метод makePersistent() вызывается синхронно и возвращает результат только после окончания сохранения объекта и обновления индексов.
Чтобы сохранить несколько объектов в JDO-реализации, вызовите метод makePersistentAll(...) и передайте ему коллекцию объектов (Collection). В текущем выпуске такой вызов реализуется просто как последовательность нескольких вызовов метода makePersistent(). В будущем этот метод сможет выполнять более эффективные пакетные вызовы к хранилищу данных.
Примечание. Если какие-либо из постоянных полей объекта данных являются ссылками на другие постоянные объекты данных, и эти объекты еще не сохранялись или были изменены с момента загрузки, объекты, на которые ссылается сохраняемый объект данных, также будут сохранены в хранилище данных. См. статью Отношения.
У каждого объекта в App Engine имеется уникальный ключ. Полный ключ содержит различные данные, включая идентификатор приложения, тип и идентификатор объекта. (Ключи также содержат сведения о группах объектов; подробнее об этом рассказано в статье Транзакции.)
Ключ объекта хранится в поле экземпляра. Определить поле первичного ключа можно с помощью аннотации @PrimaryKey.
При создании объекта приложение может предоставить идентификатор для ключа в виде строки или разрешить хранилищу данных сгенерировать цифровой идентификатор автоматически. Полный ключ должен быть уникальным среди всех объектов в хранилище данных. Иными словами, у объекта должен быть такой идентификатор, который уникально выделял бы его среди всего объектов того же типа и с тем же родителем группы объектов (при наличии). Желаемое поведение ключа можно задать с помощью типа поля и аннотаций.
Если в отношениях класс выступает в роли дочернего, то поле его ключа должно иметь такой тип, с помощью которого можно представить родителя группы объектов: либо экземпляр класса Key, либо значение Key, закодированное в виде строки. Подробнее о группах объектов рассказано в статье Транзакции, а об отношениях – в статье Отношения.
Совет. Если приложение создает новый объект и присваивает ему такой же строковый идентификатор, что и другому объекту того же типа (с тем же родителем группы объектов), то при сохранении нового объекта будет перезаписан объект, существующий в хранилище данных. Чтобы перед созданием объекта определить, не используется ли строковый идентификатор каким-либо другим объектом, можно с помощью транзакции попытаться получить объект с заданным идентификатором, а затем создать его, если он не существует. См. статью Транзакции.
Существует четыре типа полей первичного ключа:
Длинное целое значение (java.lang.Long), идентификатор объекта, автоматически создаваемый хранилищем данных. Этот вариант используется для объектов, не имеющих родителя группы объектов, если идентификаторы должно автоматически создавать хранилище данных. Поле ключа типа Long заполняется при сохранении экземпляра.
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
// ...
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
Строка (java.lang.String), идентификатор объекта ("название ключа"), предоставляемый приложением при создании объекта. Этот вариант используется для объектов, не имеющих родителя группы объектов, если идентификаторы должно предоставлять приложение. Приложение записывает нужный идентификатор в это поле перед сохранением объекта.
import javax.jdo.annotations.PrimaryKey;
// ...
@PrimaryKey
private String name;
Экземпляр класса Key (com.google.appengine.api.datastore.Key). Значение ключа содержит ключ родителя группы объектов (при наличии) и либо назначенный приложением строковый идентификатор, либо сгенерированный системой цифровой идентификатор. Чтобы создать объект со строковым идентификатором, назначенным приложением, следует создать значение Key с полем идентификатора и присвоить этому полю нужное значение. Чтобы создать объект с цифровым идентификатором, назначенным системой, оставьте в поле ключа нулевое значение. (Подробнее об использовании родителей групп объектов рассказано в статье Транзакции.)
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;
}
Приложение может создать экземпляр класса Key с помощью класса 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, но значение представляет собой закодированную строковую форму ключа. Закодированные строковые ключи дают вам возможность создавать переносимые приложения, при этом не теряя преимуществ использования групп объектов хранилища данных App Engine.
import javax.jdo.annotations.Extension;
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)
@Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
private String encodedKey;
Приложение может заполнить это значение перед сохранением объекта с помощью ключа, имеющего название, либо оставить его нулевым. Если поле закодированного ключа является нулевым, оно будет заполнено ключом, созданным системой, при сохранении объекта.
Экземпляры класса Key можно преобразовывать в представление закодированной строки и обратно с помощью методов keyToString() и stringToKey() класса KeyFactory.
При использовании закодированных строковых ключей доступ к цифровому или строковому идентификатору объекта можно предоставить с помощью дополнительного поля:
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
@Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
private String encodedKey;
@Extension(vendorName="datanucleus", key="gae.pk-name", value="true")
private String keyName;
// OR:
@Extension(vendorName="datanucleus", key="gae.pk-id", value="true")
private Long keyId;
В поле "gae.pk-name" можно записать название ключа перед сохранением объекта. При сохранении объекта в поле закодированного ключа записывается полный ключ, содержащий название ключа. Оно должно иметь тип String.
Поле "gae.pk-id" заполняется при сохранении объекта и не может быть изменено. Оно должно иметь тип Long.
Когда новый объект со сгенерированным ключом (полем ключа, для которого используется valueStrategy = IdGeneratorStrategy.IDENTITY) создается, ключ имеет значение null. Поле ключа заполняется при записи объекта в хранилище данных. Если используется транзакция, то запись объекта выполняется при ее завершении. В противном случае запись объекта выполняется при вызове метода makePersistent(), если объект создается, либо при вызове метода close() экземпляра PersistenceManager, если он обновляется.
Если приложению известны все элементы полного ключа объекта, оно может создать соответствующий экземпляр Key, не обращаясь к объекту.
Для получения ключа объекта, не имеющего родителя группы объектов, можно воспользоваться статическим методом createKey() класса KeyFactory. Этот метод принимает тип (простое название класса) и либо назначенный приложением строковый идентификатор, либо назначенный системой цифровой идентификатор, а затем возвращает объект Key. Например, чтобы воссоздать ключ объекта, имеющего тип "Employee" и название ключа "Alfred.Smith@example.com" (без родителя группы объектов), выполните следующее:
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
// ...
Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
Чтобы воссоздать ключ объекта, имеющего тип "Employee" и присвоенный системой цифровой идентификатор 52234 (без родителя группы объектов), выполните следующее:
Key k = KeyFactory.createKey(Employee.class.getSimpleName(), 52234);
Объекты Key можно преобразовывать в представление закодированной строки и обратно с помощью методов keyToString() и stringToKey() класса KeyFactory соответственно. (Обратите внимание, что эти методы отличаются от метода toString() класса Key, который возвращает понятное человеку значение, подходящее для отладки.)
Для получения ключа объекта, имеющего родителя группы объектов, можно использовать класс KeyFactory.Builder:
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
// ...
Key k = new KeyFactory.Builder(Employee.class.getSimpleName(), 52234).addChild(ExpenseReport.class.getSimpleName(), "A23Z79").getKey();
Метод addChild() экземпляра класса Builder возвращает объект Builder, поэтому вы можете связывать вызовы для добавления каждого элемента к пути ключа. Чтобы получить полное значение Key для заданного построителя, следует вызвать метод getKey() класса Builder.
Подробнее о группах объектов рассказано в статье Транзакции.
Чтобы получить объект по его ключу, воспользуйтесь методом getObjectById() класса PersistenceManager. Этот метод принимает класс объекта и его ключ:
Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
Employee e = pm.getObjectById(Employee.class, k);
Если в классе используется поле ключа, содержащее незакодированный строковый идентификатор (String) или цифровой идентификатор (Long), метод getObjectByID() может принять в качестве второго аргумента (ключа) простое значение:
Employee e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com");
Аргумент-ключ может иметь любой поддерживаемый тип поля ключа (строковый или цифровой идентификатор, значение Key, закодированный строковый ключ), причем этот тип может отличаться от поля ключа в классе. App Engine должен суметь восстановить полный ключ из названия класса и предоставленного значения. Строковые и цифровые идентификаторы являются исключительными, поэтому вызов, использовавший цифровой идентификатор, никогда не вернет объект со строковым идентификатором. Если используется значение Key или закодированный строковый ключ, ключ должен ссылаться на объект, тип которого представлен классом.
Одним из способов обновления объекта в JDO-реализации является его получение и изменение, пока экземпляр PersistenceManager, вернувший объект, еще открыт. Изменения сохраняются при закрытии экземпляра PersistenceManager. Например:
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();
}
}
Поскольку PersistenceManager вернул экземпляр Employee, PersistenceManager знает обо всех изменениях, внесенных в постоянные поля Employee, и автоматически обновляет объект в хранилище данных, когда закрывается. Он знает об изменениях, поскольку экземпляр Employee "прикреплен" к PersistenceManager.
Можно изменить объект после закрытия PersistenceManager, объявив класс "отделяемым". Для этого нужно добавить атрибут detachable к аннотации @PersistenceCapable:
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
public class Employee {
// ...
}
Теперь вы сможете считывать и записывать поля объекта Employee после того, как закроется загрузивший его PersistenceManager. В следующем примере показано, как можно использовать отделяемый объект:
public Employee getEmployee(User user) {
PersistenceManager pm = PMF.get().getPersistenceManager();
pm.setDetachAllOnCommit(true);
try {
e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com");
} finally {
pm.close();
}
return e;
}
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);
}
}
Отделяемые объекты являются неплохой альтернативой созданию объектов передачи данных. Более подробно о работе с отделяемыми объектами рассказано в документации по DataNucleus.
Чтобы удалить объект из хранилища данных, следует вызвать метод deletePersistent() класса PersistenceManager и передать ему объект:
pm.deletePersistent(e);
Если у объекта есть поля, которые содержат дочерние объекты, также хранящиеся постоянно, эти дочерние объекты тоже будут удалены. Дополнительная информация приведена в статье Отношения.