H2数据库是否支持Postgres hstore类型?Spring Boot测试适配咨询
H2数据库默认并不支持PostgreSQL的hstore扩展,这就是你执行CREATE EXTENSION hstore;时报错的核心原因。不过我们可以通过几种实用方式模拟实现兼容,满足自动化测试的读写需求:
1. 启用H2的PostgreSQL兼容模式
首先在测试数据库配置中开启H2的PostgreSQL兼容模式,让H2尽可能适配PostgreSQL的语法和行为。比如在application-test.yml里配置:
spring: datasource: url: jdbc:h2:mem:testdb;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1 driver-class-name: org.h2.Driver username: sa password:
这个配置能减少H2与PostgreSQL之间的语法冲突,让后续操作更顺畅。
2. 用TEXT/VARCHAR模拟hstore类型
修改你的测试SQL脚本,移除不被H2支持的CREATE EXTENSION hstore;语句,同时把attr列的类型从hstore改为TEXT(或VARCHAR):
CREATE TABLE my_table ( id numeric (10,0) NOT NULL, sample_id numeric (10,0), attr TEXT, -- 将hstore替换为TEXT date_inserted timestamp with time zone DEFAULT now() NOT NULL, date_updated timestamp with time zone DEFAULT now() NOT NULL, inserted_by character varying(30) DEFAULT 'MY_SERVICE' NOT NULL, updated_by character varying(30) DEFAULT 'MY_SERVICE' NOT NULL ); INSERT INTO my_table (id, sample_id, attr) VALUES (101, 43534, '"isValidate"=>"true", "categoryType"=>"3"');
调整后H2就能正常创建表和插入数据了。
3. 自定义JPA属性转换器适配双数据库
为了让同一个实体类既能适配PostgreSQL的hstore列,又能在H2的TEXT列正常工作,你可以创建一个自定义的AttributeConverter:
import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; import java.util.HashMap; import java.util.Map; import java.util.Arrays; import java.util.stream.Collectors; @Converter(autoApply = true) public class HstoreConverter implements AttributeConverter<Map<String, String>, String> { @Override public String convertToDatabaseColumn(Map<String, String> attribute) { if (attribute == null || attribute.isEmpty()) { return null; } // 转换成hstore格式的字符串 return attribute.entrySet().stream() .map(entry -> String.format("\"%s\"=>\"%s\"", entry.getKey(), entry.getValue())) .collect(Collectors.joining(", ")); } @Override public Map<String, String> convertToEntityAttribute(String dbData) { if (dbData == null || dbData.isBlank()) { return new HashMap<>(); } // 解析hstore格式字符串为Map return Arrays.stream(dbData.split(", ")) .map(entry -> entry.split("=>")) .collect(Collectors.toMap( keyPart -> keyPart[0].replace("\"", ""), valuePart -> valuePart[1].replace("\"", "") )); } }
然后在实体类的attr字段上添加注解(如果设置了autoApply = true也可以省略手动添加):
@Column(columnDefinition = "hstore") // 生产环境使用hstore类型 @Convert(converter = HstoreConverter.class) private Map<String, String> attr;
这样不管是PostgreSQL的hstore还是H2的TEXT,都能自动完成Map与字符串的双向转换,读写操作完全透明。
4. 模拟hstore查询函数(适配复杂查询场景)
如果你的测试需要用到PostgreSQL的hstore查询操作(比如attr -> 'isValidate'),可以在H2中创建模拟函数来适配:
CREATE FUNCTION hstore_get(attr TEXT, key TEXT) RETURNS TEXT AS $$ SELECT JSON_VALUE('{' || REPLACE(REPLACE(attr, '=>', ':'), ', ', ',') || '}', '$."' || key || '"') $$ LANGUAGE SQL;
之后在测试的JPQL或原生SQL中,用hstore_get(attr, 'isValidate')代替PostgreSQL的attr -> 'isValidate',就能在H2中正常执行查询了。
内容的提问来源于stack exchange,提问作者Heisenberg

