I have a problem that I can't be able to solve. I've tried to search on the web for solutions, but I didn't find any generic solution. I want to update an object, whatever it's class may be, in the datastore. For that, here's the code I'm using for the project
I'm using DataNucleus and Google AppEngine.
Here's my jdoconfig.xml
<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">
<persistence-manager-factory name="transactions-optional">
<property name="javax.jdo.PersistenceManagerFactoryClass"
value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
<property name="javax.jdo.option.ConnectionURL" value="appengine"/>
<property name="javax.jdo.option.NontransactionalRead" value="true"/>
<property name="javax.jdo.option.NontransactionalWrite" value="true"/>
<property name="javax.jdo.option.RetainValues" value="true"/>
<property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
</persistence-manager-factory>
</jdoconfig>
My PMF class
public final class PMF {
private static final PersistenceManagerFactory pmfInstance =
JDOHelper.getPersistenceManagerFactory("transactions-optional");
private PMF() {}
public static PersistenceManagerFactory get() {
return pmfInstance;
}
public static PersistenceManager getPersistenceManager() {
return pmfInstance.getPersistenceManager();
}
}
My BaseModel class, wich is a superclass for all the models of the project
@Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
@PersistenceCapable(detachable = "true", identityType = IdentityType.APPLICATION)
public abstract class BaseModel implements Serializable {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
protected Key id;
public boolean equals(Object obj) {
try {
return obj instanceof BaseModel ? this.getId().getId() == ((BaseModel) obj).getId().getId() : false;
} catch (Exception e) {
return false;
}
}
}
Here's the class (Project
) I want to save
@PersistenceCapable(detachable = "true", identityType = IdentityType.APPLICATION)
public class Project extends BaseModel implements BeanModelTag {
private static final long serialVersionUID = 3318013676594981107L;
@Persistent
private String name;
@Persistent
private String description;
@Persistent
private Date deadline;
@Persistent
private Integer status;
@Persistent
private Key manager;
@Persistent
private Set<Key> team;
}
For that, I tried several things. This method below saves a NEW instance of Project with success, but when I call to for updating an already detached object, only the field deadline is updated, the other attributes aren't updated (and yet, I went in debug mode to check out if the other attributes where changed, and yes, they were, but only deadline get's saved).
public void save(BaseModel object) {
PersistenceManager pm = PMF.getPersistenceManager();
try {
pm.makePersistent(object);
} finally {
pm.close();
}
}
So, I tried the following code
public void update(Project object) {
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
pm.currentTransaction().begin();
Project p = pm.getObjectById(Project.class, object.getId());
p.setName(object.getName());
p.setDeadline(object.getDeadline());
p.setDescription(object.getDescription());
p.setTeam(p.getTeam());
p.setStatus(object.getStatus());
pm.currentTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
pm.currentTransaction().rollback();
} finally {
pm.close();
}
}
And it worked. Ok, it works in this way, but I need a generic method for all my models, so I tried this
public void update(BaseModel object) {
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
pm.currentTransaction().begin();
BaseModel ob = pm.getObjectById(object.getClass(), object.getId());
for (Field f : ob.getClass().getDeclaredFields()) {
if (!f.toString().contains("final")) {
f.setAccessible(true);
for (Field g : object.getClass().getDeclaredFields()) {
g.setAccessible(true);
if (f.getName().equals(g.getName())) {
f.set(ob, g.get(object));
}
g.setAccessible(false);
}
}
f.setAccessible(false);
}
pm.makePersistent(ob);
pm.currentTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
pm.currentTransaction().rollback();
} finally {
pm.close();
}
}
But it doesn't work at all, nothing gets saved, and yet when I System.out the attributes manually, they are changed. I tried with and without pm.makePersistent(ob);
with no luck. I don't know what to do. I have 120 models in this project that inherits from BaseModel
and I can't find a way to make an update that works with my model.
----------- Edit -----------
Thanks for the answer. Here's my solution for now. Of course, that printStrackTree will get out of there.
public void update(BaseModel object) {
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
pm.currentTransaction().begin();
BaseModel ob = pm.getObjectById(object.getClass(), object.getId());
for (Field f : ob.getClass().getDeclaredFields()) {
if (!Pattern.compile("\\bfinal\\b").matcher(f.toString()).find()) {
f.setAccessible(true);
for (Field g : object.getClass().getDeclaredFields()) {
g.setAccessible(true);
if (f.getName().equals(g.getName())) {
f.set(ob, g.get(object));
JDOHelper.makeDirty(ob, f.getName());
}
g.setAccessible(false);
}
f.setAccessible(false);
}
}
pm.makePersistent(object);
pm.currentTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
pm.currentTransaction().rollback();
} finally {
pm.close();
}
}