简体   繁体   English

无法在使用 WebFlux 的 Spring 引导中使用 H2 和 R2DBC 创建 ConnectionFactory 错误

[英]Unable to create a ConnectionFactory Error with H2 and R2DBC in Spring Boot with WebFlux

I've created a Java Spring Boot service using the WebFlux reactive module, H2 in-memory database, and R2DBC reactive driver.我使用 WebFlux 反应模块、H2 内存数据库和 R2DBC 反应驱动程序创建了 Java Spring 引导服务。

When I run the service, it fails with an "Unable to create a ConnectionFactory" error :当我运行该服务时,它失败并出现“无法创建 ConnectionFactory”错误

Unable to create a ConnectionFactory for 'ConnectionFactoryOptions{options={driver=h2, protocol=mem, database=contentitem, options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE}}'. Available drivers: [ pool ]

This is a nested exception that seems to start in the repository and then propagate back through the service to the controller.这是一个嵌套异常,它似乎从存储库中开始,然后通过服务传播回 controller。

Having read through the resulting stack trace carefully and finding no indication about what the problem might be, the only clue I can find is that the 'connectionFactory' input parameter in my CustomConnectionFactoryInitializer class (please see below) is being highlighted with a red squiggle ("Could not autowire. There is more than one bean of 'ConnectionFactory' type")... except that there isn't.仔细阅读生成的堆栈跟踪并发现没有任何迹象表明问题可能是什么,我能找到的唯一线索是我的CustomConnectionFactoryInitializer class(请参见下文)中的“connectionFactory”输入参数用红色曲线突出显示( “无法自动装配。'ConnectionFactory' 类型的 bean 不止一个”)......除了没有。 At least there's only one explicitly defined bean -- the one in my CustomConnectionFactoryInitializer class.至少只有一个明确定义的 bean——我的CustomConnectionFactoryInitializer class 中的那个。

Any idea what's going on here?知道这里发生了什么吗?

Details细节

It's a Gradle project, and my build.gradle file is:这是一个 Gradle 项目,我的 build.gradle 文件是:

plugins {
    id 'org.springframework.boot' version '2.3.9.RELEASE'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.mycorp.service'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'com.h2database:h2'
    runtimeOnly 'io.r2dbc:r2dbc-h2'
    runtimeOnly 'io.r2dbc:r2dbc-postgresql'
    runtimeOnly 'org.postgresql:postgresql'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation 'io.projectreactor:reactor-test'
    compile 'io.springfox:springfox-swagger2:3.0.0'
    compile 'io.springfox:springfox-swagger-ui:3.0.0'
    compile 'io.springfox:springfox-spring-webflux:3.0.0'
}

test {
    useJUnitPlatform()
}

I've added a schema.sql file under main/resources, which contains the following:我在 main/resources 下添加了一个 schema.sql 文件,其中包含以下内容:

CREATE TABLE contentitem ( contentItemId INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, localizedName VARCHAR(100) NOT NULL);

I populate the table with a data.sql file in the same directory:我使用同一目录中的 data.sql 文件填充表:

INSERT INTO contentitem (contentItemId, localizedName) VALUES (0, 'Zero');
INSERT INTO contentitem (contentItemId, localizedName) VALUES (1, 'One');
INSERT INTO contentitem (contentItemId, localizedName) VALUES (2, 'Two');

I've created a CustomConnectionFactoryInitializer to create and populate the database:我创建了一个 CustomConnectionFactoryInitializer 来创建和填充数据库:

@Configuration
public class CustomConnectionFactoryInitializer {
    @Bean
    public ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
        ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
        initializer.setConnectionFactory(connectionFactory);
        CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
        populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
        initializer.setDatabasePopulator(populator);
        return initializer;
    }
}

I've defined a 'test' profile using in-memory H2 and made it active in my application.yml file:我已经使用内存 H2 定义了一个“测试”配置文件,并在我的 application.yml 文件中使其处于活动状态:

spring:
  profiles:
    active: test
---
spring:
  profiles: dev
  r2dbc:
    url: r2dbc:postgresql://localhost:5432/test
    username: postgres
    password: postgres
logging:
  level:
    org.springframework.data.r2dbc: Debug
---
spring:
  profiles: test
  r2dbc:
    url: r2dbc:h2:mem:///contentitem?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    name: sa
    password:
---
spring:
  profiles: prod
  r2dbc:
    url: r2dbc:postgresql://localhost:5432/test
    username: postgres
    password: postgres
  logging:
    level:
      org.springframework.data.r2dbc: Debug

My ContentItem model is:我的 ContentItem model 是:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Table("contentitem")
public class ContentItem {
    @Id
    @Column("contentItemId")
    private Integer contentItemId;
    @Column("localizedName")
    private String localizedName;
}

My ContentItemController is:我的 ContentItemController 是:

@RestController
@RequestMapping("/contentItems")
public class ContentItemController {
    @Autowired
    private ContentItemService contentItemService;

    @GetMapping("/{contentItemId}")
    public Mono<ResponseEntity<ContentItem>> getContentItemByUserId(@PathVariable Integer contentItemId){
        Mono<ContentItem> contentItem = contentItemService.getContentItemById(contentItemId);
        return contentItem.map( u -> ResponseEntity.ok(u))
                .defaultIfEmpty(ResponseEntity.notFound().build());
    }

My ContentItemService is:我的 ContentItemService 是:

@Service
@Slf4j
@Transactional
public class ContentItemService {

    @Autowired
    private ContentItemRepository contentItemRepository;

    public Mono<ContentItem> getContentItemById(Integer contentItemId){
        return contentItemRepository.findByContentItemId(contentItemId);
    }

}

And my ContentItemRepository is:我的 ContentItemRepository 是:

public interface ContentItemRepository extends ReactiveCrudRepository<ContentItem,Integer> {
    Mono<ContentItem> findByContentItemId(Integer contentItemId);
}

Complicating all this is that the H2 console, which I've enabled in the application.properties file with spring.h2.console.enabled=true is failing with a 404 Not Found error when I call it with http://localhost:8081/h2-console .使这一切复杂化的是,我在 application.properties 文件中使用spring.h2.console.enabled=true启用的 H2 控制台在我使用http://localhost:8081/h2-console调用它时失败并出现 404 Not Found 错误http://localhost:8081/h2-console Any ideas what could be going on here?有什么想法可以在这里发生吗?

OK, so I went back through my project file by file, diffing each file with a copy of the repo I was using as a guide.好的,所以我逐个文件地查看我的项目文件,将每个文件与我用作指南的 repo 的副本进行比较。 I found some extra database connection configuration code I thought I'd gotten rid of.我发现了一些我认为我已经摆脱的额外数据库连接配置代码。 As soon as I removed it, problem solved.一旦我删除它,问题就解决了。 Thanks to everyone who took a look and offered suggestions.感谢所有看过并提供建议的人。

For me r2dbc-postgresql was missing in the pom.xml对我来说,pom.xml 中缺少 r2dbc-postgresql

<dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-postgresql</artifactId>
</dependency>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM