Find an object by ID that has a key member only - google-app-engine

Im trying to query an object by ID that dosent have an ID field.
Doing key.getId() does return a number but will not work when using
//will not work due to parenting object
pm.getObjectById("EventModel", 5322);
public class EventModel extends Model
{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
protected Key key;
...
}
NOTE: this is object is parented by a different object
i tried to query using "key.id == idParam" and it fails because the object key is not embedded is there any way to make this work so i can query the object by id not just only the key
A workaround will be to save the an id field after the key is initiazlied and query it
but this adds 2 writes each time i create an object.
If you have any idea on how to make this work please share :)
Thanks

Related

GAE get Data using JDO with key

I created Key with my unicue ID, and save into the database.
GoogleDrivePDF pdf = new GoogleDrivePDF();
key= KeyFactory.createKey(GoogleDrivePDF.class.getSimpleName(),"0B1IQEoiXFg3IWHhJNEtlMzlvQWs");
pdf.setKey(key);
pdf.setPdfName("NAME");
everything works!
But know I have one problem.
In order to get my Object From Db I must need Key string.
GoogleDrivePDF pdf = pm.getObjectById(GoogleDrivePDF.class, "agtsdHYtY2hlY2tlcnJoCxIRVXNlcnNQREZEb2N1bWVudHMiIXZha2h0YW5nLmtvcm9naGxpc2h2aWxpQGdtYWlsLmNvbQwLEg5Hb29nbGVEcml2ZVBERiIcMEIxSVFFb2lYRmczSVdIaEpORXRsTXpsdlFXcww");
how to create that key? I think it is enycrypted key. How can I now get this object?
P.S I think because of I have one to many relationship, that may does not work. Because When I create that pdf without 1:N it gets object very well. But I cant get another datas, which are in 1:N
I have that error:
Could not retrieve entity of kind GoogleDrivePDF with key GoogleDrivePDF("0ByMb_8ccYJRFOS1ibmFtamZ1b2M")
org.datanucleus.exceptions.NucleusObjectNotFoundException: Could not retrieve entity of kind GoogleDrivePDF with key GoogleDrivePDF("0ByMb_8ccYJRFOS1ibmFtamZ1b2M")
If you want to create a Key based on some other field of your Entity (say, for instance, a field called uniqueID), you should do something like this:
Persisting:
GoogleDrivePDF pdf = new GoogleDrivePDF();
pdf.setUniqueID("0B1IQEoiXFg3IWHhJNEtlMzlvQWs");
Key key = KeyFactory.createKey(GoogleDrivePDF.class.getSimpleName(),pdf.getUniqueID());
pdf.setKey(key);
pm.makePersistent(pdf);
BTW, it is generally not a good choice to make the key field publicly accessible. I think it is better to put the code for the creation of the key inside a specific constructor, like
public GoogleDrivePDF(String uniqueID) {
this.setUniqueID(uniqueID);
this.key = KeyFactory.createKey(GoogleDrivePDF.class.getSimpleName(),uniqueID);
}
Retrieving:
String idToBeRetrieved = "0B1IQEoiXFg3IWHhJNEtlMzlvQWs";
Key key = KeyFactory.createKey(GoogleDrivePDF.class.getSimpleName(),idToBeRetrieved);
pm.getObjectById(GoogleDrivePDF.class, key);
Solution was Unowned:
#Unowned
#Persistent
private Map <String,GoogleDrivePDF > pdfs;

JDO on GAE - #Unowned fields returned as nulls

