I'm currently migrating project's DAO classes from JDO implementation to Objectify V6. The requirement I have, is to make sure that in a case of rollback it will be possible to load entities, which were saved by Objectify, with the old version of DAO.
In old code strings are stored as Text
. And if I leave Text
field in entity definition, Objectify puts it as a String
to datastore (because there is no Text
type any more).
Currently new DAO implementation is not backward compatible because of a ClassCastException
which arise when JDO implementation casts String to Text type.
Is there a way to store Text type to datastore with Objectify V6?
I tried to use String
instead of Text
in entity definition and create a TranslatorFactory
to make the conversion, but I wasn't able to find correct datastore Value
implementation type.
public class StringTextTranslatorFactory implements TranslatorFactory<String, Text> {
@Override
public Translator<String, Text> create(TypeKey<String> tk, CreateContext ctx, Path path) {
return new Translator<String, Text>() {
@Override
public String load(Value<Text> node, LoadContext ctx, Path path) throws SkipException {
Text text = node.get();
return text != null ? text.getValue() : "";
}
@Override
public Value<Text> save(String pojo, boolean index, SaveContext ctx, Path path)
throws SkipException {
return ???;
}
};
}
}
Update
The project is using an implementation of JDO 2.3 for the App Engine Datastore. The implementation is based on version 1.0 of the DataNucleus Access Platform.
Data entity defined as the following:
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class CrmNote {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
@Persistent
private Text note;
}
Stacktrace:
java.lang.ClassCastException: java.lang.String cannot be cast to com.google.appengine.api.datastore.Text
at com.timzon.snapabug.server.data.CrmNote.jdoReplaceField(CrmNote.java)
at com.timzon.snapabug.server.data.CrmNote.jdoReplaceFields(CrmNote.java)
at org.datanucleus.state.JDOStateManagerImpl.replaceFields(JDOStateManagerImpl.java:2772)
at org.datanucleus.state.JDOStateManagerImpl.replaceFields(JDOStateManagerImpl.java:2791)
at org.datanucleus.store.appengine.DatastorePersistenceHandler.fetchObject(DatastorePersistenceHandler.java:519)
at org.datanucleus.store.appengine.query.DatastoreQuery.entityToPojo(DatastoreQuery.java:649)
at org.datanucleus.store.appengine.query.DatastoreQuery.entityToPojo(DatastoreQuery.java:603)
at org.datanucleus.store.appengine.query.DatastoreQuery.access$300(DatastoreQuery.java:119)
at org.datanucleus.store.appengine.query.DatastoreQuery$6.apply(DatastoreQuery.java:783)
at org.datanucleus.store.appengine.query.DatastoreQuery$6.apply(DatastoreQuery.java:774)
at org.datanucleus.store.appengine.query.LazyResult.resolveNext(LazyResult.java:94)
at org.datanucleus.store.appengine.query.LazyResult.resolveAll(LazyResult.java:116)
at org.datanucleus.store.appengine.query.LazyResult.size(LazyResult.java:110)
at org.datanucleus.store.appengine.query.StreamingQueryResult.size(StreamingQueryResult.java:130)
at org.datanucleus.store.query.AbstractQueryResult.toArray(AbstractQueryResult.java:399)
at java.util.ArrayList.<init>(ArrayList.java:178)
at com.timzon.snapabug.server.dao.CrmNoteDAO.getOrderedCrmNotes(CrmNoteDAO.java:27)
Exception happens in auto-generated jdoReplaceField
method which is added by JDO post-compilation "enhancement". I decompiled enhanced class and I see that datastore object is casted to Text
type directly:
public void jdoReplaceField(int index) {
if (this.jdoStateManager == null) {
throw new IllegalStateException("state manager is null");
} else {
switch(index) {
case 0:
this.id = (Long)this.jdoStateManager.replacingObjectField(this, index);
break;
case 1:
this.note = (Text)this.jdoStateManager.replacingObjectField(this, index);
break;
default:
throw new IllegalArgumentException("out of field index :" + index);
}
}
}
So, if note
field is saved in data store as a String
, then in case of rollback a ClassCastException
will be thrown.
There's no way to explicitly store a Text
type with the Google-provided SDK that Objectify 6 uses; there is only StringValue
. Text
is not even in the jar.
However, I don't think this should matter. Ultimately both SDKs (the old appengine one and the new one) are just converting back and forth to protobuf structures. They are supposed to be compatible.
It's especially strange because the old low level API wrote strings into the Entity
structure; Text
was required only if the strings exceeded a certain length. So JDO should handle String
. Do you have some sort of special annotation on your String field to force it to expect Text
? What does that stacktrace look like?