HsqlException:不兼容的数据类型组合
Posted
技术标签:
【中文标题】HsqlException:不兼容的数据类型组合【英文标题】:HsqlException: incompatible data types in combination 【发布时间】:2020-12-13 08:59:27 【问题描述】:由于我将集成测试移至内存中的 HSQLDB(希望避免每次都必须提供正确的 mysql 并在每次测试后清除数据库),因此每次使用 CrudRepository#findById() 方法时都会收到此错误消息叫
Caused by: org.hsqldb.HsqlException: incompatible data types in combination
at org.hsqldb.error.Error.error(Unknown Source) ~[hsqldb-2.5.1.jar:2.5.1]
at org.hsqldb.error.Error.error(Unknown Source) ~[hsqldb-2.5.1.jar:2.5.1]
at org.hsqldb.types.CharacterType.getAggregateType(Unknown Source) ~[hsqldb-2.5.1.jar:2.5.1]
at org.hsqldb.types.Type.getAggregateType(Unknown Source) ~[hsqldb-2.5.1.jar:2.5.1]
GET /ID
、PATCH /ID
和 DELETE /ID
调用 REST API(弹簧数据休息)就是这种情况。外部例外是
Caused by: java.sql.SQLSyntaxErrorException: incompatible data types in combination in statement [select trainingre0_.id as id1_15_0_, trainingre0_.matching_rule_id as matching3_15_0_, trainingre0_.tenant as tenant2_15_0_, matchingru1_.id as id1_2_1_, matchingru1_.mode as mode1_0_1_, matchingru1_.mode as mode1_3_1_, matchingru1_.property as property2_3_1_, matchingru1_.value as value3_3_1_, matchingru1_.ignore_case as ignore_c1_6_1_, matchingru1_.mode as mode2_6_1_, matchingru1_.property as property3_6_1_, matchingru1_.value as value4_6_1_, matchingru1_.clazz_ as clazz_1_, matchingru2_.composite_matching_rule_id as composit1_1_2_, matchingru3_.id as matching2_1_2_, matchingru3_.id as id1_2_3_, matchingru3_.mode as mode1_0_3_, matchingru3_.mode as mode1_3_3_, matchingru3_.property as property2_3_3_, matchingru3_.value as value3_3_3_, matchingru3_.ignore_case as ignore_c1_6_3_, matchingru3_.mode as mode2_6_3_, matchingru3_.property as property3_6_3_, matchingru3_.value as value4_6_3_, matchingru3_.clazz_ as clazz_3_ from training_request_subscription trainingre0_ left outer join ( select id, mode, cast(null as varchar(100)) as property, cast(null as int) as value, cast(null as boolean) as ignore_case, 1 as clazz_ from composite_matching_rule union all select id, mode, property, value, cast(null as boolean) as ignore_case, 2 as clazz_ from number_matching_rule union all select id, mode, property, value, ignore_case, 3 as clazz_ from string_matching_rule ) matchingru1_ on trainingre0_.matching_rule_id=matchingru1_.id left outer join composite_matching_rule_matching_rules matchingru2_ on matchingru1_.id=matchingru2_.composite_matching_rule_id left outer join ( select id, mode, cast(null as varchar(100)) as property, cast(null as int) as value, cast(null as boolean) as ignore_case, 1 as clazz_ from composite_matching_rule union all select id, mode, property, value, cast(null as boolean) as ignore_case, 2 as clazz_ from number_matching_rule union all select id, mode, property, value, ignore_case, 3 as clazz_ from string_matching_rule ) matchingru3_ on matchingru2_.matching_rules_id=matchingru3_.id where trainingre0_.id=?]
但让我向您展示希望使其更清晰的域模型:
TrainingRequestSubscription(读取时产生错误的实体 I):
@Entity
@Data
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
@NoArgsConstructor
@Builder
@RequiredArgsConstructor
@ValidTrainingRequestSubscription
public class TrainingRequestSubscription extends AbstractBaseEntity implements TenantScoped
@NotNull
@JsonProperty(access = Access.WRITE_ONLY)
private String tenant;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@NotNull
private @NonNull MatchingRule matchingRule;
匹配规则
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Getter
@Setter
@NoArgsConstructor
// @formatter:off
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes(
@Type(value = NumberMatchingRule.class, name = "number"),
@Type(value = StringMatchingRule.class, name = "string"),
@Type(value = CompositeMatchingRule.class, name = "composite")
)
//@formatter:on
public abstract class MatchingRule extends AbstractBaseEntity
@Transient
@JsonIgnore
public abstract Set<String> getProperties();
@Transient
@JsonProperty(access = Access.READ_ONLY)
public abstract String getType();
NumberMatchingRule(这是我正在测试的匹配规则的实现)
@Entity
@Data
@Builder
@NoArgsConstructor
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class NumberMatchingRule extends MatchingRule
public static enum Mode
LESS, LESS_EQUAL, EQUAL, GREATER_EQUAL, GREATER
@Enumerated(EnumType.STRING)
@Builder.Default
private @NonNull Mode mode = Mode.EQUAL;
@NotNull
private @NonNull Double value;
@NotNull
@Size(min = 1)
private @NonNull String property;
@Override
public Set<String> getProperties()
return Set.of(property);
@Transient
@JsonProperty(access = Access.READ_ONLY)
public String getType()
return "number";
这一切都在 MySQL 上运行良好,堆栈跟踪没有给我任何可能导致问题的方向。所以我在这里依靠你的经验。
也许发布 repo 本身也很有用,因为它可以包含影响行为的 @Query 注释,但我只有在 @Query 上不应该涉及的 findAll() 方法上。事实上,使用 findAll() 的测试会成功。
@CrossOrigin
public interface TrainingRequestSubscriptionRepo extends CrudRepository<TrainingRequestSubscription, UUID>
@PreAuthorize("isFullyAuthenticated() and hasAnyScopeFor('trainingrequestsubscription', 'read')")
@Query("SELECT e FROM ##entityName e WHERE CONCAT(e.tenant.id, '') IN ?#security.getTenants('trainingrequestsubscription', 'r') OR '*' IN ?#security.getTenants('trainingrequestsubscription', 'r')")
@Override
Set<TrainingRequestSubscription> findAll();
@PreAuthorize("isFullyAuthenticated() and hasAnyScopeFor('trainingrequestsubscription', 'read')")
@PostAuthorize("hasPermission(returnObject, 'read')")
@Override
Optional<TrainingRequestSubscription> findById(UUID id);
// @formatter:off
@PreAuthorize(
"isFullyAuthenticated() and " +
"(" +
"(#entity.id == null and hasPermission(#entity, 'create'))" + " or " +
"(#entity.id != null and hasPermission(#entity, 'update'))" +
")")
// @formatter:on
@Override
<S extends TrainingRequestSubscription> S save(@Param("entity") S entity);
@PreAuthorize("isFullyAuthenticated() and hasPermission(#entity, 'delete')")
@Override
void delete(@Param("entity") TrainingRequestSubscription entity);
编辑:
正如 cmets 中所建议的,我在服务器模式下使用 --silent false
和 `--trace true`` 运行 HSQLDB 以查看传入的请求是什么:
[Server@2e0fa5d3]: 0:SQLCLI:SQLPREPARE insert into number_matching_rule (mode, property, value, id) values (?, ?, ?, ?)
[Server@2e0fa5d3]: 0:SQLCLI:SQLEXECUTE:1
[Server@2e0fa5d3]: 0:SQLCLI:SQLFREESTMT:1
[Server@2e0fa5d3]: 0:SQLCLI:SQLPREPARE insert into training_request_subscription (matching_rule_id, tenant, id) values (?, ?, ?)
[Server@2e0fa5d3]: 0:SQLCLI:SQLEXECUTE:2
[Server@2e0fa5d3]: 0:SQLCLI:SQLFREESTMT:2
[Server@2e0fa5d3]: 0:SQLCLI:SQLENDTRAN:COMMIT
[Server@2e0fa5d3]: 0:HSQLCLI:SETSESSIONATTR:
[Server@2e0fa5d3]: 0:HSQLCLI:SETSESSIONATTR:
[Server@2e0fa5d3]: 0:HSQLCLI:GETSESSIONATTR
[Server@2e0fa5d3]: 0:HSQLCLI:SETSESSIONATTR:
[Server@2e0fa5d3]: 0:SQLCLI:SQLPREPARE select trainingre0_.id as id1_15_0_, trainingre0_.matching_rule_id as matching3_15_0_, trainingre0_.tenant as tenant2_15_0_, matchingru1_.id as id1_2_1_, matchingru1_.mode as mode1_0_1_, matchingru1_.mode as mode1_3_1_, matchingru1_.property as property2_3_1_, matchingru1_.value as value3_3_1_, matchingru1_.ignore_case as ignore_c1_6_1_, matchingru1_.mode as mode2_6_1_, matchingru1_.property as property3_6_1_, matchingru1_.value as value4_6_1_, matchingru1_.clazz_ as clazz_1_, matchingru2_.composite_matching_rule_id as composit1_1_2_, matchingru3_.id as matching2_1_2_, matchingru3_.id as id1_2_3_, matchingru3_.mode as mode1_0_3_, matchingru3_.mode as mode1_3_3_, matchingru3_.property as property2_3_3_, matchingru3_.value as value3_3_3_, matchingru3_.ignore_case as ignore_c1_6_3_, matchingru3_.mode as mode2_6_3_, matchingru3_.property as property3_6_3_, matchingru3_.value as value4_6_3_, matchingru3_.clazz_ as clazz_3_ from training_request_subscription trainingre0_ left outer join ( select id, mode, cast(null as varchar(100)) as property, cast(null as int) as value, cast(null as boolean) as ignore_case, 1 as clazz_ from composite_matching_rule union all select id, mode, property, value, cast(null as boolean) as ignore_case, 2 as clazz_ from number_matching_rule union all select id, mode, property, value, ignore_case, 3 as clazz_ from string_matching_rule ) matchingru1_ on trainingre0_.matching_rule_id=matchingru1_.id left outer join composite_matching_rule_matching_rules matchingru2_ on matchingru1_.id=matchingru2_.composite_matching_rule_id left outer join ( select id, mode, cast(null as varchar(100)) as property, cast(null as int) as value, cast(null as boolean) as ignore_case, 1 as clazz_ from composite_matching_rule union all select id, mode, property, value, cast(null as boolean) as ignore_case, 2 as clazz_ from number_matching_rule union all select id, mode, property, value, ignore_case, 3 as clazz_ from string_matching_rule ) matchingru3_ on matchingru2_.matching_rules_id=matchingru3_.id where trainingre0_.id=?
对我来说,它看起来很像 Hibernate 记录的内容(见上文)。老实说,我不知道它应该是什么样子,所以我很难说是哪里出了问题。
编辑: @fredt 的回答建议number_matching_rule
表的property
列不是varchar
类型。以下 HSQL 数据库管理器显示的屏幕截图实际上是varchar
。
【问题讨论】:
你需要找出并报告Spring生成的SQL查询编译失败。一种方法是使用quiet=false
在服务器模式下运行 HSQLDB,并检查提交到服务器的查询。
谢谢,@fredt。请检查上面的编辑。
【参考方案1】:
错误消息在包含 UNION ALL 的子查询之一中引发,并且与表中名为 property
的列的类型有关,例如 number_matching_rule
。第一个 SELECT 将类型定义为 varchar(100)
并且名为 property
的表列的类型不是字符串(VARCHAR 等)。
例如:
select id, mode, cast(null as varchar(100)) as property, cast(null as int) as value, cast(null as boolean) as ignore_case, 1 as clazz_ from composite_matching_rule
union all
select id, mode, property, value, cast(null as boolean) as ignore_case, 2 as clazz_ from number_matching_rule
union all
select id, mode, property, value, ignore_case, 3 as clazz_ from string_matching_rule
检查生成的表并查看property
列使用的SQL 类型。还要检查其他列的类型兼容性。
如果查询是手写的,则 CAST(property AS VARCHAR(100))
之类的转换会将列转换为所需的类型。
编辑:
您可以在 DatabaseManager 中检查查询:
EXPLAIN PLAN FOR select trainingre0_.id ...
如果抛出异常,分别尝试包含联合的两个子查询并简化,直到找到导致异常的列。
【讨论】:
感谢您的跟进。我在原始帖子中附加了一个屏幕截图,证明property
列的类型为varchar
。如果我理解正确,您怀疑情况并非如此。 number_matching_rule
表中的其他列是:ID (varchar)、MODE (varchar)、VALUE (DOUBLE)。 string_matching_rule
具有 ID (varchar)、IGNORE_CASE (boolean)、MODE (varchar)、PROPERTY (varchar)、VALUE (varchar)。两个表中的value
列不同。会是这个原因吗?以上是关于HsqlException:不兼容的数据类型组合的主要内容,如果未能解决你的问题,请参考以下文章
Java HQL org.hsqldb.HsqlException:用户缺少权限或找不到对象
在 prepareStatement 中使用 COALESCE 来“插入”cmd
原因:org.hsqldb.HsqlException: invalid statemnet - 导入 CSV 数据时需要文本表