当前位置: 首页 > 知识库问答 >
问题:

为什么spring-boot-data-jdbc查询方法不需要Args构造函数

司马高韵
2023-03-14

在我看来,使用spring-boot-data-jdbc的实体上的所有查询方法都需要一个无参数的构造函数?有原因吗?这里有一个简单的例子给出了这个例外。使用H2数据库

原因:org.SpringFramework.Beans.BeanInstantiationException:无法实例化[Com.Example.RelationalDataAccess.Person]:未找到默认构造函数;嵌套异常是java.lang.NosuchMethodException:com.example.relationalDataAccess.Person.()

通常是Person实体-

import org.springframework.data.annotation.Id;

public class Person {
    

    @Id
    private long id;
    private  String firstName;
    private  String lastName;

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

然后是存储库

public interface PersonRepository extends CrudRepository<Person, Long>{

    @Query("select * from person where first_name= :firstName")
    List<Person> findByFirstName(@Param("firstName") String firstName);

}

下面是模式

create table person (
                        id integer identity not null primary key,
                        first_name varchar(30),
                        last_name varchar(30)
);
@SpringBootApplication
public class Application implements CommandLineRunner {

    @Autowired
    private PersonRepository repository;


    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... arg0) throws Exception {
        Person person = new Person("John", "Doe");
        repository.save(person);
        System.out.println("FIND BY FIRST_NAME ----->> "+ repository.findByFirstName("John"));
    }
}
spring.jpa.hibernate.ddl-auto=none
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:persondb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=test
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG
logging.level.org.springframework.data=INFO

下面是App的堆栈痕迹--

