我正在使用Spring Data JDBC在Sping Boot 中构建Todo应用程序。我已经构建了所有的REST端点,它们在单元测试中工作正常,但是当我运行应用程序以访问浏览器中的端点/页面时,我得到以下错误:
Parameter 0 of constructor in dev.iosenberg.todo.controllers.TodoController required a bean of type 'dev.iosenberg.todo.repositories.TodoRepository' that could not be found.
字符串
以下是一些相关文件:
TodoApplication.java:
package dev.iosenberg.todo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TodoApplication {
public static void main(String[] args) {
SpringApplication.run(TodoApplication.class, args);
}
}
型
models/Todo.java:
package dev.iosenberg.todo.models;
import org.springframework.data.annotation.Id;
public record Todo(@Id Long id, Long userId, String name, String description, boolean completed) {
}
型
repositories/TodoRepository.java:
package dev.iosenberg.todo.repositories;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import dev.iosenberg.todo.models.Todo;
@Repository
public interface TodoRepository extends CrudRepository<Todo,Long>, PagingAndSortingRepository<Todo, Long>{
}
型
controllers/TodoController.java:
package dev.iosenberg.todo.controllers;
import java.net.URI;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties.Pageable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import dev.iosenberg.todo.models.Todo;
import dev.iosenberg.todo.repositories.TodoRepository;
import java.util.List;
@RestController
@RequestMapping("/todos")
public class TodoController {
@Autowired
private TodoRepository todoRepository;
public TodoController(TodoRepository todoRepository) {
this.todoRepository = todoRepository;
}
@GetMapping
public ResponseEntity<List<Todo>> findAll(Pageable pageable) {
Page<Todo> page = todoRepository.findAll(
PageRequest.of(1,1)
);
return ResponseEntity.ok(page.getContent());
}
@GetMapping("/{requestedId}")
public ResponseEntity<Todo> findById(@PathVariable Long requestedId) {
Optional<Todo> todoOptional = todoRepository.findById(requestedId);
if(todoOptional.isPresent()) {
return ResponseEntity.ok(todoOptional.get());
}
else {
return ResponseEntity.notFound().build();
}
}
@PostMapping
public ResponseEntity<Void> createTodo(@RequestBody Todo newTodoRequest, UriComponentsBuilder ucb) {
Todo savedTodo = todoRepository.save(newTodoRequest);
URI locationOfNewTodo = ucb
.path("todos/{id}")
.buildAndExpand(savedTodo.id())
.toUri();
return ResponseEntity.created(locationOfNewTodo).build();
}
@PutMapping("/{id}")
private ResponseEntity<Void> putTodo(@PathVariable Long id, @RequestBody Todo todoUpdate) {
Optional<Todo> todoOptional = todoRepository.findById(id);
if(todoOptional.isPresent()) {
Todo updatedTodo = todoUpdate;
todoRepository.save(updatedTodo);
return ResponseEntity.noContent().build();
}
return ResponseEntity.notFound().build();
}
@DeleteMapping("/{id}")
private ResponseEntity<Void> deleteTodo(@PathVariable Long id) {
if(!todoRepository.existsById(id)) {
return ResponseEntity.notFound().build();
}
todoRepository.deleteById(id);
return ResponseEntity.noContent().build();
}
}
型
我也有这个服务于一些网页MvcConfig.java:
package dev.iosenberg.todo;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MvcConfig implements WebMvcConfigurer {
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/home").setViewName("home");
registry.addViewController("/").setViewName("home");
registry.addViewController("/test").setViewName("test");
registry.addViewController("/hello").setViewName("hello");
registry.addViewController("/login").setViewName("login");
}
}
型
build.gradle:
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.5'
id 'io.spring.dependency-management' version '1.1.3'
}
group = 'dev.iosenberg'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
repositories {
mavenCentral()
}
dependencies {
// implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
// implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'org.springframework.data:spring-data-jdbc'
testImplementation 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// testImplementation 'org.springframework.security:spring-security-test'
}
tasks.named('test') {
useJUnitPlatform()
}
test {
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
showExceptions true
exceptionFormat "full"
showCauses true
showStackTraces true
// Change to `true` for more verbose test output
showStandardStreams = true
}
}
型
这是整个控制台输出:
2023-11-09T12:09:23.449-05:00 INFO 27748 --- [ main] dev.iosenberg.todo.TodoApplication : Starting TodoApplication using Java 17.0.9 with PID 27748 (C:\Users\ikeos\Documents\git\todo\bin\main started by ikeos in C:\Users\ikeos\Documents\git\todo)
2023-11-09T12:09:23.455-05:00 INFO 27748 --- [ main] dev.iosenberg.todo.TodoApplication : No active profile set, falling back to 1 default profile: "default"
2023-11-09T12:09:25.106-05:00 INFO 27748 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-11-09T12:09:25.119-05:00 INFO 27748 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-11-09T12:09:25.120-05:00 INFO 27748 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.15]
2023-11-09T12:09:25.274-05:00 INFO 27748 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-11-09T12:09:25.277-05:00 INFO 27748 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1740 ms
2023-11-09T12:09:25.366-05:00 WARN 27748 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'todoController' defined in file [C:\Users\ikeos\Documents\git\todo\bin\main\dev\iosenberg\todo\controllers\TodoController.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'dev.iosenberg.todo.repositories.TodoRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
2023-11-09T12:09:25.372-05:00 INFO 27748 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2023-11-09T12:09:25.395-05:00 INFO 27748 --- [ main] .s.b.a.l.ConditionEvaluationReportLogger :
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2023-11-09T12:09:25.431-05:00 ERROR 27748 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in dev.iosenberg.todo.controllers.TodoController required a bean of type 'dev.iosenberg.todo.repositories.TodoRepository' that could not be found.
Action:
Consider defining a bean of type 'dev.iosenberg.todo.repositories.TodoRepository' in your configuration.
型
还有更多的代码不太相关,但如果有用的话,整个代码库都在这里:https://github.com/iosenberg/todo
我尝试了两种解决方案。
第一个是添加@ PencentScan和(basePackages =“dev.iosenberg.todo”)/(basePackages =“dev.iosenberg.todo.repositories”,“dev.iosenberg.todo.controllers”,etc.),最好的结果是删除了Repository Bean错误,但是当我试图访问通过应用程序提供的任何页面时,我得到了一个Whitelab 404错误页面。
第二个解决方案是将我的测试存储库(data.sql和schema.sql)从src/test/resources移动到src/main/resources,但我只是得到了SQL错误,说它不能识别表。
我已经搜索了几乎每一个提到Repository错误的Stack Overflow页面,并且对于下一步该怎么做完全是空白的。
3条答案
按热度按时间w3nuxt5m1#
问题是你没有数据库。
您已经将
com.h2database:h2
声明为仅testImplementation
依赖项,因此它在测试中可用,但在实际运行应用程序时不可用。最简单的修复方法是将其更改为
implementation
依赖项,应用程序将正常启动。当然,对于生产环境,您可能希望连接到适当的持久化数据库。此外,您可能希望使用Testcontainers而不是内存中的数据库进行集成测试。但这些都超出了本问题的范围。如何调试Spring Boots自动配置的问题
1.通过将
debug=true
添加到application.properties
文件来启用Sping Boot 的调试1.特别是在输出的
Negative matches
部分,查找应该发生但没有发生的自动配置。搜索相关技术是一个好主意。搜索 JDBC 会产生以下相关结果:字符串
这看起来像你有一个
DataSource
,这只是证明你不应该在第一个匹配上停止。下一场比赛是:
型
所以,
DataSourceAutoConfiguration
确实运行了,但我们似乎仍然没有DataSource
bean。从这里我们了解到,类可用和该类型的bean可用之间有一个重要的区别。当你大声说出来时,这是很明显的,但当你查看一些日志文件时,很容易忽略。剩下的搜索只是找到了更多不起作用的东西,这些东西要么是不相关的,要么是不奇怪的,如果没有
DataSource
bean的话。这返回了大量的点击率。但是
Negative matches
中的第一个真的很有帮助。型
没有找到嵌入式数据库!它试图使用什么数据库?我检查了
application.properties
的jdbc url(没有)和数据库的依赖关系,只找到了上面提到的测试依赖关系。4zcjmb1e2#
在你的例子中,问题是使用
record
作为实体,这是真正错误的,这是问题的根源。在Hibernate中,使用代理创建和管理实体。代理是在运行时生成并扩展实体类的类。这些代理依赖于实体类具有无参数构造函数和setter。由于记录没有这些,它们不能用作实体。
要修复它:
而不是
字符串
写一个Java类
型
一个很好的答案和一些关于这个主题的解释见这个Q/A
v1l68za43#
你的代码有几个问题。
import jakarta.persistence.Id
的@Entity and @Id
适当地注解实体User
实体转换为users
字符串
}
jdbc
相比,使用implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
要方便得多,在您的情况下,您无法使用spring数据jpa魔术。'com.h2database:h2'
应该是implementation
,限制只能用testImplementation
测试spring. jdbc.url=jdbc:h2:jQuery:test;DB_CLOSE_ON_EXIT= jQuery
spring. spring.username=sa
spring.密码.密码=
spring.驱动程序.driverClassName=org.h2.Driver
@EnableJpaRepositories(basePackages = "dev.iosenberg.todo.repositories") @EntityScan(basePackages = "dev.iosenberg.todo.models")
个服务器.错误.include-message=always服务器.错误.include-exception=true服务器.错误.include-stacktrace=always服务器.错误.include-binding-errors=always spring.thymeleaf.prefix= classpath:/templates/ spring.thymeleaf.suffix= .html
=数据源
spring. spring.url=jdbc:h2:js:test;DB_CLOSE_ON_EXIT= spring. js.username=sa spring. js.password= spring. js.driverClassName=org.h2.Driver
=
= JPA /休眠
=
spring.jpa.show-sql=true spring.jpa.open-in-view=false spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.format_sql=true #spring.jpa.properties.hibernate.generate_statistics=true
= THYMELEAF
是否开启模板缓存。
spring.thymeleaf.cache=false
在这之后,一切都应该做得很好。
型
有关配置和设置的更多信息,可以参考https://github.com/ericus20/spring-boot-starter