JPA,Hibernate:在现有工作 DTO 中添加新变量时“无法在类上找到适当的构造函数”

Posted

技术标签:

【中文标题】JPA,Hibernate:在现有工作 DTO 中添加新变量时“无法在类上找到适当的构造函数”【英文标题】:JPA, Hibernate: "Unable to locate appropriate constructor on class" on adding new variables in an existing working DTO 【发布时间】:2020-12-26 22:24:08 【问题描述】:

我的 DTO -

data class PodcastDTO(val id: Long, val author: String, val title:String, val description: String,
                      val categoryId: Long, val subCategoryId: Long,
                      val categoryName: String, val subCategoryName: String?): Serializable

我的查询 -

@Query("SELECT new com.krtkush.sample.modules.podcast.models.PodcastDTO" +
            "(p.id, p.author, p.title, p.description, p.categoryId, p.subCategoryId, c.name, c2.name) " +
            "FROM Podcast p " +
            "LEFT JOIN Category c ON p.categoryId = c.id " +
            "LEFT JOIN Category c2 ON p.subCategoryId = c2.id " +
            "WHERE p.owner = :ownerId")
    fun getPodcastsByOwner(@Param("ownerId")ownerId: Long): List<PodcastDTO>

当我尝试构建我的应用程序时,我收到以下错误 -

Unable to locate appropriate constructor on class [com.krtkush.audiotime.modules.podcast.models.PodcastDTO]. Expected arguments are: long, java.lang.String, java.lang.String, java.lang.String, java.lang.Long, long, java.lang.String, java.lang.String [SELECT new com.krtkush.audiotime.modules.podcast.models.PodcastDTO(p.id, p.author, p.title, p.description, p.categoryId, p.subCategoryId, c.name, c2.name) FROM com.krtkush.audiotime.modules.podcast.models.Podcast p LEFT JOIN com.krtkush.audiotime.modules.category.models.Category c ON p.categoryId = c.id LEFT JOIN com.krtkush.audiotime.modules.category.models.Category c2 ON p.subCategoryId = c2.id WHERE p.owner = :ownerId]

完整的错误是-

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'podcastRepository': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract java.util.List com.krtkush.audiotime.modules.podcast.repositories.PodcastRepository.getPodcastsByOwner(long)!
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:178) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:101) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1821) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getObjectForBeanInstance(AbstractAutowireCapableBeanFactory.java:1266) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:260) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:621) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:609) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.data.repository.config.DeferredRepositoryInitializationListener.onApplicationEvent(DeferredRepositoryInitializationListener.java:51) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.repository.config.DeferredRepositoryInitializationListener.onApplicationEvent(DeferredRepositoryInitializationListener.java:36) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:404) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:361) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:898) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:554) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at com.krtkush.audiotime.AudioTimeApplicationKt.main(AudioTimeApplication.kt:13) ~[main/:na]
Caused by: java.lang.IllegalArgumentException: Validation failed for query for method public abstract java.util.List com.krtkush.audiotime.modules.podcast.repositories.PodcastRepository.getPodcastsByOwner(long)!
    at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:93) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.jpa.repository.query.SimpleJpaQuery.<init>(SimpleJpaQuery.java:63) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.jpa.repository.query.JpaQueryFactory.fromMethodWithQueryString(JpaQueryFactory.java:76) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.jpa.repository.query.JpaQueryFactory.fromQueryAnnotation(JpaQueryFactory.java:56) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$DeclaredQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:146) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:216) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:81) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lookupQuery(QueryExecutorMethodInterceptor.java:99) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lambda$mapMethodsToQuery$1(QueryExecutorMethodInterceptor.java:92) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) ~[na:na]
    at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133) ~[na:na]
    at java.base/java.util.Collections$UnmodifiableCollection$1.forEachRemaining(Collections.java:1052) ~[na:na]
    at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.mapMethodsToQuery(QueryExecutorMethodInterceptor.java:94) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lambda$new$0(QueryExecutorMethodInterceptor.java:84) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at java.base/java.util.Optional.map(Optional.java:265) ~[na:na]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.<init>(QueryExecutorMethodInterceptor.java:84) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:331) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:297) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.util.Lazy.getNullable(Lazy.java:212) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.util.Lazy.get(Lazy.java:94) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:244) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:57) ~[spring-data-commons-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:171) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    ... 24 common frames omitted
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate appropriate constructor on class [com.krtkush.audiotime.modules.podcast.models.PodcastDTO]. Expected arguments are: long, java.lang.String, java.lang.String, java.lang.String, java.lang.Long, long, java.lang.String, java.lang.String [SELECT new com.krtkush.audiotime.modules.podcast.models.PodcastDTO(p.id, p.author, p.title, p.description, p.categoryId, p.subCategoryId, c.name, c2.name) FROM com.krtkush.audiotime.modules.podcast.models.Podcast p LEFT JOIN com.krtkush.audiotime.modules.category.models.Category c ON p.categoryId = c.id LEFT JOIN com.krtkush.audiotime.modules.category.models.Category c2 ON p.subCategoryId = c2.id WHERE p.owner = :ownerId]
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:138) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:725) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:113) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:366) ~[spring-orm-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at com.sun.proxy.$Proxy114.createQuery(Unknown Source) ~[na:na]
    at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:87) ~[spring-data-jpa-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    ... 52 common frames omitted
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate appropriate constructor on class [com.krtkush.audiotime.modules.podcast.models.PodcastDTO]. Expected arguments are: long, java.lang.String, java.lang.String, java.lang.String, java.lang.Long, long, java.lang.String, java.lang.String [SELECT new com.krtkush.audiotime.modules.podcast.models.PodcastDTO(p.id, p.author, p.title, p.description, p.categoryId, p.subCategoryId, c.name, c2.name) FROM com.krtkush.audiotime.modules.podcast.models.Podcast p LEFT JOIN com.krtkush.audiotime.modules.category.models.Category c ON p.categoryId = c.id LEFT JOIN com.krtkush.audiotime.modules.category.models.Category c2 ON p.subCategoryId = c2.id WHERE p.owner = :ownerId]
    at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:74) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.hql.internal.ast.ErrorTracker.throwQueryException(ErrorTracker.java:93) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:282) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:192) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:162) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:604) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:716) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
    ... 60 common frames omitted


