Objectify and Extensible Enums With Interfaces

65 views
Skip to first unread message

Yannos Vastakis

unread,
Jan 6, 2022, 1:55:30 PM1/6/22
to objectify-appengine
Hello,

I have the following classes : 

public interface Foo {
        String getCategory();
}

public enum MyEnum implements  Foo {
A,B,C;

MyEnum () {}

@Override
public  String getCategory() {
return "MyEnum";
}

@Entity
public class MyEntity implements Serializable {

@Id private Long id;

  @Index private Foo foo;

// No Arg Constructor 
// All Arg Constructor 
.....
// Setter
// Getter
}

When trying to call save on entity MyEntity I get 

com.googlecode.objectify.SaveException: Error saving com.server.foobar.entity. MyEntity @10fb29ac: java.lang.IllegalStateException: Class 'class com.blabla.server.model. MyEnum   ' is not a registered @Subclass

Is there any way to deal with this issue ? I just want to have different type of Enums (that implement the interface) persisted as a member of my MyEntity. I did not have any problem with simple enums but now i need to have something more complex

Veronika Priesner

unread,
Jan 7, 2022, 3:08:25 AM1/7/22
to objectify-appengine
Hi,

You're probably just missing the @Subclass annotation as well as the call to register the class (ObjectifyService.register(MyEnum.class);).

Yannos Vastakis

unread,
Jan 7, 2022, 9:54:03 AM1/7/22
to objectify-appengine
So the  (ObjectifyService.register(MyEnum.class); is only adding a translator (I tried to debug the execution).

From what I can tell I need @Entity in the interface class but after that I get the following NPE - which is telling me that there is no Key Field in the interface - but how could I have such a key in an interface ...

Caused by: java.lang.NullPointerException: null
        at com.googlecode.objectify.impl.KeyMetadata.findKeyFields(KeyMetadata.java:76)
        at com.googlecode.objectify.impl.KeyMetadata.findKeyFields(KeyMetadata.java:76)
        at com.googlecode.objectify.impl.KeyMetadata.<init>(KeyMetadata.java:59)
        at com.googlecode.objectify.impl.KeyPopulator.<init>(KeyPopulator.java:19)

Any ideas how to bypass it ? 

Thank you in advance,

Yannos Vastakis

unread,
Jan 7, 2022, 9:54:03 AM1/7/22
to objectify-appengine
Hi why should an enum be annotated with such an Annotation ? From reading the documentation I though this one was only used for actual classes.

I will try it and report back. Will this annotation create a table / entity in datastore for each Enumeration ?
Στις Παρασκευή 7 Ιανουαρίου 2022 στις 10:08:25 π.μ. UTC+2, ο χρήστης Veronika Priesner έγραψε:

Yannos Vastakis

unread,
Jan 7, 2022, 9:54:04 AM1/7/22
to objectify-appengine
I followed your advice but I am still getting the same error message.

I am pretty sure I register the class correctly (I paste part of my logs and the code that registers classes is the following one).

ObjectifyService.register(clazz);
log.info("Registering Objectify  @Entity or @Subclas class: {}", clazz.getName());

2022-01-07 12:07:24.487  INFO [,,,] 14376 --- [  restartedMain] com.foobar.server.config.ObjectifyConfig    : Registering Objectify @Entity or @Subclass class: com.foobar.server.model.MyEnum 

I tried to even call it like  ObjectifyService.register(MyEnum.class); in order to be 100% sure that there is ain't a problem with the way I scan for classes with @Entity or @Subclass but still no luck.

Am I missing anything else ? Is there a chance the problem is related to the fact that I have an interface ? The documentation you posted (I have read this chapter) shows an example with "regular" classes and not with interfaces.

Thank you for your time in advance!!!

Στις Παρασκευή 7 Ιανουαρίου 2022 στις 10:08:25 π.μ. UTC+2, ο χρήστης Veronika Priesner έγραψε:

Jeff Schnitzer

unread,
Jan 7, 2022, 12:16:40 PM1/7/22
to objectify-appengine
This is an odd request.

Why do you want `MyEntity.foo` declared as an interface rather than as its enum type? Objectify persists concrete things, not abstract interfaces. The implementation of the Foo interface could be anything, including objects which have no material representation (say, `getCategory()` returns a computed value) or  objects that contain an elaborate object graph.

If you know that `foo` is always going to be the enum, declare it in `MyEntity` as `private MyEnum foo;`. You can still have a public `Foo getFoo()` getter method to hide this fact from the outside world.

If you really do have wildly varying implementations of `MyEntity.foo`, then there is only one possible option: Use the @Serialize annotation and use java serialization to save the object graph. That's the only way you can save an arbitrary unknown object graph (which is what you have by declaring an interface - the implementation could be anything). However, this comes with a lot of disadvantages, the main one being that you're saving nonportable binary data into the datastore. It should be a last resort.

TL;DR: You need to tell persistence frameworks what concrete types you are using.

Jeff

--
You received this message because you are subscribed to the Google Groups "objectify-appengine" group.
To unsubscribe from this group and stop receiving emails from it, send an email to objectify-appen...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/objectify-appengine/6fc32077-bc90-47d1-8df0-fc1f53404866n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages