R2DBC 和枚举 (PostgreSQL)

my-*_*my- 4 postgresql enums spring-data spring-data-r2dbc r2dbc

2020Enum年 8 月15 日更新:似乎在 6 月 16添加了支持。R2DBC 提交

H2DBC 是否支持 PostgreSQL 枚举?我检查了他们的git page但它没有提到任何关于它的内容。如果是这样,如何使用枚举(插入、选择)?
让我们说 PostgreSQL 枚举

CREATE TYPE mood AS ENUM ('UNKNOWN', 'HAPPY', 'SAD', ...);
Run Code Online (Sandbox Code Playgroud)

Java类

@Data
public class Person {
    private String name;
    private Mood mood;
    // ...

    enum Mood{ UNKNOWN, HAPPY, SAD, ...}
}
Run Code Online (Sandbox Code Playgroud)

我试过:

        // insert
        var person = ...;
        client.insert()
                .table("people")
                .using(person)
                .then()
                .subscribe(System.out::println);

        // select
        var query = "SELECT * FROM people";
        client.execute(query)
                .as(Person.class)
                .fetch().all()
                .subscribe(System.out::println);
Run Code Online (Sandbox Code Playgroud)

但我收到错误消息:

# on insert
 WARN [reactor-tcp-epoll-1] (Loggers.java:294) - Error: SEVERITY_LOCALIZED=ERROR, SEVERITY_NON_LOCALIZED=ERROR, CODE=42804, MESSAGE=column "mood" is of type mood but expression is of type character varying, HINT=You will need to rewrite or cast the expression., POSITION=61, FILE=parse_target.c, LINE=591, ROUTINE=transformAssignedExpr
# on select
ERROR [reactor-tcp-epoll-1] (Loggers.java:319) - [id: 0x8581acdb, L:/127.0.0.1:39726 ! R:127.0.0.1/127.0.0.1:5432] Error was received while reading the incoming data. The connection will be closed.
reactor.core.Exceptions$ErrorCallbackNotImplemented: org.springframework.data.mapping.MappingException: Could not read property private ...
Run Code Online (Sandbox Code Playgroud)

我找到了类似的帖子,但没有运气来解决我的问题..也许我应用错了..
欢迎任何帮助或提示。

qaz*_*230 5

org.springframework.data:spring-data-r2dbc:1.0.0.RELEASE和测试io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE

科特林版。

  1. 定义一个枚举类

    enum class Mood {
        UNKNOWN,
        HAPPY,
        SAD
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建自定义编解码器

    class MoodCodec(private val allocator: ByteBufAllocator) :  Codec<Mood> {
        override fun canEncodeNull(type: Class<*>): Boolean = false
    
        override fun canEncode(value: Any): Boolean = value is Mood
    
        override fun encode(value: Any): Parameter {
            return Parameter(Format.FORMAT_TEXT, oid) {
                ByteBufUtils.encode(allocator, (value as Mood).name)
            }
        }
    
        override fun canDecode(dataType: Int, format: Format, type: Class<*>): Boolean = dataType == oid
    
        override fun decode(buffer: ByteBuf?, dataType: Int, format: Format, type: Class<out Mood>): Mood? {
            buffer ?: return null
            return Mood.valueOf(ByteBufUtils.decode(buffer))
        }
    
        override fun type(): Class<*> = Mood::class.java
    
        override fun encodeNull(): Parameter =
            Parameter(Format.FORMAT_TEXT, oid, Parameter.NULL_VALUE)
    
        companion object {
            // Get form `select oid from pg_type where typname = 'mood'`
            private const val oid = YOUR_ENUM_OID
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 注册编解码器

    您可能需要更改runtimeOnly("io.r2dbc:r2dbc-postgresql")implementation("io.r2dbc:r2dbc-postgresql")

    @Configuration
    @EnableR2dbcRepositories
    class AppConfig : AbstractR2dbcConfiguration() {
        override fun connectionFactory(): ConnectionFactory = PostgresqlConnectionConfiguration.builder()
            .port(5432) // Add your config here.
            .codecRegistrar { _, allocator, registry ->
                registry.addFirst(MoodCodec(allocator))
                Mono.empty()
            }.build()
            .let { PostgresqlConnectionFactory(it) }
    }
    
    Run Code Online (Sandbox Code Playgroud)


Dou*_*Liu 5

我通过添加定制器而不是自己创建连接工厂,将以下内容用于 Spring boot 2.6.4 + r2dbc-postgresql 0.8.11。

感谢 @Hantsy 指出 EnumCodec。我将其添加到定制器中,因此它可以与现有的自动配置过程很好地配合。另外,spring-data 不断将我的枚举转换为字符串,直到我添加转换器。

希望这些可以给其他人提供一点帮助。

  1. 将 EnumCodec 作为扩展注册到构建器定制器

    可以注册多个枚举,只需重复 withEnum() 调用即可。

  /**
   * Use the customizer to add EnumCodec to R2DBC
   */
  @Bean
  public ConnectionFactoryOptionsBuilderCustomizer connectionFactoryOptionsBuilderCustomizer() {
    return builder -> {
      builder.option(Option.valueOf("extensions"),
                     List.of(EnumCodec.builder()
                       .withEnum("enum_foo", FooEnum.class)
                       .withRegistrationPriority(RegistrationPriority.FIRST)
                       .build()));

      logger.info("Adding enum to R2DBC postgresql extensions: {}", builder);
    };
  }
Run Code Online (Sandbox Code Playgroud)
  1. 通过扩展EnumWriteSupport实现spring数据转换器
public class FooWritingConverter extends EnumWriteSupport<Foo> {
}
Run Code Online (Sandbox Code Playgroud)
  1. 注册转换器,以便 spring 数据不会总是将枚举转换为字符串。

    这一步是 spring-boot-autoconfigure 项目中 R2dbcDataAutoConfiguration 的稍微增强版本。

  /**
   * Register converter to make sure Spring data treat enum correctly
   */
  @Bean
  public R2dbcCustomConversions r2dbcCustomConversions(DatabaseClient databaseClient) {
    logger.info("Apply R2DBC custom conversions");
    R2dbcDialect dialect = DialectResolver.getDialect(databaseClient.getConnectionFactory());
    List<Object> converters = new ArrayList<>(dialect.getConverters());
    converters.addAll(R2dbcCustomConversions.STORE_CONVERTERS);
    return new R2dbcCustomConversions(
      CustomConversions.StoreConversions.of(dialect.getSimpleTypeHolder(), converters),
      List.of(
        new FooWritingConverter()
      ));
  }
Run Code Online (Sandbox Code Playgroud)

步骤 1 和 3 可以添加到您的应用程序类或任何其他有效配置中。