H2 2.0.202搭配Hibernate GeoDBDialect无法存储UUID类型数据问题
我们长期使用H2做集成测试,H2 2.0.202版本发布后,我们尝试对代码库进行版本升级,却发现无法持久化使用java.util.UUID类型的实体。示例如下:
public class MyEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) private UUID id; }
我们使用Hibernate向H2数据库保存该实体时会抛出JdbcSQLDataException异常,错误信息为Value too long for column。测试代码如下:
@DataJpaTest class H2Test { @Autowired MyRepository myRepository; @Test void testSave() { myRepository.save(new MyEntity()); Assertions.assertThat(myRepository.findAll()).hasSize(1); } }
我们当前使用的方言是org.hibernate.spatial.dialect.h2geodb.GeoDBDialect,这似乎是问题诱因之一:移除该方言后上述简单测试可正常运行,但我们的业务需要使用空间数据,必须保留该方言。想咨询这是H2 2.0.202与Hibernate之间存在兼容性缺失,还是我们可以通过调整配置解决该问题?我在Hibernate Jira中未找到匹配的相关问题,也暂时无法创建新Issue。
问题原因
该问题由GeoDBDialect与H2 2.x版本的兼容性问题导致:
- H2 2.0+版本新增了原生
UUID数据类型,取代了1.x版本中用VARCHAR(36)或BINARY(16)存储UUID的方案 - 目前官方发布的
GeoDBDialect仅适配H2 1.x版本,未注册H2 2.x的UUID类型映射,默认将java.util.UUID映射为BINARY(16)类型 - 未匹配到原生UUID映射时,Hibernate会将UUID对象序列化为36位长度的字符串写入数据库,长度远超
BINARY(16)的16字节容量,因此抛出Value too long for column异常
可用解决方案
方案1:自定义扩展方言(推荐,无需修改实体代码)
继承GeoDBDialect手动新增H2 2.x的UUID类型映射,替换原有方言配置即可:
public class H22xGeoDBDialect extends GeoDBDialect { public H22xGeoDBDialect() { super(); // 注册H2 2.x原生UUID类型映射 registerColumnType(java.sql.Types.OTHER, "uuid"); registerHibernateType(java.sql.Types.OTHER, "uuid"); } }
将Hibernate配置项hibernate.dialect的值修改为上述自定义类的全限定名即可。
方案2:显式指定UUID字段的列定义
在UUID主键上添加@Column注解手动指定列类型,无需修改方言配置:
@Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(columnDefinition = "UUID") private UUID id;
该方案需要修改所有UUID类型的实体字段,适合实体数量较少的场景。
方案3:升级Hibernate Spatial版本
Hibernate Spatial 6.0+版本已经提供了适配H2 2.x的空间方言支持,升级到对应版本后使用官方提供的H2 2.x空间方言即可原生解决该映射问题。
内容的提问来源于stack exchange,提问作者Carsten Hoffmann

