0
votes

Abstract

I have a working application in Appengine using Java and JDO 3. I found these arguments (auto_now and auto_now_add) which correspond exactly what I want to implement in Java. So essentially the question is: How to convert AppEngine's Python DateTimeProperty to Java JDO?

Constraints

  1. Converting my application to Python is not an option.
  2. Adding two Date properties and manually populating these values whenever a create/update happens is not an option.
  3. I'm looking for a solution which corresponds to what JDO/Appengine/Database authors had in mind for this scenario when they created the APIs.
  4. It would be preferable to have a generic option: say I have 4 entities in classes: C1, C2, C3, C4 and the solution is to add a base class C0, which all 4 entities would extend, so the 4 entities don't even know they're being "audited".

[update] I tried (using a simple entity)

@PersistenceCapable public class MyEntity {
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY, primaryKey = "true")
    private Long id;
    @Persistent private String name;
    ...

1. @Persistent public void getLastUpdate() { return new Date(); }

As suggested by answer, but it seems to always update the value, even when I just load the value from the datastore or just modify an unrelated field (e.g. String name).

You can easily enough have a property (setter/getter) on a java class and have the property persistable (rather than the field). Within that getter you can code whatever you want to control what value goes into the datastore.

If I didn't do the following hack, I can't read the value stored in the datastore [neither with the hack :( ]:

@Persistent public Date getLastUpdate() { return new Date(); }
private Date prevUpdate;
public void setLastUpdate(Date lastUpdate) { this.prevUpdate = lastUpdate; }
public Date getPrevUpdate() { return prevUpdate; }

Is there any way to differentiate if a persistence operation is in progress or my code is calling the getter?

2. @Persistent(customValueStrategy = "auto_now_add") private Date lastUpdate;

I modeled auto_now_add after org.datanucleus.store.valuegenerator.TimestampGenerator replacing Timestamp with java.util.Date. But it was only populated once at the first makePersistent call, regardless of how many times I modified other fields and called makePersistent. Also note that it doesn't seem to behave as the documentation says (or my English is rusty):

Please note that by defining a value-strategy for a field then it will, by default, always generate a value for that field on persist. If the field can store nulls and you only want it to generate the value at persist when it is null (i.e you haven't assigned a value yourself) then you can add the extension "strategy-when-notnull" as false

3. preStore using PersistenceManager.addInstanceLifecycleListener

Works as expected, but I could make it work across multiple entities using a base class.

pm.addInstanceLifecycleListener(new StoreLifecycleListener() {
    @Override public void preStore(InstanceLifecycleEvent event) {
        MyEntity entity = (MyEntity)event.getPersistentInstance();
        entity.setLastUpdate(new Date());
    }
    @Override public void postStore(InstanceLifecycleEvent event) {}
}, MyEntity.class);

4. implements StoreCallback and public void jdoPreStore() { this.setLastUpdate(new Date()); }

Works as expected, but I could make it work across multiple entities using a base class.

To satisfy my 4th constraint (using solutions 3 or 4)

Whatever I do I can't make the following structure work:

public abstract class Dateable implements StoreCallback {
    @Persistent private Date created;
    @Persistent private Date lastUpdate;
    public Dateable() { created = new Date(); }
    public void jdoPreStore() { this.setLastUpdate(new Date()); }
    // ... normal get/set properties for the above two
}
@PersistenceCapable public class MyEntity extends Dateable {
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY, primaryKey = "true") private Long id;
    @Persistent private String name;

The problems when the enhancer runs:

  • public abstract class Dateable:

    DataNucleus.MetaData Registering class "[...].Dateable" as not having MetaData.

  • public abstract class Dateable with the above log, but running the code anyway:
    Creation date changes whenever I create or read the data from datastore.

  • @PersistenceCapable public abstract class Dateable:

    DataNucleus.MetaData Class "[...].MyEntity" has been specified with 1 primary key fields, but this class is using datastore identity and should be application identity.

1

1 Answers

0
votes

JDO simply provides persistence of Java classes (and its fields/properties) so don't see what the design of JDO has to do with it.

You can easily enough have a property (setter/getter) on a java class and have the property persistable (rather than the field). Within that getter you can code whatever you want to control what value goes into the datastore. Either that or you use a preStore listener to be able to set things just before persistence so the desired value goes into the datastore.