从 H2 数据库中读取 TINYINT 值作为布尔值/布尔值时出现 MappingException

Posted

技术标签:

【中文标题】从 H2 数据库中读取 TINYINT 值作为布尔值/布尔值时出现 MappingException【英文标题】:Getting MappingException when reading TINYINT value from H2 database as boolean/Boolean 【发布时间】:2020-12-03 01:21:22 【问题描述】:

我正在使用 R2DBC H2 集成测试我的反应式应用程序(基于 Spring Webflux,Java)。

版本

驱动:io.r2dbc:r2dbc-h2:0.8.4.RELEASE (Spring boot 2.3.2.RELEASE) Java:11 操作系统:Mac

我有一个这样定义的表:

CREATE TABLE `prep_task` (
  `id` SERIAL,
  `external_id` VARCHAR(45) NOT NULL,
  `name` VARCHAR(45) NOT NULL,
  `description` VARCHAR(100) NULL,
  `is_active` TINYINT,
  PRIMARY KEY (`id`)
);

以及对应的实体:

@Table("prep_task")
public class PrepTaskEntity implements Persistable<Long> 
  @Id
  @Column("id")
  private Long id;

  @Column("external_id")
  private String externalId;

  @Column("name")
  private String name;

  @Column("description")
  private String description;

  @Column("is_active")
  private boolean isActive;

  public Long getId() 
    return id;
  

  public void setId(Long id) 
    this.id = id;
  

  public String getExternalId() 
    return externalId;
  

  public void setExternalId(String externalId) 
    this.externalId = externalId;
  

  public String getName() 
    return name;
  

  public void setName(String name) 
    this.name = name;
  

  public String getDescription() 
    return description;
  

  public void setDescription(String description) 
    this.description = description;
  

  public boolean isActive() 
    return isActive;
  

  public void setActive(boolean active) 
    isActive = active;
  

  @Override
  public boolean isNew() 
    return id == null;
  



保存PrepTaskEntity时没有问题,但尝试读取时出现以下错误

org.springframework.data.mapping.MappingException: Could not read property @org.springframework.data.relational.core.mapping.Column(value="is_active")private java.lang.Boolean com.example.demosvc.persistence.entities.PrepTaskEntity.isActive from result set!
    at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.readFrom(MappingR2dbcConverter.java:172) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ Handler com.example.demosvc.controllers.PrepTaskController#getPrepTasksForGivenBU(String) [DispatcherHandler]
    |_ checkpoint ⇢ com.example.demosvc.filters.TenancyContextFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ HTTP GET "/sites/BU-002/prepTasks" [ExceptionHandlingWebHandler]
Stack trace:
        at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.readFrom(MappingR2dbcConverter.java:172) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
        at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.read(MappingR2dbcConverter.java:133) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
        at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.read(MappingR2dbcConverter.java:116) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
        at org.springframework.data.r2dbc.convert.EntityRowMapper.apply(EntityRowMapper.java:46) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
        at org.springframework.data.r2dbc.convert.EntityRowMapper.apply(EntityRowMapper.java:29) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
        at io.r2dbc.h2.H2Result.lambda$map$0(H2Result.java:67) ~[r2dbc-h2-0.8.4.RELEASE.jar:0.8.4.RELEASE]
        at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:100) ~[reactor-core-3.3.8.RELEASE.jar:3.3.8.RELEASE]
        at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:73) ~[reactor-core-3.3.8.RELEASE.jar:3.3.8.RELEASE]
...
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.Byte] to type [java.lang.Boolean]
    at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:321) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:194) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:174) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.getPotentiallyConvertedSimpleRead(MappingR2dbcConverter.java:263) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
    at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.readValue(MappingR2dbcConverter.java:187) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
    at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.readFrom(MappingR2dbcConverter.java:169) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
    at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.read(MappingR2dbcConverter.java:133) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
    at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.read(MappingR2dbcConverter.java:116) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
    at org.springframework.data.r2dbc.convert.EntityRowMapper.apply(EntityRowMapper.java:46) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
    at org.springframework.data.r2dbc.convert.EntityRowMapper.apply(EntityRowMapper.java:29) ~[spring-data-r2dbc-1.1.2.RELEASE.jar:1.1.2.RELEASE]
    at io.r2dbc.h2.H2Result.lambda$map$0(H2Result.java:67) ~[r2dbc-h2-0.8.4.RELEASE.jar:0.8.4.RELEASE]