Let's say I have a very easy, classic setup: GAE(1.7.4) + GWT(2.5.0) Application, running on local Jetty (Development Server), using JDO for persistence.
Let's also say I have just 2 #PersistenceCapable classes: Person and Color. Every Person has exactly one favourite Color, but it does not mean that this Person owns this Color - many different Persons can have the same favourite Color. There is a limited number of well-known Colors and a Color may exist even if it is not anyone's favourite.
To model this I should use #Unowned relationship - please correct me if I am wrong:
#PersistenceCapable
public class Color { // just the most regular Entity class
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
String rgb;
// getter, setter, no constructor
}
#PersistenceCapable
public class Person {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
String surname;
#Persistent
#Unowned // here is the tricky part
Color color;
// getters, setters, no constructor
}
With some simple, well-known, PersistentManager-based code, I am able to successfully create and persist an instance of a Color class. I see it in GAE Development Console -> Datastore Viewer, having nice generated Key and ID/Name of (13), and my assigned RGB.
With very similar code, I am able to create an instance of Person class (in another request), assign a pre-existing Color as his favourite color (it pre-existed, I obtained it by pm.getObjectById()) and persist it. I see it in Datastore Viewer, with my nice generated Key and ID/Name of (15) and my assigned surname, and color_key_OID of (13). This looks very promising.
But then, when I fetch the Person(15) back from the DB (simple pm.getObjectById(), no transactions), it has my assigned surname correctly, but has null instead of Color(13)! Right - the Datastore Viewer gets it ok, but my code does not.
Oh, the problematic code? "Person p = pm.getObjectById(Person.class, key);".
(side notes: I am also having the same problem with #Unowned collections (nice list of values in Datastore Viewer, but null Collection field in my code.) My JDO jars on classpath are "datanucleus-api-jdo-3.1.1.jar" and "jdo-api-3.0.1.jar" so I assume they support #Unowned. There is no problem with not-#Unowned fields. I get no exceptions upon persisting or fetching, just plain nulls as field values.)
Either mark the color to be "eagerly fetched"
#Persistent(defaultFetchGroup="true")
#Unowned
Color color
or define your own fetchgroup like this:
#FetchGroup(name="eager", members={#Persistent(name="color")})
#PersistenceCapable
public class Person {
and use it if required by specifying the group to be fetched:
PersistenceManager pm = pmf.getPersistenceManager();
pm.getFetchPlan().addGroup("eager");
I was facing the same issue in one of my #Unowned Lists. I had more other two, which the Array is fetched perfectly.
What solved this issue for me was to change the name of property for a bigger one. In your case is like change the property name from "color" to something bigger, like "myfavoritecolor".
I have the same issue what you describe. How DataNucleus said you need to describe the whole lifecycle of the objects. In my case the problem was solved forcing getting the color, from the person object, before closing the PersistenceManager with the close() function.
Remember JDO uses the lazy-load technique to get objects.
I was able to solve this problem by adding fetch groups to the query and not to persistent manager.
PersistenceManager pm = PMF.get().getPersistenceManager();
logger.info("EVENTS FETCH GROUPS : " + pm.getFetchPlan().getGroups());
/*pm.getFetchPlan().addGroup("eventFetchGroup");
pm.getFetchPlan().setMaxFetchDepth(2);*/
Query q = pm.newQuery(Event.class);
q.getFetchPlan().addGroup("eventFetchGroup");
logger.info("EVENTS FETCH GROUPS : " +q.getFetchPlan().getGroups());
q.setFilter("date >= fromDate && date <= toDate");
q.declareParameters("java.util.Date fromDate, java.util.Date toDate");

JDO-GAE Key getting created even when IdGeneratorStrategy.IDENTITY is commented

I have the following code,
public class UserAccount {
#PrimaryKey
#Persistent
private Long id;
....
so, creating the primary key via code, but when I view the datastore viewer through
http://localhost:8888/_ah/admin , I see key got populated via datastore (some encoded key). Is this an expected behavior ?. The problem however is when I try to connect my child objects with the parent objects. Thanks alot.
Yes, it's absolutely expected. For more info take a look at JDO Keys

Add a new attribute to entity in datastore?

I have an entity in my app engine datastore. There's actually only one instance of this entity. I can see it in my admin console. Is it possible to add a new attribute to the entity via the admin console (using gql perhaps)?
Right now it looks something like:
Entity: Foo
Attributes: mName, mAge, mScore
and I'd like to add a new boolean attribute to this entity like "mGraduated" or something like that.
In the worst case I can write some code to delete the entity then save a new one, but yeah was just wondering.
Thanks
-------- Update ---------
Tried adding the new attribute to my class (using java) and upon loading from the datastore I get the following:
java.lang.NullPointerException:
Datastore entity with kind Foo and key Foo(\"Foo\") has a null property named mGraduated.
This property is mapped to com.me.types.Foo.mGraduated, which cannot accept null values.
This is what my entity class looks like, I just added the new attribute (mGraduated), then deployed, then tried loading the single entity from the datastore (which produced the above exception):
#PersistenceCapable
public class Foo
{
#PrimaryKey
private String k;
/** Some old attributes, look like the following. */
#Persistent
#Extension(vendorName = "datanucleus", key = "gae.unindexed", value="true")
private String mName;
...
/** Tried adding the new one. */
#Persistent
#Extension(vendorName = "datanucleus", key = "gae.unindexed", value="true")
private boolean mGraduated;
The only way to implement this is to use Boolean as the type for the new property..
Than in set method you can accept boolean value, that's no issue.
If you want the get method to also return boolean.. you also can, but be sure to check if the value is null and if so.. return default value (e.g. true)
so
private Boolean newProp = null; // can also assing default value .. e.g. true;
public void setNewProp(boolean val)
{
this.newProp = val;
}
public boolean getNewProp()
{
if(this.newProp == null)
return true; // Default value if not set
return this.newProp.booleanValue();
}
I recommend you not to migrate your data in this case - it can be very costly and can deplete your quota easily (read old data, create new, delete old = 3 operations for every entry in you data store)
You can't do this through the admin console, but you shouldn't have to delete the entity. Instead just update it- the Datastore does not enforce schemas for Kinds.
E.g., if Foo is a subclass of db.Model (Python), change your model subclass to include the new property; fetch the model instance (e.g., by its key), update the instance, including setting the value of the new field; and save the modified instance. Since you just have one instance this is easy. With many such instances to update you'd probably want to do this via task queue tasks or via a mapreduce job.
You have declared the new mGraduated field using the primitive type boolean, which cannot be null. The existing entity can't be loaded into the model class because it doesn't have this property. One option is to declare this property using the Boolean class, which can accept a null value.
The Admin Console only knows about properties in existing entities. You cannot use the Admin Console directly to create a new property with a name not used by any existing entities. (This is just a limitation of the Console. App code can do this easily.)

How to auto-fetch JDO nested collection of entities?

Probably a very trivial problem.
I have an object that looks like this:
#PersistenceCapable
public class Parent {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private String _id;
#Persistent
private List<Child> _children;
//...
}
... the nested entity looks like this (I am forced to declare primary key as Key otherwise it won't persist):
#PersistenceCapable
public class Child {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key _id;
#Persistent
private String _whatever;
//...
}
When I persist everything gets persisted OK (including Child entities), but I would like to get back everything by getting the parent object (e.g. getObjectById), but the collection comes back as null.
Owned One-to-Many Relationships seem to be what I am looking for -- but I am having trouble to see how it can help me to get back the parent object with the populated collection of children entities.
Any help appreciated!
#Persistent(defaultFetchGroup = "true")
Does the trick, you're right.
The content of your _children attribute is loaded only when you access it (before pm.close !) for the first time. It's called lazy-loading. If you want to have the child Entity or Collection of Child Entities to be directly loaded by default, apply the above "trick".
In my app, in case of a Collection of child Entities, it generates an Error message (Datastore does not support joins..) on the Dev Server, but you can ignore this wrong error, it is working fine in Dev and Prod Environments.
Be aware that fetching a Collection through it's parent Entity costs 1 datastore fetch per Child Entity.
this seems to do the trick:
#Persistent(defaultFetchGroup = "true")
True, while setting
#Persistent(defaultFetchGroup = "true")
at the field is the way to auto-load the related object in main object during fetch, things may not work as expected for nested objects if not supported with right configuration. If your have class with related object hosting other related object down to level 2 or 3, then configuring maxFetchDepth appropriately is critical.
<property name="datanucleus.maxFetchDepth" value="2"/>
is the configuration element in your JDOconfig.xml file to configure how deep you want your default fetch group objects to be loaded with main fetch.

Resources