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

无法使用带有Spring数据反应存储库的Oracle R2DBC驱动程序执行任何查询

冯风史
2023-03-14

我第一次使用SpringData反应式存储库。

我已经过了官方的留档,我创建了一个基本的CRUD API来使用它们。

我从H2开始,只是为了简单起见,一切都按预期进行。

当我尝试创建新实体时,一切正常:

% curl -v -# -X POST http://localhost:8080/wallet/
*   Trying ::1:8080...
* Connected to localhost (::1) port 8080 (#0)
> POST /wallet/ HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.77.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 201 Created
< Content-Type: application/json
< Content-Length: 57
< 
* Connection #0 to host localhost left intact
{"id":"6cccd902-01a4-4a81-8166-933b2a109ecc","balance":0}

代码非常简单(与SpringData Repositories一样):

import com.jfcorugedo.reactivedemo.wallet.model.Wallet;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;

public interface WalletRepository extends ReactiveCrudRepository<Wallet, String> {
}

和控制器:

import com.jfcorugedo.reactivedemo.wallet.dao.WalletRepository;
import com.jfcorugedo.reactivedemo.wallet.model.Wallet;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import java.math.BigDecimal;

import static org.springframework.data.relational.core.query.Query.query;

@RestController
@RequestMapping("wallet")
@Slf4j
public class WalletController {

    private WalletRepository walletRepository;

    @Autowired
    private R2dbcEntityTemplate template;

    public WalletController(WalletRepository walletRepository) {
        this.walletRepository = walletRepository;
    }

    @GetMapping("{id}")
    public Mono<ResponseEntity<Wallet>> get(@PathVariable("id") String id) {

        return walletRepository
                .findById(id)
                .map(ResponseEntity::ok)
                .defaultIfEmpty(ResponseEntity.notFound().build());
    }

    @GetMapping("count")
    public Mono<ResponseEntity<Long>> count() {

        return walletRepository
                .count()
                .map(ResponseEntity::ok)
                .defaultIfEmpty(ResponseEntity.notFound().build());
    }

    @PostMapping
    public Mono<ResponseEntity<Wallet>> create() {

        return walletRepository
                .save(Wallet.empty())
                .map(w -> ResponseEntity.status(201).body(w));
    }

    @PostMapping("/entityTemplate")
    public Mono<ResponseEntity<Wallet>> insert() {

        log.info("Inserting using R2dbcEntityTemplate");
        return template.insert(new Wallet(null, BigDecimal.ZERO))
                .map(ResponseEntity::ok);
    }
}

DTO也非常简单:

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.data.annotation.Id;

import java.math.BigDecimal;

@AllArgsConstructor
@Getter
public class Wallet {

    @Id
    private String id;
    private BigDecimal balance;

    public static Wallet empty() {
        return new Wallet(null, BigDecimal.ZERO);
    }

    public Wallet withId(String id) {
        return new Wallet(id, this.balance);
    }
}

然后我在文档中检查Oracle也受支持。

我查看了Oracle驱动程序的官方文档。

确实,该驱动程序正在开发中,因此它还没有准备好生产。

但是,我克隆了存储库,并尝试在本地Oracle实例上执行一些测试,一切正常。

下面是我直接使用Oracle驱动程序执行的代码:

String r2dbcUrl = "r2dbc:oracle://?oracleNetDescriptor="+DESCRIPTOR;
    Mono.from(ConnectionFactories.get(ConnectionFactoryOptions.parse(r2dbcUrl)
      .mutate()
      .option(ConnectionFactoryOptions.USER, USER)
      .option(ConnectionFactoryOptions.PASSWORD, PASSWORD)
      .build())
      .create())
      .flatMapMany(connection ->
        Mono.from(connection.createStatement(
          "INSERT INTO WALLET (ID, BALANCE) VALUES ('" + UUID.randomUUID().toString() + "', 0)")
          .execute())
          .flatMapMany(result ->
            result.map((row, metadata) -> row.get(0, String.class)))
          .concatWith(Mono.from(connection.close()).cast(String.class)))
      .toStream()
      .forEach(System.out::println);

    // A descriptor may also be specified as an Option
    Mono.from(ConnectionFactories.get(ConnectionFactoryOptions.builder()
      .option(ConnectionFactoryOptions.DRIVER, "oracle")
      .option(Option.valueOf("oracleNetDescriptor"), DESCRIPTOR)
      .option(ConnectionFactoryOptions.USER, USER)
      .option(ConnectionFactoryOptions.PASSWORD, PASSWORD)
      .build())
      .create())
      .flatMapMany(connection ->
        Mono.from(connection.createStatement(
          "SELECT * from wallet")
          .execute())
          .flatMapMany(result ->
            result.map((row, metadata) -> row.get(0, String.class)))
          .concatWith(Mono.from(connection.close()).cast(String.class)))
      .toStream()
      .forEach(System.out::println);

我正在使用Oracle开发人员在示例文件夹中提供的代码。

执行此代码后,一切正常,并在我的钱包表中创建新行。

最后,我尝试在SpringData中执行同样的操作。

我使用完全相同的描述符、用户和密码连接到Oracle。

这是我用来获取ConnectionFactory的配置类:

package com.jfcorugedo.reactivedemo.config;

import com.jfcorugedo.reactivedemo.wallet.model.Wallet;
import io.r2dbc.spi.ConnectionFactories;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.ConnectionFactoryOptions;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
import org.springframework.data.r2dbc.mapping.event.BeforeConvertCallback;

import reactor.core.publisher.Mono;

import java.util.UUID;

@Configuration
@ConditionalOnProperty(name = "dababase.vendor", havingValue = "oracle")
@Slf4j
public class OracleR2dbcConfig extends AbstractR2dbcConfiguration {

    @Value("${database.host:localhost}")
    private String host;

    @Value("${database.port:1521}")
    private int port;

    @Value("${database.serviceName}")
    private String serviceName;

    @Override
    @Bean("r2dbcConnectionFactory")
    public ConnectionFactory connectionFactory() {
        String descriptor = "(DESCRIPTION=" +
                "(ADDRESS=(HOST=" + host + ")(PORT=" + port + ")(PROTOCOL=tcp))" +
                "(CONNECT_DATA=(SERVICE_NAME=" + serviceName + ")))";

        log.info("Creating connection factory with descriptor " + descriptor);

        String r2dbcUrl = "r2dbc:oracle://?oracleNetDescriptor="+descriptor;
        return ConnectionFactories.get(ConnectionFactoryOptions.parse(r2dbcUrl)
                .mutate()
                .option(ConnectionFactoryOptions.USER, "jfcorugedo")
                .option(ConnectionFactoryOptions.PASSWORD, System.getenv("DB_PASSWORD"))
                .build());
    }

    @Bean
    BeforeConvertCallback<Wallet> idGenerator() {
        return (entity, table) -> entity.getId() == null ? Mono.just(entity.withId(UUID.randomUUID().toString())) : Mono.just(entity);
    }
}

它与我在另一个项目中使用的非常相似:

private static final String DESCRIPTOR = "(DESCRIPTION=" +
    "(ADDRESS=(HOST="+HOST+")(PORT="+PORT+")(PROTOCOL=tcp))" +
    "(CONNECT_DATA=(SERVICE_NAME="+SERVICE_NAME+")))";
...
String r2dbcUrl = "r2dbc:oracle://?oracleNetDescriptor="+DESCRIPTOR;
    Mono.from(ConnectionFactories.get(ConnectionFactoryOptions.parse(r2dbcUrl)
      .mutate()
      .option(ConnectionFactoryOptions.USER, USER)
      .option(ConnectionFactoryOptions.PASSWORD, PASSWORD)
      .build())
      .create())
...

切换到Oracle后,SpringBoot应用程序启动,没有任何错误:

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

2021-08-05 12:48:22.891  INFO 99453 --- [           main] c.j.r.ReactiveDemoApplication            : Starting ReactiveDemoApplication using Java 11.0.10 on APM3LC02CH2VNMD6R with PID 99453 (/Users/lp68ba/Developer/personal/reactive-demo/target/classes started by lp68ba in /Users/lp68ba/Developer/personal/reactive-demo)
2021-08-05 12:48:22.892  INFO 99453 --- [           main] c.j.r.ReactiveDemoApplication            : No active profile set, falling back to default profiles: default
2021-08-05 12:48:23.165  INFO 99453 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data R2DBC repositories in DEFAULT mode.
2021-08-05 12:48:23.214  INFO 99453 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 45 ms. Found 1 R2DBC repository interfaces.
2021-08-05 12:48:23.554  INFO 99453 --- [           main] c.j.r.config.OracleR2dbcConfig           : Creating connection factory with descriptor (DESCRIPTION=(ADDRESS=(HOST=localhost)(PORT=1521)(PROTOCOL=tcp))(CONNECT_DATA=(SERVICE_NAME=ORCLCDB)))
2021-08-05 12:48:24.129  INFO 99453 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port 8080
2021-08-05 12:48:24.142  INFO 99453 --- [           main] c.j.r.ReactiveDemoApplication            : Started ReactiveDemoApplication in 1.466 seconds (JVM running for 2.021)

但是,现在,当我尝试执行任何操作时,连接保持打开状态,并且什么也没有发生:

% curl -v -# -X POST http://localhost:8080/wallet/
*   Trying ::1:8080...
* Connected to localhost (::1) port 8080 (#0)
> POST /wallet/ HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.77.0
> Accept: */*
> 

在应用程序的日志中,我可以看到以下跟踪:

2021-08-05 13:08:20.735 DEBUG 144 --- [nPool-worker-19] o.s.r2dbc.core.DefaultDatabaseClient     : Executing SQL statement [INSERT INTO WALLET (ID, BALANCE) VALUES (:P0_id, :P1_balance)]

然而,执行永远不会结束,并且不会在数据库中创建任何内容。

我尝试了这两种方法:Spring数据反应存储库和R2DBCEntityTemplate,结果相同。

我已经生成了Oracle R2DBC驱动程序的自定义版本,并提供了一些跟踪信息,这就是我得到的:

直接使用Oracle R2DBC驱动程序(一切正常):

Creating OracleConnectionFactoryImpl with options: ConnectionFactoryOptions{options={driver=oracle, oracleNetDescriptor=(DESCRIPTION=(ADDRESS=(HOST=localhost)(PORT=1521)(PROTOCOL=tcp))(CONNECT_DATA=(SERVICE_NAME=ORCLCDB))), password=REDACTED, user=jfcorugedo}}
Oracel reactive adapter obtained: oracle.r2dbc.impl.OracleReactiveJdbcAdapter@c33b74f
Datasource obtained: oracle.jdbc.pool.OracleDataSource@696da30b
Creating a new connection
using adatper y datasource to create a new connection
Creating a OracleConnectionImpl with JDBC connection oracle.jdbc.driver.T4CConnection@10f7f7de
createStatement(sql): INSERT INTO WALLET (ID, BALANCE) VALUES ('9a3ab3db-ec38-4544-ac87-4e1a4ad40343', 0)
close()

使用SpringData Reactive Repositories(连接卡住,什么也没发生):

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

2021-08-05 13:12:49.557  INFO 304 --- [           main] c.j.r.ReactiveDemoApplication            : Starting ReactiveDemoApplication using Java 11.0.10 on APM3LC02CH2VNMD6R with PID 304 (/Users/lp68ba/Developer/personal/reactive-demo/target/classes started by lp68ba in /Users/lp68ba/Developer/personal/reactive-demo)
2021-08-05 13:12:49.559  INFO 304 --- [           main] c.j.r.ReactiveDemoApplication            : No active profile set, falling back to default profiles: default
2021-08-05 13:12:49.849  INFO 304 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data R2DBC repositories in DEFAULT mode.
2021-08-05 13:12:49.891  INFO 304 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 38 ms. Found 1 R2DBC repository interfaces.
2021-08-05 13:12:50.208  INFO 304 --- [           main] c.j.r.config.OracleR2dbcConfig           : Creating connection factory with descriptor (DESCRIPTION=(ADDRESS=(HOST=localhost)(PORT=1521)(PROTOCOL=tcp))(CONNECT_DATA=(SERVICE_NAME=ORCLCDB)))
Creating OracleConnectionFactoryImpl with options: ConnectionFactoryOptions{options={driver=oracle, oracleNetDescriptor=(DESCRIPTION=(ADDRESS=(HOST=localhost)(PORT=1521)(PROTOCOL=tcp))(CONNECT_DATA=(SERVICE_NAME=ORCLCDB))), password=REDACTED, user=jfcorugedo}}
Oracel reactive adapter obtained: oracle.r2dbc.impl.OracleReactiveJdbcAdapter@5f172d4a
Datasource obtained: oracle.jdbc.pool.OracleDataSource@934b52f
2021-08-05 13:12:50.736  INFO 304 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port 8080
2021-08-05 13:12:50.745  INFO 304 --- [           main] c.j.r.ReactiveDemoApplication            : Started ReactiveDemoApplication in 1.417 seconds (JVM running for 4.428)
Creating a new connection
using adatper y datasource to create a new connection
Creating a OracleConnectionImpl with JDBC connection oracle.jdbc.driver.T4CConnection@42dce884
2021-08-05 13:12:54.481 DEBUG 304 --- [nPool-worker-19] o.s.r2dbc.core.DefaultDatabaseClient     : Executing SQL statement [INSERT INTO WALLET (ID, BALANCE) VALUES (:P0_id, :P1_balance)]
OracleConnectionImpl#createStatement(sql): INSERT INTO WALLET (ID, BALANCE) VALUES (:P0_id, :P1_balance)
Creating OracleStatementImpl with SQL: INSERT INTO WALLET (ID, BALANCE) VALUES (:P0_id, :P1_balance)
OracleConnectionImpl#close()

我不知道为什么执行会被SpringData卡住。连接似乎没问题,我在这里使用的参数与我直接在Oracle驱动程序中使用的参数完全相同。

有人有使用SpringData R2DBC存储库和Oracle R2DBC驱动程序的工作示例吗?

您可以检查此存储库中的代码。

共有1个答案

司徒池暝
2023-03-14

目前,在使用Spring数据编程时,请坚持使用Oracle R2DBC的0.1.0版本。

较新版本的Oracle R2DBC实现了R2DBC SPI的0.9.0. M1版本,Spring Data目前不支持该版本。这在GitHub讨论中得到证实:https://github.com/oracle/oracle-r2dbc/issues/30#issuecomment-862989986

一旦我回滚到Oracle R2DBC的0.1.0版,我就能够让演示应用程序正常工作。我必须重构OracleR2dbcConfig.java因为直到0.1.0之后才添加对Oracle Net描述符的支持。普通URL可以很好地配置主机、端口和服务名称:

    public ConnectionFactory connectionFactory() {
        String url =
          String.format("r2dbc:oracle://%s:%d/%s", host, port, serviceName);
        log.info("Creating connection factory with URL:" + url);

        return ConnectionFactories.get(ConnectionFactoryOptions.parse(url)
                .mutate()
                .option(ConnectionFactoryOptions.USER, user)
                .option(ConnectionFactoryOptions.PASSWORD, System.getenv("DB_PASSWORD"))
                .build());
    }

此外,我必须在使用curl执行测试之前手动创建表:

sql prettyprint-override">create table wallet (id VARCHAR2(256), balance NUMBER);

我认为Spring Data通常会自动创建表,所以我不确定为什么要手动执行此操作。如果未创建表,则INSERT将失败并出现错误,表明钱包表不存在:

ORA-04043: object WALLET does not exist

在这些更改之后,curl命令似乎正在通过ok:

curl -v -# -X POST http://localhost:8080/wallet/
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /wallet/ HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
> 
< HTTP/1.1 201 Created
< Content-Type: application/json
< Content-Length: 57
< 
* Connection #0 to host localhost left intact
{"id":"2bcecf46-05eb-46b4-90ec-cfacff2bbaa8","balance":0}* Closing connection 0
 类似资料:
  • 我使用的是,需要提供不同字段的搜索特性。在搜索前输入字段是可选的。我有5个字段,即、、、和。 这里我只需要按用户查询给定的值,其他字段应该忽略。ex, 所以这里我们要考虑输入和查询的值。在这种情况下,Spring data有本文中提到的限制(不可伸缩,所有可能的查询都应该编写)我使用,但问题仍然存在,因为字段应该被忽略,几乎所有可能的查询都需要开发。在这个。如果搜索字段为怎么办?? 用可选字段实现

  • MySQL连接器是maven依赖项的一部分,所有数据库属性如url、用户名、密码都在application.properties中提到。 获取RuntimeException,例如: 驱动程序com.mysql.jdbc.Driver声称不接受jdbcUrl,jdbc/mysql://10.53.235.141:3306/hms。 请帮忙解决。

  • 在执行简单查询时,我有一个重复的查询。文件: someclass.java: someclassRepository.java: service.java: Application.Properties: 日志文件:

  • 我在调试中构建我的项目没有问题。我刚刚安装了一个新的npm包(bugsnag),然后做了react-本机链接,现在我不能再构建它了。我尝试了谷歌的许多建议解决方案,但都不起作用。 奇怪的部分,我试图建立我的项目的备份文件,它没有建立也有同样的错误,所以感觉像它与其他东西有关? 错误详细信息: .... 导致原因:Java . util . concurrent . execution except

  • 问题内容: 我正在使用,要求提供具有不同字段的搜索功能。搜索之前输入的字段是optional.I有5场说。 在这里我只需要查询用户给定的值,其他字段应该被忽略。 因此,这里我们考虑输入的值和进行查询。在这种情况下,Spring数据是具有限制中提到的这篇文章(不可扩展,所有可能出现的问题,应书面)我使用的,但仍然存在问题的领域应该被忽视,需要开发的几乎所有可能出现的问题。在这。如果搜索字段是? 用可

  • 问题内容: 最终用户(开发人员或生产人员)可以推荐一个Sql驱动程序包,最好使用“ database / sql”包。我对Postgres,ODBC,MySql(以及可能用于高容量的其他商品(即,不是Sqlite)的其他商品)感兴趣,这些商品最好可在Windows和/或Linux(最好同时使用)上使用。令我感兴趣的是,它可能需要最近进行了更新/维护,并且必须与最新的Go版本一起使用。效率(吞吐量)