仅供参考,我使用 mysql 作为 H2 的连接模式。

@Configuration
@Profile(Application.Profiles.INTEGRATION_TEST)
public class H2ConnectionConfig extends AbstractR2dbcConfiguration 

  private final R2dbcConfigProperties r2dbcConfigProperties;

  @Autowired
  public H2ConnectionConfig(R2dbcConfigProperties r2dbcConfigProperties) 
    this.r2dbcConfigProperties = r2dbcConfigProperties;
  

  @Override
  @Bean
  public ConnectionFactory connectionFactory() 
    RoutingConnectionFactory connectionFactory = new RoutingConnectionFactory();
    Map<String, ConnectionFactory> factories = new HashMap<>();
    H2ConnectionConfiguration.Builder configurationBuilder = H2ConnectionConfiguration.builder()
        .property(H2ConnectionOption.MODE, "MySQL")
        .property(H2ConnectionOption.DB_CLOSE_DELAY, "-1")
        .property(H2ConnectionOption.DB_CLOSE_ON_EXIT, "false");
    for (String tenant : r2dbcConfigProperties.getTenants()) 
      String databaseName = r2dbcConfigProperties.getDbPrefix() + tenant;
      factories.put(tenant, new H2ConnectionFactory(configurationBuilder.inMemory(databaseName).build()));
    
    connectionFactory.setTargetConnectionFactories(factories);
    connectionFactory.setDefaultTargetConnectionFactory(factories.get("default"));
    return connectionFactory;
  


【问题讨论】:

我认为问题可能在于您使用的是布尔值(原始)。尝试使用布尔(包装器),我认为它可以解决您的问题。 @gagarwa 也尝试过使用布尔值。同样的情况。查看更新的堆栈跟踪 您能否更新您的表以将 isActive 映射到 BIT/BOOL/BOOLEAN?这就是推荐的。问题是没有从 Byte(映射到 tinyint)到 Boolean 的转换器。我也将 Integer 映射到 Boolean(在 DB2 中),但不确定这是类型问题(int vs byte)还是对您不起作用的 db 问题。 @gagarwa 如果 H2 是这种情况,那么我应该为 H2 定义单独的 sql 脚本并为主应用程序单独定义。因为主要的使用 Mysql db,它没有 BOOLEAN 类型。这就是为什么使用 TINYINT @gagarwa 你说这是推荐的吗?这在任何地方都提到过吗?这是 R2DBC-H2 的未解决问题吗?因为在非反应性世界中,这不是问题 【参考方案1】:

文件Data Types说

TINYINT 可能的值是:-128 到 127。 另请参见整数文字语法。 映射到 java.lang.Byte

所以你应该像这样改变你的 DTO 类:

@Table("prep_task")
public class PrepTaskEntity implements Persistable<Long> 

    // .....

    @Column("is_active")
    private Byte isActive;

    // .....

    public boolean isActive() 
        return isActive != null && isActive != 0;
    

    public void setActive(boolean active) 
        isActive = (byte) (active ? 1 : 0);
    

    // .....


【讨论】:

我会说你应该使用正确的布尔类型,但这是一个有效的解决方法......我自己用它来处理遗留应用程序。

以上是关于从 H2 数据库中读取 TINYINT 值作为布尔值/布尔值时出现 MappingException的主要内容,如果未能解决你的问题,请参考以下文章

mysql 数据库中的布尔字段

用于存储布尔值的 MySQL 数据类型

用于存储布尔值的 MySQL 数据类型

用于存储布尔值的 MySQL 数据类型

在 Symfony 中使用 Doctrine 的 DBAL 检索布尔值

“将 Tiny 视为布尔值”和实体框架 4