Skip to content

Way to define fallback class when _class not found #3250

Closed as not planned
Closed as not planned
@evilmilo

Description

@evilmilo

For a while we've had a class used in a lot of our mongo collections, FieldChange, which just recorded when a field changed. This is used in collections e.g.

class FieldChange {
   String field;
   String oldValue;
   String newValue;
}

class ExampleClass {
   List<FieldChange> changes = new ArrayList<>();
}

We can see in mongo that _class has not been written e.g. for ExampleClass in mongo:

{
    "changes" : [
        {
            "field" : "name",
            "oldValue": "Bob",
            "newValue": "Robert"
        }
    ]
}

We now are trying to enrich our model, so FieldChange becomes an abstract class, and we have many variations, but what was FieldChange is now SimpleFieldChange.

abstract class FieldChange {
   String field;
}

class SimpleFieldChange extends FieldChange {
   String field;
   String oldValue;
   String newValue;
}

But now we cannot load the existing data from mongo

org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate example.FieldUpdateEvent using constructor public example.FieldUpdateEvent() with arguments 

We've been investigating specifying details via annotations we assumed would work, e.g.

@JsonTypeInfo(
    use = JsonTypeInfo.Id.CLASS,
    include=JsonTypeInfo.As.PROPERTY,
    property = "_class",
    defaultImpl = SimpleFieldChange.class
)
abstract class FieldChange {
   String field;
}

And a few variations on the fields as well as classes, but nothing seemed to work. We finally debugged, and can see that Mongo loads up items in the collection using DefaultMongoTypeMapper, which extends DefaultTypeMapper.

can see this has method that starts:

public <T> TypeInformation<? extends T> readType(S source, TypeInformation<T> basicType) {
	Assert.notNull(source, "Source must not be null");
	Assert.notNull(basicType, "Basic type must not be null");
	Class<?> documentsTargetType = getDefaultedTypeToBeUsed(source);
	if (documentsTargetType == null) {
		return basicType;
	}
	Class<T> rawType = basicType.getType();
        ...

effectively what would be nice is if getDefaultedTypeToBeUsed(source) had some way to then lookup by basicType if nothing in source was found for the mapping. No simple way to hook into this though, since getDefaultedTypeToBeUsed only takes source.

Would be nice if annotations on the basicType could be inspected for default implementation, this would seem to fit in with expectations.

Otherwise if their was a pluggable way to define default mappings, would seem could be put into above block, e.g.

public <T> TypeInformation<? extends T> readType(S source, TypeInformation<T> basicType) {
	Assert.notNull(source, "Source must not be null");
	Assert.notNull(basicType, "Basic type must not be null");
	Class<?> documentsTargetType = getDefaultedTypeToBeUsed(source);

	final Class<T> rawType = basicType.getType();		

	if (documentsTargetType == null) {
		// have some fallback type map that means we could lookup FieldUpdateEvent and tell it to act like it found ValueFieldUpdate
		documentsTargetType = customFallback.lookup(rawType.getName());
		if (documentsTargetType == null) {
			return basicType;
		}
	}
        ...

We're so unsure how to do this, so we're now looking to run a migration on all our data to add _class information, but feels like their should be a simpler way to configure this in code.

Metadata

Metadata

Assignees

Labels

for: stackoverflowA question that's better suited to stackoverflow.com

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions