Saving Key with Objectify - google-app-engine

When saving a Objectify Entity to the Appengine datastore, is it possible to save a custom Datastore "Key" instead of the auto generated Key hash?

You can use any String or long and annotate it as being the key, like this:
import javax.persistence.Id;
public class MyEntityClass {
#Id
private String myCustomKey;
public MyEntityClass(String keyId) {
this.myCustomKey = keyId;
}
}
Then you can retrieve it again using:
MyEntityClass object = ofy().get(new Key<MyEntityClass>(MyEntitiyClass.class, "specificKeyId"));

Related

Get spring-data-mongodb to honor getter/setter without backing field?

I have a general-purpose POJO:
public class Thing {
private String name;
private String etc;
public String getName() {
return name;
}
// other getters and setters
}
I'm using Spring 4.3.9 and Spring-data-mongodb 1.10.4. I want to store instances of this POJO in Mongodb, but I have some constraints:
I can't add Spring annotations to the base class (but I can subclass Thing and annotate that).
I want to use the name field as the Mongodb unique ID (mainly to avoid creating a separate unique index for it).
I want to (redundantly) store the name field as an actual field named "name", so that other consumers of the collection don't have to know that "name" is stored in the _id.
I started out trying this:
public class SpringThing extends Thing {
#Id
#Override
public String getName() {
return super.getName();
}
#Override
public void setName(String name) {
super.setName(name);
}
}
This causes spring to use the value of name for _id, but of course it doesn't store a field named "name" in Mongodb. The documentation says that spring will use a "property or field" named "id" or annotated with #Id. So I tried defining a redundant getter/setter which accesses the name field:
public class SpringThing extends Thing {
#Id
public String getId() {
return super.getName();
}
public void setId(String id) {
super.setName(id);
}
}
Unfortunately, spring ignores getId and setId here, and stores the object with an autogenerated ID. I also tried creating redundant getters/setters annotated with #Field("name"), but spring seems to ignore any getter/setter pair without an actual field.
Adding an actual ID field and storing a copy of the name there does work:
public class SpringThing extends Thing {
#Id
private String id;
#Override
public void setName(String id) {
this.id = id;
super.setName(id);
}
}
But it requires defining a pointless field named "id".
Is there a more reasonable way to get what I want? Is what I'm trying to do reasonable to begin with?
Thanks to a hint by #mp911de, I ended up creating a subclass of Thing that looks like this:
#TypeAlias("thing")
#Document(collection = "things")
public class SpringThing extends Thing {
#Id
#AccessType(Type.PROPERTY)
#JsonIgnore
public String getId() {
return super.getName();
}
public void setId(String taskName) {
super.setName(taskName);
}
}
The #TypeAlias annotation overrides the name which spring would use for the type, to cover up the fact that I've created a subclass just to add annotations.
#Id says that this is the getter for _id.
#AccessType says to access this field through the getter and setter rather than by directly accessing the field. This is what I needed; without it, spring looks for a private member variable named something like id.
#JsonIgnore is the Jackson (JSON library that we're using) annotation to prevent including the id field when serializing these objects to JSON.

Google App Engine Datastore how to get entity for Key<?>

I'm new to Datastore and now I'm developing Gae application with Datastore and Objectify. Entity class has the form
#Entity
public class MyClass1 {
public Key<MyClass2> field1;
#Id Long id;
and so on
}
MyClass2 has the form
#Entity
public class MyClass2 {
....
#Id public Long id;
#Index public String field2;
....
}
I have entity of MyClass1. How can I get the value of field2 ?
If I use DatastoreService1.get(myclass1.field1) I get
method get(Key) in the DatstoreService is not applicable for arguments (Key<MyClass2>)
Please help me
Your error doesn't really relate to the question, so I assume you want to get the value of field1, not field2.
The error is because the DatastoreService get() method expects a com.google.appengine.api.datastore.Key, but you are passing it a com.googlecode.objectifyKey<T>, which is an Objectify key - they're not the same thing. Here are the docs.
I would recommend using a Ref<MyClass2> as you can then just do something like:
MyClass2 myClass2 = myClass1.field1.get();
(using your code example).

Objectify does not fetch by String ID

I have a datastore kind in which the ID field is String
ofy().load().type( Scores.class ).id( playerName ).now();
This fetches null. I have confirmed the entity with the given playername exists.
This does not happen to another Kind whose ID is long.
Code for Scores class
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Parent;
#Entity
public class Scores
{
#Parent
public Key<RankerRoot> parentKey;
#Id
public String playerName;
public int value;
}
You need to be sure you are giving Objectify enough information to construct the entity's Key. So, if the entity has an ancestor, the ID/Name alone will be insufficient.
If your entity has an ancestor, you can construct the Key and then load the entity by Key, like this:
Key<Scores> scoreKey = Key.create(parentKey, Scores.class, playerName);
ofy().load().key(scoreKey).now();