java.lang.IllegalStateException: Failed to execute CommandLineRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:779) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    at com.example.relationaldataaccess.Application.main(Application.java:21) ~[classes/:na]
Caused by: org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate com.example.relationaldataaccess.Person using constructor NO_CONSTRUCTOR with arguments 
    at org.springframework.data.mapping.model.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:65) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:87) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    at org.springframework.data.relational.core.conversion.BasicRelationalConverter.createInstance(BasicRelationalConverter.java:147) ~[spring-data-relational-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.springframework.data.jdbc.core.convert.BasicJdbcConverter$ReadingContext.createInstanceInternal(BasicJdbcConverter.java:527) ~[spring-data-jdbc-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.springframework.data.jdbc.core.convert.BasicJdbcConverter$ReadingContext.mapRow(BasicJdbcConverter.java:387) ~[spring-data-jdbc-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.springframework.data.jdbc.core.convert.BasicJdbcConverter.mapRow(BasicJdbcConverter.java:323) ~[spring-data-jdbc-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.springframework.data.jdbc.core.convert.EntityRowMapper.mapRow(EntityRowMapper.java:67) ~[spring-data-jdbc-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.springframework.data.jdbc.repository.support.JdbcQueryLookupStrategy$PostProcessingRowMapper.mapRow(JdbcQueryLookupStrategy.java:152) ~[spring-data-jdbc-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:94) ~[spring-jdbc-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:61) ~[spring-jdbc-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:679) ~[spring-jdbc-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:617) ~[spring-jdbc-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:669) ~[spring-jdbc-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:694) ~[spring-jdbc-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.query(NamedParameterJdbcTemplate.java:176) ~[spring-jdbc-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.data.jdbc.repository.query.AbstractJdbcQuery.lambda$getQueryExecution$2(AbstractJdbcQuery.java:127) ~[spring-data-jdbc-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.springframework.data.jdbc.repository.query.StringBasedJdbcQuery.execute(StringBasedJdbcQuery.java:85) ~[spring-data-jdbc-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor$QueryMethodInvoker.invoke(QueryExecutorMethodInterceptor.java:195) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:152) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:130) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at com.sun.proxy.$Proxy48.findByFirstName(Unknown Source) ~[na:na]
    at com.example.relationaldataaccess.Application.run(Application.java:28) ~[classes/:na]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:795) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    ... 5 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.relationaldataaccess.Person]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.example.relationaldataaccess.Person.<init>()
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:146) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.data.mapping.model.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:62) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    ... 36 common frames omitted
Caused by: java.lang.NoSuchMethodException: com.example.relationaldataaccess.Person.<init>()
    at java.base/java.lang.Class.getConstructor0(Class.java:3349) ~[na:na]
    at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2553) ~[na:na]
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:139) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    ... 37 common frames omitted

2020-09-23 19:31:04.053  INFO 29948 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2020-09-23 19:31:04.059  INFO 29948 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

Process finished with exit code 1

共有1个答案

翟嘉年
2023-03-14

我认为这条错误信息是误导的。Spring Data JDBC不需要No-Args-Constructor,但它需要一种设置所有属性的方法,并且不访问私有字段。它们毕竟是私有的,因此只能由拥有它们的类访问。您的id字段不满足此要求。

执行下列操作之一应可解决此问题:

  • 使id字段至少为package private,即无修饰符
  • ID字段添加一个setter。至少再次包私有
  • 向构造函数的参数添加ID
  • 添加一个将所有三个字段都作为参数的构造函数。您可能必须将@PersistenceConstructor添加到该构造函数。
 类似资料:
  • 问题内容: 必须使用无参数构造函数(像Hibernate这样的工具会在此构造函数上使用反射来实例化对象)。 我得到了这个手挥手的答案,但是有人可以进一步解释吗?谢谢 问题答案: hibernate,并且通常通过反射创建对象的代码用于创建类的新实例。此方法需要一个公共的无参数构造函数才能实例化该对象。对于大多数用例,提供无参数构造函数不是问题。 有一些基于序列化的技巧可以解决没有no-arg构造函数

  • 问题内容: 我对此代码有疑问:https : //github.com/reactjs/redux/blob/master/examples/async/containers/App.js 特别: 我猜这是一个两部分的问题。 为什么我需要将句柄更改设置为类的实例,我不能只对handleChange使用静态函数并直接在类中调用它 ? 我不知道这是怎么回事: 谢谢 问题答案: 以相反的顺序回答… 返回

  • 问题内容: 使用Promises时,为什么不能在代码库的其他地方触发并定义? 我不明白为什么,应该在声明诺言的地方本地化逻辑。这是疏忽大意,还是强制执行此参数有好处吗? 我相信执行程序功能应该是可选的,并且它的存在应该确定promise是否封装了解决方案。没有这样的授权,promise的可扩展性就更大,因为您不必立即启动异步。承诺也应该是可重置的。这是1档开关,1或0 或。可以附加许多平行和顺序的

  • 问题内容: 这有任何重要目的吗?可以省略吗? 问题答案: 并非总是必要的,但是它确实有其用途。假设我们想在基类上创建一个复制方法。像这样: 现在,当我们创建一个新的并复制它时会发生什么? 该副本不是的实例。这是因为(没有显式检查),我们无法从“基本”类返回副本。我们只能返回。但是,如果我们重置了构造函数: …然后一切都按预期进行:

  • 问题内容: 如果您尝试封送引用了没有no-arg构造函数的复杂类型的类,例如: 与Java一起的JAXB实现,如下所示: JAXB会抛出一个 现在,我了解了为什么JAXB在解组时需要no-arg构造函数-因为它需要实例化该对象。但是为什么JAXB在编组时需要no-arg构造函数? 另外,还有一个问题,为什么Java的JAXB实现会在字段为null时抛出异常,并且无论如何都不会将其编组? 我是否缺少

  • 我目前正在学习Java的继承,我在理解它方面遇到了很大的困难,但是我不能理解的一个主要问题是为什么在下面的示例中有必要调用一个超类构造函数,它有什么帮助?此示例来自Oracle的方法教程。 我最初的想法是,会缩短当前构造函数中参数的代码,它只会接受父类中传入的值并将其添加到子类中(如下所示): 但是,正如我已经知道的那样,这是错误的,并且再次将我带到了真正做什么的问题上。我会非常感谢一些形式的解释