Object not storing in Google App Engine datastore - java

I'm having a problem storing an object in the datastore. I have an object, MyObject, that I'm trying to store but when the code is executed nothing happens. I go to look at the datastore dashboard and MyObject isn't there. No exceptions are thrown and there are no errors.
Here's my object
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class MyObject{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
String name;
#Persistent
String beta;
#Persistent
double loc;
public MyObject(String name1){
name = name1;
}
//getters and setters
}
and here's the code to store the object
public static void saveMyObject(MyObject a)throws Exception{
PersistenceManager pm = PMF.get().getPersistenceManager();
try{
pm.makePersistent(a);
}
catch(Exception e){
throw e;
}
finally{
pm.close();
}
}
Can anyone see what I'm missing?

It looks like you are using JDO, so you might want either to add a JDO tag or else mention that somewhere....
I would replace
try
{
pm.makePersistent(a);
}
with
try
{
MyObject myObj = new MyObject(a.getName()); // or whatever the getter is
myObj.setField2(a.getField2()); // Copy 1 data member from a
... // Make a MyObject.copy(...) method?
pm.makePersistent(myObj);
}
The key thing is that JDO uses enhancement: magic bytecode that is inserted after main Java compilation. I manipulate my persistent entity objects within the lifecycle ("scope") of enhancement to get JDO to work.
I also use transactions for writing (I don't know your JDO auto-transaction setting(s)). I always use transactions when creating and persisting a new persistent entity. You might want to try that if the change above does not work.

Related

GAE datastore will not persist owned 1:1 relationship within embedded JDO class

I have three #PC classes:
#PersistenceCapable
class A {
#PrimaryKey #Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
private String id;
#Persistent #Embedded
private B b;
public void setB(B b){
this.b=b;
}
}
#PersistenceCapable #EmbeddedOnly
class B {
#Persistent
private String someInfo;
#Persistent
private C c;
public void setC(C c){
this.c=c;
}
}
#PersistenceCapable
class C {
#PrimaryKey #Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
private String id;
#Persistent
private String value;
public void setValue(String value){
this.value=value;
}
}
I want to achieve that B is persisted to same entity as A while holding a reference to C but GAE does not let me, I get a following exception on commit:
Detected attempt to establish A(1) as the parent of C(2) but the entity identified by C(2) has already been persisted without a parent. A parent cannot be established or changed once an object has been persisted.
in this code:
A a = new A();
B b = new B();
C c = new C();
c.setValue("foo");
b.setC(c);
a.setB(b);
m.makePersistent(a);
additionally: a look into DatastoreViewer shows me that C has been persisted! But A is missing. This might happen because I do not explicitelly rollback the transaction on exception which is not relevant in this case, but reveals that C is written before its parent A.
what am I missing? Tx
Update 2:
as suggested I have enabled transaction explicitely:
Transaction tx = pm.currentTransaction();
try {
tx.begin();
pm.makePersistent(a);
tx.commit();
} finally {
if (tx.isActive()) {
tx.rollback();
}
pm.close();
}
same exception was thrown as when doing .makePersistent() w/o explicit transaction. Then I set disabled the global cross tx option in JDO config:
<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="false"/>
and now get a different exception with a possible hint:
cross-group transaction need to be explicitly specified, see
TransactionOptions.Builder.withXGfound both Element {
type: "A"
id: 1
}
and Element {
type: "C"
id: 2
}
I assume you're problem vanishes if you set up a transaction boundary for this to work. Although the documentation claims that what you do should be possible, I assume you have set datanucleus.appengine.autoCreateDatastoreTxns to true in your jdoconfig.xml.
Thus, C is stored in its own transaction with an entity group of it's own. In a second transaction, you store A and thus try to reassign C to A's entity groupy which is forbidden:
because entity groups can only be assigned when the entities are created
So: set up a transaction (as recommended).
ok FINALLY I got the point,
yet I think that DataStore's JDO implementation ( I don't know if its DataNucleus job) miss there something. According to transactions on DataStore usually only entities along an ancestor can be persisted in one go, exceptionally GAE claims to support Cross-Group Transactions which is limited by number but can persist unrelated entity paths. Both did not apply in my case.
The poor solution is now to enforce unowned relationship via a Key (GAE proprietary), which is the only possible kandidate to describe ancestor path so that DataStore gets it right is following extension to my POJOs:
class A {
...
private String naturalPK;
public String getNaturalPK(){
return naturalPK;
}
...
}
class C {
...
public void setId(String id){
this.id=id;
}
...
}
and the persistency-code:
tx.begin();
// we have to assign parent/child keys to enforce ancestor path
Key parentKey = KeyFactory.createKey("A", A.getNaturalPK());
a.setId(KeyFactory.keyToString(parentKey));
// now build child key
a.getB().getC().setId(KeyFactory.createKeyString(parentKey, "C", A.getNaturalPK()));
pm.makePersistent(offerer);
tx.commit(); // works.
one issue is that I cannot use surrogate keys here, the other thing is that I dont want non-JDO and non-BO code to be in my POJOs, so the parent-child relationship has to be established somewhere in die JDO-DAO implementation. I suppose that DataNucleus GAE port is doing somewhat inaccurate here as the persistency graph looks to be reversed :)

