S2201
S2201

Reputation: 1349

How to completely customize the way that type information gets written to a document by Spring Data MongoDB?

Is it possible to use different type attribute (instead of _class) for each polymorphic collection like it's implemented in Doctrine(PHP) or Jackson libraries? Current solution allows to store type information in document field. By default it is a full class name stored in the field named _class.

We can easy change it to save some custom string (alias) instead of full class name and change default discriminator field name from _class to something else.

In my situation I'm working on legacy database while legacy application is still in use. Legacy application uses Doctrine (PHP) ODM as datalayer. Doctrine allows to define discriminator field name (_class in SpringData) by annotation and have it different for each collection.

In Spring Data when I pass typeKey to DefaultMongoTypeMapper it used for all collections.

Thanks.

// MyCustomMongoTypeMapper.java
// ...

@SuppressWarnings("unchecked")
@Override
public <T> TypeInformation<? extends T> readType(DBObject source, TypeInformation<T> basicType) {

    Assert.notNull(basicType);
    Class<?> documentsTargetType = null;

    Class<? super T> parent = basicType.getType();
    while (parent != null && parent != java.lang.Object.class) {
        final String discriminatorKey = getDiscriminatorKey(parent); //fetch key from annotation
        if (null == discriminatorKey) {
            parent = parent.getSuperclass();
        } else {
            accessor.setKey(discriminatorKey);
            return super.readType(source, basicType);
        }
    }
    accessor.resetKey();
    return super.readType(source, basicType);
}

Upvotes: 1

Views: 1094

Answers (1)

Oliver Drotbohm
Oliver Drotbohm

Reputation: 83051

Something that should work for you is completely exchanging the MongoTypeMapper instance that MappingMongoConverter uses. As you discovered the already available implementation assumes a common field name and takes yet another strategy to either write the fully-qualified class name or an alias or the like.

However, you should be able to just write your own and particularly focus on the following methods:

  • void writeType(TypeInformation<?> type, DBObject dbObject) — you basically get the type and have complete control over where and how to put that into the DBObject.
  • <T> TypeInformation<? extends T> readType(DBObject source, TypeInformation<T> defaultType); — you get the type declared on the reading side (i.e. usually the most common type of the hierarchy) and based on that have to lookup the type from the given source document. I guess that's exactly the inverse of what's to be implemented in the other method.

On a final note, I would strongly recommend against using different type field names for different collections as on the reading side you might run into places where just Object is declared on the property and you basically don't get no clue where to even look for in the document.

Upvotes: 3

Related Questions