Is there a way to control the fetch size used for owned relationship ?
Example :
#PersistenceCapable
public class Employee {
/** The contact info sets. */
#Persistent(defaultFetchGroup = "true")
#Element(dependent = "true")
private Collection<ContactInfo> contactInfoSets;
/** The key. */
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
public Collection<ContactInfo> getContactInfo() {
return contactInfoSets;
}
}
#PersistenceCapable
public class ContactInfo {
/** The key. */
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
}
In the example above, if I do :
Employee e = pm.getObjectById(Employee.class, "1");
e.getContactInfoSets();
It will fetch every owned contact in group of 20. How to tell jdo to fetch all contacts in a single query ?
PS : I tried to set pm.getFetchPlan().setFetchSize(FetchPlan.FETCH_SIZE_GREEDY); without success.
FetchPlan.setFetchSize is obviously for use by queries and nothing else (as per the spec).
Use of v2 of the GAE JDO plugin ought to load all relation objects without any additional call to the DB (since the ids of the related objects are stored in the owning object). The log tells you what datastore calls are made
Related
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/
I am using DataNeucleus JDO implementation which works fine on my test server but does not work well when deployed.I am new to GAE and I know I that not all DataNeucleus features are supported. Not sure how to make this work.
I have a class say :
#PersistenceCapable
public class MyInfoClass {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Key key;
#Persistent(mappedBy = "myInfoClass")
private List <BuddyData> buddyList;
...
}
And the BuddyData
#PersistenceCapable
public class BuddyData {
...
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Key key;
// the one-to-many rel to MyInfoClass -> BuddyData
#Persistent
private MyInfoClass myInfoClass;
....
}
I save the data to data store as below which works in test environment but does not work when deployed to app engine.
PersistenceManager pm = PMF.get().getPersistenceManager();
Transaction tx = pm.currentTransaction();
try {
tx.begin();
//NOTE : myInfo is the persistent MyInfoClass object retrieved from the datastore
myInfo.getBuddyList().add(new BuddyData(email));
tx.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (tx.isActive()) {
tx.rollback();
}
pm.close();
}
Owned relationships can be quite tricky in GAE. I remember battling with them for hours.
First of all, you have an owned relationship cycle. I doubt that is supported by GAE. So you will need to change one of the entities to use Key to reference the other one, instead of the direct object relationship (i.e. owned relationship).
For example:
#PersistenceCapable
public class BuddyData {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Key key;
// use Key instead of MyInfoClass
#Persistent
private Key myInfoClass;
}
Secondly, when retrieving BuddyData list make sure the PersistenceManager is still open. If you do it like this:
PersistenceManager pm = PMF.get().getPersistenceManager();
MyInfoClass info = pm.getObjectById(MyInfoClass.class, key);
pm.close();
info.getBuddyList();
Then contents of buddyList will be empty.
In such case you either need to move pm.close() after getBuddyList(), or set it as the default fetch group (see below), which will eagerly prefetch all contents at a potential performance cost.
#Persistent(mappedBy = "myInfoClass", defaultFetchGroup = "true")
private List <BuddyData> buddyList;
Good luck.
How do you query for a list of objects stored using GAE, where a Set field contains a specified string? ie Imagine this imaginary example:
#PersistenceCapable
class Photos {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
String name = "";
Set<String> tag = new Hashset<String>();
}
Assuming there are 40,000 photos in the "database", How do I Query all photos where tag='2009'
Or another example from google's documentation, if you have the following class:
#PersistenceCapable
public class Person {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private Set<Key> favoriteFoods;
// ...
}
How do you fetch a list of all Person objects where they have a specific favourite food Key?
How would you do that in Java ?
collField.contains(:stringParam)
Try something like this, which is basically taken from the google documentation:
Query query = pm.newQuery("select from com.example.Note where tag == tagname order by created");
query.declareParameters("String tagname");
return (List<Note>) query.execute(tag);
As per the comment below, this also seems to work:
Query query = pm.newQuery("select from com.example.Note where tags.contains(tagname) order by created");
query.declareParameters("String tagname");
return (List<Note>) query.execute(tag);
I've omitted some code(package declarations, imports, other fields)
for shortness.
I have here simple One-to-Many relation.
It worked fine till this moment.
#PersistenceCapable(identityType = IdentityType.APPLICATION,
detachable="true")
class Restaurant implements Serializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Key id
#Persistent(mappedBy = "restaurant")
List<RestaurantAddress> addresses = new ArrayList<RestaurantAddress>()
}
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#PersistenceCapable(identityType = IdentityType.APPLICATION,
detachable="true")
class RestaurantAddress implements Serializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Key id
#Persistent
Restaurant restaurant
}
Now i need to get(select) all the Restaurants from DB:
def getRestaurantsToExport(final String dst, final int count) {
String field = restaurantExportFields[dst]
return transactionExecute() { PersistenceManager pm ->
Query q = pm.newQuery(Restaurant.class)
q.filter = "$field == null"
q.setRange(0, count)
return q.execute()
}
}
But there are on problem - query gives me 12 restaurants(as in DB) but
every Restaurant has 0 Address but in Datastore every Restaurant has
minimum 2 addresses.
Have anyone the same problem or knows the solution ?
are you sure the Addresses are not lazy loaded? Just a guess... is there some way to force an "eager" loading of the objects
If someone will have the same problem:
Replace
#Persistent(mappedBy = "restaurant")
List<RestaurantAddress> addresses = new
ArrayList<RestaurantAddress>
with
#Persistent(mappedBy = "restaurant",defaultFetchGroup = "true")
List<RestaurantAddress> addresses = new
ArrayList<RestaurantAddress>
Another method is that you have to "touch" addresses property for
every Restaurant in the retrieved list before closing
PersistentManager. After PersistenManager being closed you cannot
retrieve anything from datastore and Restaurant keeps null.
Solution found with help of google-appengine-java users.
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;