Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document that DTOs don't work with native queries [DATAJPA-1714] #2009

Closed
spring-projects-issues opened this issue Apr 16, 2020 · 6 comments
Closed
Assignees
Labels
type: documentation A documentation update

Comments

@spring-projects-issues
Copy link

Jens Schauder opened DATAJPA-1714 and commented

Trying to return a DTO from a native query results in an exception like the following:

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [org.springframework.data.jpa.repository.sample.NameOnlyDto]

	at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:321)
	at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:194)
	at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:174)
	at org.springframework.data.repository.query.ResultProcessor$ProjectingConverter.convert(ResultProcessor.java:297)
	at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.lambda$and$0(ResultProcessor.java:217)
	at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.convert(ResultProcessor.java:228)
	at org.springframework.data.repository.query.ResultProcessor.processResult(ResultProcessor.java:156)
	at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:157)
	at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:142)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor$QueryMethodInvoker.invoke(QueryExecutorMethodInterceptor.java:195)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:152)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:130)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:149)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
	at com.sun.proxy.$Proxy128.findDtoByNativeQuery(Unknown Source)
	at org.springframework.data.jpa.repository.UserRepositoryFinderTests.returnsDtoForNativeQuery(UserRepositoryFinderTests.java:287)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
	at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
	at org.junit.vintage.engine.execution.RunnerExecutor.execute(RunnerExecutor.java:43)
	at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
	at java.util.Iterator.forEachRemaining(Iterator.java:116)
	at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
	at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:485)
	at org.junit.vintage.engine.VintageTestEngine.executeAllChildren(VintageTestEngine.java:82)
	at org.junit.vintage.engine.VintageTestEngine.execute(VintageTestEngine.java:73)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
	at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
	at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

Reference URL: https://stackoverflow.com/a/61255102/66686

Referenced from: pull request #419

1 votes, 2 watchers

@spring-projects-issues
Copy link
Author

Jens Schauder commented

The referenced PR contains a reproducing test

@amhokies
Copy link

I'm having this issue even when not using a native query:

Repository:

@Query("SELECT cso.myField AS myField FROM MyEntity cso")
    List<MyEntityDTO> testQuery();

DTO:

@Value
public class MyEntityDTO {
    private long myField;
}

Error:

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.company.package.MyEntityDTO]

It, however, works just fine if I reference the constructor in the query:

@Query("SELECT new com.company.package.MyEntityDTO(cso.myField) FROM MyEntity cso")
    List<MyEntityDTO> testQuery();

@pwrsoft
Copy link

pwrsoft commented Mar 3, 2021

@amhokies by the way - thank you for the workaround I searched for.

But still waiting to be fixed!

@s3curitybug
Copy link

Any update on this? Thank you.

@schauder schauder added the status: waiting-for-triage An issue we've not yet triaged label Jun 7, 2021
@schauder
Copy link
Contributor

For JPQL queries there is the constructor clause as described by @amhokies.
For native (i.e. SQL) queries something similar can be done using SqlResultSetMapping and named queries.

The question is, should Spring Data JPA offer something beyond that?
I see the following options so far:

  • Construct DTOs similar as other Spring Data modules do it. This would mean we need to consider DTOs entities, although JPA doesn't. And constructing our own mapping layer around them. The semantics would be different and it would be a lot of work to build or maintain.
  • Construct SqlResultSetMapping programmatically to avoid the annotations. This should be possible with Hibernates ResultTransformer but that is Hibernate specific and not part of JPA.

@schauder
Copy link
Contributor

The team decided that we will not implement this, since the drawbacks of both possible implementations are to big.

You therefore have the following workarounds available:

  • Use JPQL and a constructor expression.
  • Use a named query and a SqlResultSetMapping.
  • And the catch all: use a custom method implementation, possibly with a Hibernate ResultTransformer.

I'll use this ticket to correct the documentation.

schauder added a commit to spring-projects/spring-data-commons that referenced this issue Jun 16, 2021
mp911de added a commit that referenced this issue Jun 17, 2021
Update placeholder name to reflect its position instead of the content that is being added by using modules.

Original pull request: #2237.
See #2009
mp911de added a commit that referenced this issue Jun 17, 2021
Update placeholder name to reflect its position instead of the content that is being added by using modules.

Original pull request: #2237.
See #2009
mp911de added a commit that referenced this issue Jun 17, 2021
Update placeholder name to reflect its position instead of the content that is being added by using modules.

Original pull request: #2237.
See #2009
mp911de pushed a commit to spring-projects/spring-data-commons that referenced this issue Jun 17, 2021
mp911de pushed a commit to spring-projects/spring-data-commons that referenced this issue Jun 17, 2021
mp911de pushed a commit to spring-projects/spring-data-commons that referenced this issue Jun 17, 2021
@mp911de mp911de added status: declined A suggestion or change that we don't feel we should currently apply type: documentation A documentation update and removed status: waiting-for-triage An issue we've not yet triaged type: bug A general bug status: declined A suggestion or change that we don't feel we should currently apply labels Jun 17, 2021
@mp911de mp911de changed the title DTOs don't work with native queries [DATAJPA-1714] Document that DTOs don't work with native queries [DATAJPA-1714] Jun 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment