Description
Description of the issue
If an entity contains a field of some type which is defined in another package, a mapping exception will be thrown during a read operation if said object doesn't provide an empty constructor (private, protected or public). Retrofitting all our classes with an empty constructor can't be a feasible solution.
This exception happens since Spring Boot 3.2.0 and is not present in version 3.1.5, which suggest a possible regression.
Example
All code snippets are taken from the example project to reproduce this issue, which can be found here:
https://gitlab.com/elderbyte/public/examples/spring-mongo-mapping-param-noname-issue
Project contains two packages:
com.mappingissue.app
- Spring Boot Applicationcom.mappingissue.other
- Package that contains some objects
Class Cat
and Collar
are defined in com.mappingissue.app
package.
Class CollarLib
is defined in com.mappingissue.other
packege.
- app
-- Cat
-- Collar - other
-- CollarLib
Class Cat
is used to persist and read data from the DB (Cat document).
Class Cat
contains a field of type Collar
and a field of type CollarLib
.
Both Collar
and CollarLib
have the same fields, String id
and String company
Cat.class com.mappingissue.app
@Document
public class Cat {
@Id
private String id;
private String name;
private Collar collar;
private CollarLib collarLib;
...
}
Collar.class com.mappingissue.app
public class Collar {
private String id;
private String company;
...
}
CollarLib.class com.mappingissue.other
public class CollarLib {
private String id;
private String company;
...
}
When a Cat with a existing (non null) CollarLib is read, the MappingException is thrown.
Hence, for following tests, success()
will pass but fail_MappingException_ParameterDoesNotHaveAName
will fail with exception (Stack traced can be found at the end of the issue):
@Test
public void success() {
mongoOperations.save(
new Cat(
"1",
"Sir Meow",
new Collar("John Doe", "123 Fake Street"),
null
)
);
var cat = mongoOperations.find(
Query.query(
Criteria.where("id").is("1")
),
Cat.class
).get(0);
Assertions.assertEquals("1", cat.getId());
}
@Test
public void fail_MappingException_ParameterDoesNotHaveAName() {
mongoOperations.save(
new Cat(
"2",
"Lady Purr",
new Collar("1", "Collar Inc"),
new CollarLib("1", "Collar Inc")
)
);
var cat = mongoOperations.find(
Query.query(
Criteria.where("id").is("2")
),
Cat.class
).get(0);
Assertions.assertEquals("2", cat.getId());
}
The issue is resolved if CollarLib
in the com.mappingissue.other
provides an empty constructor
Used versions
Spring Boot: 3.2.1
Java: openjdk version "17.0.5" 2022-10-18 LTS
MongoDB: 6.0.3
Example project for reproduction
https://gitlab.com/elderbyte/public/examples/spring-mongo-mapping-param-noname-issue
Stacktrace from example project
org.springframework.data.mapping.MappingException: Parameter org.springframework.data.mapping.Parameter@c84af0a8 does not have a name
at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:61 undefined)
at org.springframework.data.mapping.model.SpELExpressionParameterValueProvider.getParameterValue(SpELExpressionParameterValueProvider.java:49 undefined)
at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.extractInvocationArguments(ClassGeneratingEntityInstantiator.java:301 undefined)
at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator$EntityInstantiatorAdapter.createInstance(ClassGeneratingEntityInstantiator.java:273 undefined)
at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:98 undefined)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:519 undefined)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readDocument(MappingMongoConverter.java:487 undefined)
at org.springframework.data.mongodb.core.MappingMongoConverter$DefaultConversionContext.convert.convert(MappingMongoConverter.java:2366 undefined)
at org.springframework.data.mongodb.core.MappingMongoConverter$ConversionContext.convert.convert(MappingMongoConverter.java:2175 undefined)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1941 undefined)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$AssociationAwareMongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:2000 undefined)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$AssociationAwareMongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1959 undefined)
at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:71 undefined)
at org.springframework.data.mapping.model.SpELExpressionParameterValueProvider.getParameterValue(SpELExpressionParameterValueProvider.java:49 undefined)
at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.extractInvocationArguments(ClassGeneratingEntityInstantiator.java:301 undefined)
at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator$EntityInstantiatorAdapter.createInstance(ClassGeneratingEntityInstantiator.java:273 undefined)
at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:98 undefined)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:519 undefined)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readDocument(MappingMongoConverter.java:487 undefined)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:423 undefined)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:419 undefined)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:119 undefined)
at org.springframework.data.mongodb.core.MongoTemplate$ReadDocumentCallback.doWith(MongoTemplate.java:3278 undefined)
at org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:2912 undefined)
at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:2592 undefined)
at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:2573 undefined)
at org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:866 undefined)
at org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:856 undefined)
at com.elderbyte.mappingissue.app.ApplicationTest.fail_MappingException_ParameterDoesNotHaveAName(ApplicationTest.java:58 undefined)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77 undefined)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43 undefined)
at java.base/java.lang.reflect.Method.invoke(Method.java:568 undefined)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:728 undefined)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60 undefined)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131 undefined)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156 undefined)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147 undefined)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86 undefined)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103 undefined)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93 undefined)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106 undefined)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64 undefined)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45 undefined)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37 undefined)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92 undefined)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86 undefined)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:218 undefined)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73 undefined)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:214 undefined)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:139 undefined)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151 undefined)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141 undefined)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139 undefined)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95 undefined)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511 undefined)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155 undefined)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141 undefined)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139 undefined)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95 undefined)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511 undefined)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155 undefined)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141 undefined)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139 undefined)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138 undefined)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95 undefined)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35 undefined)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57 undefined)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54 undefined)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107 undefined)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88 undefined)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54 undefined)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67 undefined)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52 undefined)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114 undefined)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86 undefined)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86 undefined)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:119 undefined)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:94 undefined)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:89 undefined)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:62 undefined)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77 undefined)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43 undefined)
at java.base/java.lang.reflect.Method.invoke(Method.java:568 undefined)
at org.gradle.internal.ReflectionDispatch.dispatch.dispatch(ReflectionDispatch.java:36 undefined)
at org.gradle.internal.ReflectionDispatch.dispatch.dispatch(ReflectionDispatch.java:24 undefined)
at org.gradle.internal.ContextClassLoaderDispatch.dispatch.dispatch(ContextClassLoaderDispatch.java:33 undefined)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94 undefined)
at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193 undefined)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129 undefined)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100 undefined)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60 undefined)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56 undefined)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113 undefined)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65 undefined)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69 undefined)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74 undefined)