Google Datastore - Problems updating entity

I am dusting off my google app-engine / datastore skills ... and getting stuck on something very simple.
As per the example on the GAE documentation I am trying to update an entity as follows:
// persistence and business logic
PersistenceManager pm = PMF.get().getPersistenceManager();
// get it
NickName n = pm.getObjectById(NickName.class, nicknameId);
// update fields
n.givenName = "new name";
n.nickName = "new nickname";
n.timeStamp = new Date();
// close manager to persist changes
pm.close();
This doesn't work (as in the changes are not persisted, but no errors or anything else)!
At the same time I found that if I create a new entity with the same ID the changes get persisted:
// persistence and business logic
PersistenceManager pm = PMF.get().getPersistenceManager();
NickName n = new NickName("new name", "new nickname", new Date());
// set id
n.id = nicknameId;
pm.makePersistent(n);
pm.close();
I have the feeling I already solved this the 1st time I approached app engine and the data-store.
This is what my entity looks like:
#PersistenceCapable
public class NickName {
public NickName(String name, String nickname, Date timestamp) {
this.givenName = name;
this.nickName = nickname;
this.timeStamp = timestamp;
}
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
public String id;
#Persistent
public String givenName;
#Persistent
public String nickName;
#Persistent
public Date timeStamp;
}
Any help appreciated!
One issue may be that you are setting the fields directly instead of going through the setter methods. I'm fairly certain that JDO works by instrumenting the field setters so that they notify the persistence layer of any changes that occur. It has no way of directly monitoring changes to the backing field values themselves. So maybe try:
n.setGivenName("new name");
n.setNickName("new nickname");
n.setTimeStamp(new Date());
You're able to get away with setting the field directly when you create the object because the makePersistent() call tells the persistence manager that is needs to inspect the field values and save them. Though it's worth noting that setting field values directly like this is generally considered to be poor coding style.
Also, have you tried using the JPA interface instead of the JDO interface? In GAE they should be interchangeable:
EntityManager em = EMF.get();
NickName n = em.find(NickName.class, nicknameId);
n.givenName = "new name";
n.nickName = "new nickname";
n.timeStamp = new Date();
em.merge(n);
em.close();
This gives you an explicit merge() call which should work even with setting the field values directly.

Why does JDO think this detached object is clean?

I am trying to learn JDO (and at the same time its GAE and Spring intricacies) by creating a small web app, and am having trouble getting updated domain objects to persist back to the database. I initially grab the entity from the DB and detach it so that I can show it to the user and allow them to change it. Once the user has made the changes and posts the form back to the app, I again grab the entity from the DB (detached), update its properties, and then call a pm.makePersistent(). The abbreviated code is as follows:
User Domain Object:
#PersistenceCapable(detachable="true")
public class User extends BaseEntity {
#Persistent
private String firstName = "";
#Persistent
private String middleInitial = "";
#Persistent
private String lastName = "";
}
DAO Read Method:
public User read(Key key) throws DataException {
PersistenceManager pm = PMF.get().getPersistenceManager();
User pkg, detached = null;
try {
pkg = (User) pm.getObjectById(User.class, key);
detached = pm.detachCopy(pkg);
detached.setIsAlreadyInDB(true);
}
catch (Exception e) {
throw new DataException("An error occured trying to read the User object. Details:\n" + e.getMessage());
}
finally {
pm.close();
}
return detached;
}
DAO Update Method:
private void update(User pkg) throws DataException {
PersistenceManager pm = PMF.get().getPersistenceManager();
Transaction tx = pm.currentTransaction();
try {
tx.begin();
pm.makePersistent(pkg);
tx.commit();
}
finally {
if (tx.isActive()) tx.rollback();
pm.close();
}
}
Now when I get down into the update method, I've proven to myself that I'm working with just the same object from my read via inspecting its hashCode(), I've changed a value using the domain object's setter method, I've even printed the changed value to the console to make sure it's getting done, and JDOHelper.isDirty() still returns false, and therefore none of the changes get persisted back to the database.
Any thoughts on what I'm missing or if I'm approaching this from the wrong angle? Thank you for helping out a JDO beginner!
JDOHelper.isDirty is for managed objects. A detached object is not managed. DataNucleus provides a helper method of its own to get the dirty fields while detached since the logic is implementation-specific
String[] dirtyFieldNames = NucleusJDOHelper.getDetachedObjectDirtyFields(obj, pm);

