Spring Boot Sping Boot 找不到存储库Bean

bjp0bcyl  于 2023-11-17  发布在  Spring
关注(0)|答案(3)|浏览(171)

我正在使用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页面,并且对于下一步该怎么做完全是空白的。

w3nuxt5m

w3nuxt5m1#

问题是你没有数据库。
您已经将com.h2database:h2声明为仅testImplementation依赖项,因此它在测试中可用,但在实际运行应用程序时不可用。
最简单的修复方法是将其更改为implementation依赖项,应用程序将正常启动。当然,对于生产环境,您可能希望连接到适当的持久化数据库。此外,您可能希望使用Testcontainers而不是内存中的数据库进行集成测试。但这些都超出了本问题的范围。

如何调试Spring Boots自动配置的问题

1.通过将debug=true添加到application.properties文件来启用Sping Boot 的调试
1.特别是在输出的Negative matches部分,查找应该发生但没有发生的自动配置。搜索相关技术是一个好主意。搜索 JDBC 会产生以下相关结果:

DataSourceAutoConfiguration matched:
      - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType' (OnClassCondition)
      - @ConditionalOnMissingBean (types: io.r2dbc.spi.ConnectionFactory; SearchStrategy: all) did not find any beans (OnBeanCondition)

   DataSourceTransactionManagerAutoConfiguration matched:
      - @ConditionalOnClass found required classes 'org.springframework.jdbc.core.JdbcTemplate', 'org.springframework.transaction.TransactionManager' (OnClassCondition)

字符串
这看起来像你有一个DataSource,这只是证明你不应该在第一个匹配上停止。
下一场比赛是:

DataSourceInitializationConfiguration:
      Did not match:
         - @ConditionalOnSingleCandidate (types: javax.sql.DataSource; SearchStrategy: all) did not find any beans (OnBeanCondition)
      Matched:
         - @ConditionalOnClass found required class 'org.springframework.jdbc.datasource.init.DatabasePopulator' (OnClassCondition)


所以,DataSourceAutoConfiguration确实运行了,但我们似乎仍然没有DataSource bean。从这里我们了解到,类可用和该类型的bean可用之间有一个重要的区别。当你大声说出来时,这是很明显的,但当你查看一些日志文件时,很容易忽略。
剩下的搜索只是找到了更多不起作用的东西,这些东西要么是不相关的,要么是不奇怪的,如果没有DataSource bean的话。
这返回了大量的点击率。但是Negative matches中的第一个真的很有帮助。

DataSourceAutoConfiguration.EmbeddedDatabaseConfiguration:
      Did not match:
         - EmbeddedDataSource did not find embedded database (DataSourceAutoConfiguration.EmbeddedDatabaseCondition)


没有找到嵌入式数据库!它试图使用什么数据库?我检查了application.properties的jdbc url(没有)和数据库的依赖关系,只找到了上面提到的测试依赖关系。

4zcjmb1e

4zcjmb1e2#

在你的例子中,问题是使用record作为实体,这是真正错误的,这是问题的根源。
在Hibernate中,使用代理创建和管理实体。代理是在运行时生成并扩展实体类的类。这些代理依赖于实体类具有无参数构造函数和setter。由于记录没有这些,它们不能用作实体。
要修复它:
而不是

public record Todo(@Id Long id, Long userId, String name, String description, boolean completed){}

字符串
写一个Java类

@Entity  
    @Table(name= "todo")   
    public class Todo {    
      
    @Id   
    private Long id;

    //others entity fields

    … 
   }


一个很好的答案和一些关于这个主题的解释见这个Q/A

v1l68za4

v1l68za43#

你的代码有几个问题。

  • 首先,不要使用java记录作为实体https://howtodoinjava.com/spring/java-records-with-spring-data-jpa/#:~:text= Java%20records%20cannot%20be%20used,that%20map%20to%20database%20tables。
  • 您需要使用来自import jakarta.persistence.Id@Entity and @Id适当地注解实体
  • User实体转换为users
@Entity
 @Table(name = "users")
 public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String username;
  private String password;

字符串
}

  • jdbc相比,使用implementation 'org.springframework.boot:spring-boot-starter-data-jpa'要方便得多,在您的情况下,您无法使用spring数据jpa魔术。
  • 'com.h2database:h2'应该是implementation,限制只能用testImplementation测试
  • 如果您想为h2自定义配置文件,这里有一个示例,但它是可选的

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")

  • 我在application.properties中使用的

服务器.错误.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
在这之后,一切都应该做得很好。

Connected to the target VM, address: '127.0.0.1:52923', transport: 'socket'

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.5)

2023-11-09T13:33:33.955-05:00  INFO 49861 --- [           main] dev.iosenberg.todo.TodoApplication       : Starting TodoApplication using Java 17.0.9 with PID 49861 (/Users/ericus20/StackOverflow/todo/build/classes/java/main started by ericus20 in /Users/ericus20/StackOverflow/todo)
2023-11-09T13:33:33.957-05:00  INFO 49861 --- [           main] dev.iosenberg.todo.TodoApplication       : No active profile set, falling back to 1 default profile: "default"
2023-11-09T13:33:34.244-05:00  INFO 49861 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-11-09T13:33:34.270-05:00  INFO 49861 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 21 ms. Found 2 JPA repository interfaces.
2023-11-09T13:33:34.521-05:00  INFO 49861 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-11-09T13:33:34.526-05:00  INFO 49861 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-11-09T13:33:34.526-05:00  INFO 49861 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.15]
2023-11-09T13:33:34.584-05:00  INFO 49861 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-11-09T13:33:34.585-05:00  INFO 49861 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 607 ms
2023-11-09T13:33:34.661-05:00  INFO 49861 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-11-09T13:33:34.696-05:00  INFO 49861 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.2.13.Final
2023-11-09T13:33:34.699-05:00  INFO 49861 --- [           main] org.hibernate.cfg.Environment            : HHH000406: Using bytecode reflection optimizer
2023-11-09T13:33:34.848-05:00  INFO 49861 --- [           main] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
2023-11-09T13:33:34.862-05:00  INFO 49861 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2023-11-09T13:33:34.953-05:00  INFO 49861 --- [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:test user=SA
2023-11-09T13:33:34.954-05:00  INFO 49861 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2023-11-09T13:33:35.400-05:00  INFO 49861 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
Hibernate: 
    create table todo (
        id bigint generated by default as identity,
        completed boolean not null,
        description varchar(255),
        name varchar(255),
        user_id bigint,
        primary key (id)
    )
Hibernate: 
    create table users (
        id bigint generated by default as identity,
        password varchar(255),
        username varchar(255),
        primary key (id)
    )
2023-11-09T13:33:35.434-05:00  INFO 49861 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2023-11-09T13:33:35.750-05:00  INFO 49861 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-11-09T13:33:35.756-05:00  INFO 49861 --- [           main] dev.iosenberg.todo.TodoApplication       : Started TodoApplication in 1.972 seconds (process running for 2.268)


有关配置和设置的更多信息,可以参考https://github.com/ericus20/spring-boot-starter

相关问题