Process finished with exit code 1

但是,如果我从查询中删除 p.categoryId, p.subCategoryId 并从 DTO 中删除相应字段,一切正常。

 @Query("SELECT new com.krtkush.audiotime.modules.podcast.models.PodcastDTO" +
            "(p.id, p.author, p.title, p.description, c.name, c2.name) " +
            "FROM Podcast p " +
            "LEFT JOIN Category c ON p.categoryId = c.id " +
            "LEFT JOIN Category c2 ON p.subCategoryId = c2.id " +
            "WHERE p.owner = :ownerId")
    fun getPodcastsByOwner(@Param("ownerId")ownerId: Long): List<PodcastDTO>

和,

data class PodcastDTO(val id: Long, val author: String, val title:String, val description: String,
                 val categoryName: String, val subCategoryName: String?): Serializable

上面的代码完美无缺。我尝试了一种不同的方法,但最终也出现了问题,正如here

所解释的那样

请帮忙。

【问题讨论】:

> 在查询中选择 p.subCategoryId 和 p.categoryId 以匹配构造函数。它是匹配的,对吧?还是我错过了什么? 为什么 Hibernate 期望 long 对应 subCategoryId?它期望其他人使用Long 我也注意到了。 id 也是如此。 这应该行得通!您可以尝试按预期声明一个辅助构造函数来验证,主构造函数是否使用不同的签名创建。 能否给我看一些二级构造函数的代码? 【参考方案1】:

正如@Micheal 所指出的,我也注意到了,休眠的 long 和 Long 之间存在不匹配。对于所有长变量,我必须在我的 DTO 中明确使用 java.lang.Long。这可能是与 Kotlin 一起使用时 hibernate 中的一个错误。

【讨论】:

很遗憾我没有写这个作为答案:-) 我一直在我的 Kotlin 实体中使用 Int 并且从未遇到任何问题(还)。

以上是关于JPA,Hibernate:在现有工作 DTO 中添加新变量时“无法在类上找到适当的构造函数”的主要内容,如果未能解决你的问题,请参考以下文章

JPA与Hibernate的关系

简述 JPA 与 Spring Data JPA 与 Hibernate

JPA基础

如何在 JPA Hibernate 映射中将 GUID(不是 PK)添加到已经具有 PK(整数)的现有实体

springboot JPA

第六篇:SpringBoot整合JPA