JDO not fetching collection member field

Have a class:
class Node implements Serializable
{
private String name;
public String getName { return name; }
public void setName(String val){ name = val; }
public Node(){}
}
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
class NodeBag implements Serializable
{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Long id;
#Persistent(serialized="true")
private ArrayList<Node> nodes = new ArrayList<Node>();
public String getNodes { return nodes; }
public void setNodes(ArrayList<Node> val){ nodes = val; }
public NodeBag(){}
}
I can save it to the db with this
PersistenceManager pm = PMF.getManager();
try
{
pm.makePersistent(newBag);
}
finally
{
pm.close();
}
But when i load it back
PersistenceManager pm = PMF.getManager();
Query q = pm.newQuery(NodeBag.class);
try
{
List<NodeBag> pipelines = (List<NodeBag>)q.execute();
return nodeBags; // nodeBags[0].nodes is always empty
}
finally
{
q.closeAll();
}
Nodebag.nodes is always empty!
Did i miss something?
Thanks in advance.
Regards,
Paul
In your call to return the objects you can use the FetchPlan to specify what FetchGroup to return. See JDO docs for more information on the FetchGroup options.
You can ensure that all the entities are fetched, by specifying in your PersistenceManager the FetchGroup to use. The modified code is shown below:
PersistenceManager pm = PMF.getManager();
pm.getFetchPlan().setGroup(FetchGroup.ALL);
Query q = pm.newQuery(NodeBag.class);
try {
List<NodeBag> pipelines = (List<NodeBag>)q.execute();
return nodeBags; // nodeBags[0].nodes is always empty
} finally {
q.closeAll();
}
I had a heck of a time getting fetch groups to work. Both Query and PersistenceManager have a getFetchPlan(), but only the one on PersistenceManager seems to work.
Also, make sure you make your objects detachable and use pm.detachCopyAll() on the result.
Missed putting it in the fetch plan ? mark in default fetch group perhaps, or access the field, or put in a custom fetch plan, as per the DataNucleus docs and JDO spec.
Actually, i wanted also the return the answer across the wire by converting it to JSON.
And i've managed to load the child objects. The trick i use is detach. By detaching, everything will be loaded.
Thanks.
Use Collection insted of List and it should start working as long as it is inside a transaction

Loading a collection of Enums with Google app engine datastore

I am using the Goole app engine datastore with Java and trying to load an Object with a List of Enums. Every time I load the object the List is null. The object is
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class ObjectToSave {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
#Persistent
private List<AnEnum> anEnumList;
//public getters and setters
}
The enum is simple
public enum AnEnum {
VALUE_1,
VALUE_2;
}
The code to save it is
ObjectToSave objectToSave = new ObjectToSave();
List<AnEnum> anEnumList = new ArrayList<AnEnum>();
anEnumList.add(AnEnum.VALUE_1);
objectToSave.setAnEnumList(anEnumList);
PersistenceManager pm = pmfInstance.getPersistenceManager();
try {
pm.makePersistent(objectToSave);
} finally {
pm.close();
}
The code to load it is
PersistenceManager pm = pmfInstance.getPersistenceManager();
try {
Key key = KeyFactory.createKey(ObjectToSave.class.getSimpleName(), id);
ObjectToSave objectToSave = pm.getObjectById(ObjectToSave.class, key);
} finally {
pm.close();
}
I can view the data in the datastore using http://localhost:8080/_ah/admin and can see my List has been saved but it is not there when the object is loaded.
I created my project with the Eclipse plugin and haven't made any changes to the datastore settings as far as I know. So why id my Enum list null?
Yes but your List field is not in the default fetch group at loading so hence is not loaded.
Read JDO Fetch Groups. You could add it to the DFG, or enable a custom fetch group, or just "touch" the field before closing the PM.
--Andy (DataNucleus)
How are you creating an instance of ObjectToSave? The default value of all instance variable reference types is null, so unless you have (additional) code to create an instance of List<AnEnum> and assign it to anEnumList, null would be expected.

Categories