Parent Entity Google Cloud API

I'll explain the situation. I followed these two great tuto:
http://rominirani.com/2014/01/10/google-cloud-endpoints-tutorial-part-1/
https://cloud.google.com/developers/articles/how-to-build-mobile-app-with-app-engine-backend-tutorial
However, they explain how create API for Google Cloud Endpoint, with entities independent from each other. So the API class looks like this:
package com.example.mobileassistant;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* Offer entity.
*/
#Entity
public class Offer {
#Id
private String offerId;
private String title;
private String description;
private String imageUrl;
.... (With Getter and Setter)
}
My question is: How to create a class with a parent entity of another? Like the entitie Wheel, with Parent Car. I can not create a relationship with its parent entity (With the annotation)!
I tried this but it did not seem to work (Foreign Key):
package com.example.mobileassistant;
import javax.jdo.annotations.ForeignKey;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* Offer entity.
*/
#Entity
public class Wheel {
#Id
private String Id;
#ForeignKey
private String parent_id;
private String title;
private String description;
.... (With Getter and Setter)
}
Actually, I have given up JDO because it don't really support parent entity, like I can read here: http://www.igorkromin.net/index.php/2013/03/14/jdo-youre-dumped-app-engine-and-bi-directional-relationships/
So I'm using now Objectify with Google Cloud Endpoint (For my Android App) and It works so perfect, especially with parent/child relationship !! You can read example here: http://blog.xebia.fr/2014/06/23/google-app-engine-cloud-endpoint-creer-notre-api-v2-et-lutiliser-avec-angularjs/
Why would it when you put it there?
See this page
http://www.datanucleus.org/products/accessplatform/jpa/annotations.html#ForeignKey
which explains clear enough that it is part of the JoinColumn, JoinTable, CollectionTable, SecondaryTable annotations.

GAE Datastore with GWT, making more friendly/smaller keys

I am currently working with GWT, GAE and using JPA as my ORM. I have an issue where the keys that GAE is generating are too large reasonably to be used on a mobile device with RequestFactory. The amount of data in a small list is overwhelming due to the size of the ID/KEY when converted to String.
I am using String for my key's so that I can handle inheritence.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
protected String key;
This creates a key that is very long example "agxzbWFydGJhcnNpdGVyFAsSDUVzdGFibGlzaG1lbnQYuAIM" and gets larger due to storing object type and parent in the key.
I need a way to create a smaller unique id but still have the ability to handle inheritence in GAE. I tried Long as the #Id/key but was not able to use a #OneToMany relationship on my objects due to the relationship that is built into the String/Key key.
The other option is to create a sequence for each class and use a Long property for that id. There is an example below but I am not sure how to handle a generated Long sequence in app engine.
#GeneratedValue
private Long friendlyClassSpecificKey;
Any advice would be appreciated. If there is another option other than using the sequence for each class type I am interested but if not is there an example of creating a sequence (That is not the #ID) for a specific class?
I came up with a good solution for smaller keys. I think the best way to do this cleanly is to use jpa/jdo 2 for app engine with an unowned relationship. This way you can fetch the keys from (Long) id using just their type and not have to use the parent relationship.
This is the base datstore object and notice I am using the app engine key.
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class DatastoreObject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
public Long getId() {
return key.getId();
}
}
This class will use the #Unowned attribute supported in jpa 2 so that the inventory's key does not contain the parent establishment key. Otherwise you would have to pass the parent id in also and resolve that to a key based on type. This is because in an owned relationship the child key contains the parent key also.
#Entity
public class Establishment extends DatastoreObject {
#Unowned
#OneToOne(cascade = CascadeType.ALL)
private Inventory inventory;
}
Then in my dao base class I use the class
public class DaoBase<T extends DatastoreObject> {
protected Class<T> clazz;
#SuppressWarnings("unchecked")
public DaoBase() {
clazz = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
/**
* Find an object by it's shortened long id
* #param id
* #return
* #throws EntityNotFoundException
*/
public T find(Long id) {
if (id == null) {
return null;
}
EntityManager em = ThreadLocalPersistenceManager.getEntityManager();
Key key = getKey(id);
T obj = em.find(clazz, key);
return obj;
}
protected Key getKey(Long id) {
return KeyFactory.createKey(clazz.getSimpleName(), id);
}
}

Resources