I need to have two objects of the same type. By default appengine doesn't allow it, but I found this parameter: datanucleus.appengine.allowMultipleRelationsOfSameType, so I can save the two same type objects.
In debug mode, before calling the makePersistent method I checked a value inside each object and they were differents, however, when I tried to recover the values from the datastore, they were the same. Both had the value of the second object?
This code is to save the object FaseGAE:
manager = GAEDAOFactory.get().getPersistenceManager();
Key faseKey = KeyFactory.stringToKey(grupo.getFaseKey());
FaseGAE faseGAE = manager.getObjectById(FaseGAE.class, faseKey);
faseGAE.addGrupoGAE(grupoGAE);
faseGAE = manager.makePersistent(faseGAE);
manager.close();
This code is to get the object:
manager = GAEDAOFactory.get().getPersistenceManager();
FaseGAE faseGAE2 = manager.getObjectById(FaseGAE.class, faseKey);
FaseGAE object:
#PersistenceCapable
public class FaseGAE {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent private List<GrupoGAE> grupos;
GrupoGAE object:
#PersistenceCapable
public class GrupoGAE {
#PrimaryKey
#Persistent (valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent private List<MyClass1> list;
MyClass1 object:
#PersistenceCapable
public class MyClass1 {
#PrimaryKey
#Persistent (valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent private MyClass2 sameTypeObject1;
#Persistent private MyClass2 sameTypeObject2;
#Persistent private String testValue1;
#Persistent private String testValue2;
MyClass2 Object:
#PersistenceCapable
public class MyClass2{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
testValue1 and testValue2 keeps different values, but sameTypeObject1 and sameTypeObject2 have the value of sameTypeObject2. I checked the datastore and both objects were created with different values. It seems like both point to the same reference.
Am I doing something wrong?
Something it's missing to work with same type relations?
Definitely AppEngine doesn't allow same type relations?
I've encountered a similar problem before, I"m not too sure what's your exact problem and whether it's the same. But hope this answer will at least point you in the right direction
However, there are a couple of "best practices" you can adopt when using java with GAE.
1) implement Serializable for classes
i.e. public class FaseGAE implements Serializable
- this will enable persistent capable classes to be stored and retrieve with session objects.
2) you could try using objectify for GAE datastore
http://code.google.com/p/objectify-appengine/
Related
I have a class Post and within it I have a list of Reviews. Is it possible to retrieve a Post object without reviewList (or as an empty list)? Or maybe I should use some other model to achieve this.
#PersistenceCapable
class Post {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
long id;
#Persistent
String title;
#Persistent
List<Review> reviewList;
}
.
#PersistenceCapable
class Review {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
long id;
#Persistent
String comment;
}
The documentation says:
Accessing a collection performs a query
I read this as: the collection is lazy-loaded. This means that when you load a Post, its reviews are not laoded. They will be loaded automatically when you access the collection (i.e. when calling any method of the collection).
I need some help with a JDO query.
I have the following Entities:
recipe:
#PersistenceCapable
class Recipe{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long key;
...
#Persistent(mappedBy = "recipe")
private List<RecipeIngredient> ingredients
}
recipeIngredient:
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
class RecipeIngredient implements Serializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
Integer amount
#Persistent
Key unit
#Persistent
Key ingredient
#Persistent
Recipe recipe
ingredient:
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
class Ingredient implements Serializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
String name
A Recipe can have several recipeIngredients which holds the actual ingredient, the amount of the ingredient and the unit.
I would like to get all recipes by ingredients which only hold the given ingredients and not more.
At the moment I do this:
get all ingredient objects by ingredient name
get all recipeIngredient objects by ingredient key
get all recipes by recipeIngredient
check if all recipeIngredients from recipe are in recipeIngredient list from before
if so add recipe to output list
Can I do this with a query? maybe something similar to having ?
Try something like...
select r from Recipe r
where ingredients.contains(ri) && (ri.ingredient.name.matches(:searchTxt))
Also take a look at: http://www.datanucleus.org/servlet/forum/listthreads?forum=9
I don't use Google App Engine but I do use DataNucleus heavily and I believe that DataNucleus is the implementation of JDO that the App Engine uses.
Let me know how this works on App Engine as I do this same kind of thing all the time in my apps.
I intend questions not to be a child since I had to manipulate it independently, and I don't want to persist the questions field, I would to fill it up by retrieving the questions manually. Here is the code.
Questionnaire.java
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Questionnaire{
//supposedly non-persistent
public List<Question> questions = new ArrayList<Question>();
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
public Long questionnaireID;
#Persistent
public String title;
#Persistent
private int items;
#Persistent
public String description;
Question.java
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Question{
//non-persistent as well
public ArrayList<Choice> choiceList = new ArrayList<Choice>();
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
public Long questionID;
#Persistent
public String text;
#Persistent
public long questionnaireID;
public Question(){
}
would spit out this error:
org.datanucleus.store.appengine.MetaDataValidator$DatastoreMetaDataException: Error in meta-data for com.ivanceras.server.Question.questionID: Cannot have a java.lang.Long primary key and be a child object (owning field is com.ivanceras.server.Questionnaire.questions).
Adding a #NotPersistent might help.
The GAE/J docs are totally misleading; they suggest that you need #Persistent on every field and that is totally wrong. All fields have a default persistent flag ... things like String, primitives, Collection, List, Set, Map are by default persistent so no need for #Persistent on those. This point has been made to Google several times yet the docs still have this.
Use the DataNucleus docs if you want clear information as per the JDO spec
Adding "transient" may help too
I'm using the Google App Engine in combination with the Google Web Toolkit to write a bug tracker (to see what the technologies are capable of).
Modelled after Google Code's issue tracker, I decided that an issue can have 0 or more labels, that can be defined beforehand (let's say in the settings).
The label class (CustomLabel):
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class CustomLabel implements Serializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
private String encodedKey;
#Persistent
#Extension(vendorName="datanucleus", key="gae.pk-id", value="true")
private Long keyId;
/**
* label caption.
*/
#Persistent
private String caption;
// Unimportant getters / setters
}
Now the parent class (Issue):
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Issue implements Serializable {
private static final long serialVersionUID = 1L;
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
// Replacing the Long key by this key doesn't make a difference
// #PrimaryKey
// #Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
// #Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
// private String encodedKey;
#Persistent
private String summary;
#Persistent
private String description;
#Persistent
private ArrayList<CustomLabel> labels;
// Other details
}
When I'm trying to persist a new Issue with existing CustomLabels I get the following exception:
org.datanucleus.exceptions.NucleusUserException: Detected attempt to establish Issue(11) as the parent of CustomLabel(1) but the entity identified by CustomLabel(1) has already been persisted without a parent. A parent cannot be established or changed once an object has been persisted.
How can this be solved? I cannot use Key's and create an unowned relationship, since I'm sending the objects to the GWT front-end (which is compiled to Javascript and com.google.appengine.api.datastore.Key isn't supported). Besides that would break referential integrity, which is undesirable.
You can't assign an already existing CustomLabel as a child element to a new Issue. Each CustomLabel entity can only belong to one Issue object, due to the way datanucleus handles relationships. It puts both the parent and child object into the same Entity Group. An entity can only belong to one Entity Group. So let's say you create a Custom Label called "nastybug" and persist it. It now belongs to some entity group X. When you create a new Issue, and go to persist that, it will belong to some Entity Group Y. Datanucleus (and the actual google datastore) will not let you try to store an entity from group X into group Y.
If you want labels to be shared among issues, you will need to use an unowned relationship. You are correct that you cannot pass a Key through GWT's RPC mechanism, so you may need to translate your JDO objects into some other form before sending them.
You can read about entity groups here
Use #Unowned for your list:
import com.google.appengine.datanucleus.annotations.Unowned;
...
#Persistent
#Unowned
private List<Category> categories;
And PrimaryKey Long for your CustomLabel id:
#Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
#PrimaryKey
private Long id;
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable ="false")
public class Foo implements IsSerializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
#Persistent
private Long revision;
#Persistent
private String information;
}
The problem is this object keeps overwriting itself when persisted, not creating a new 'record' with the next revision.
In a traditional RDBMS it'd be a two-column primary key.
How do I accomplish this with Google App Engine Datastore?
You need to create and write a new record for each update if you want to keep a revision history. The id uniquely identifies the record - the Datastore has no way of knowing that you consider the revision to be part of the id too.
I think this is the best way to solve it.
#PersistenceCapable(identityType = IdentityType.APPLICATION,detachable = "false")
public class Foo implements IsSerializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long _internalId;
#Persistent
private Long id;
#Persistent
private Long revision;
#Persistent
private String information;
}
where id and revision are treated as the primary key in the application.