从 H2 数据库读取 TINYINT 值时出现 MappingException boolean/Boolean
Getting MappingException when reading TINYINT value from H2 database as boolean/Boolean
我正在使用 R2DBC H2 来集成测试我的反应式应用程序(基于 Spring Webflux,Java)。
版本
- 驱动:io.r2dbc:r2dbc-h2:0.8.4.RELEASE (Spring boot 2.3.2.RELEASE)
- Java: 11
- OS: Mac
我有一个 table 定义如下:
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[=13=](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[=13=](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;
}
}
文档Data Types说
TINYINT
Possible values are: -128 to 127.
See also integer literal grammar. Mapped to java.lang.Byte.
所以你应该像这样改变你的 DTO class:
@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);
}
// .....
}
我正在使用 R2DBC H2 来集成测试我的反应式应用程序(基于 Spring Webflux,Java)。
版本
- 驱动:io.r2dbc:r2dbc-h2:0.8.4.RELEASE (Spring boot 2.3.2.RELEASE)
- Java: 11
- OS: Mac
我有一个 table 定义如下:
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[=13=](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[=13=](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;
}
}
文档Data Types说
TINYINT
Possible values are: -128 to 127.
See also integer literal grammar. Mapped to java.lang.Byte.
所以你应该像这样改变你的 DTO class:
@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);
}
// .....
}