Spring Boot3.2+Hibernate升级后Postgres存储异常及类型转换报错
问题概述
出现错误:java.lang.ClassCastException: class java.time.LocalDateTime cannot be cast to class [Ljava.lang.Object; (java.time.LocalDateTime and [Ljava.lang.Object; are in module java.base of loader 'bootstrap')
详细背景
将Spring Boot从2.7.x版本升级到3.2版本后,应用无法启动,对某实体字段做了如下修改:
// 旧代码 @Column(name = "last_modified") @Convert(converter = LocalDateTimeConverter.class) private LocalDateTime lastModified; // 新代码 @Column(name = "last_modified") @JdbcType(BinaryJdbcType.class) @Convert(converter = LocalDateTimeConverter.class) private LocalDateTime lastModified;
Postgres中原有last_modified列数据大小为51字节,但修改后新增行的该列大小变为430字节。
使用如下转换器,数据存入数据库前和取出后大小均为51字节,但旧数据读取时引发异常:
@Override public Byte[] convertToDatabaseColumn(final LocalDateTime localDateTime) { log.info("Inside convertToDatabaseColumn"); if (localDateTime == null) { return null; } try (final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); final ObjectOutputStream oos = new ObjectOutputStream(buffer)) { oos.writeObject(localDateTime); log.info("buffer size: {}", buffer.size()); return ArrayUtils.toObject(buffer.toByteArray()); } catch (Exception e) { // 异常处理省略 } } @Override public LocalDateTime convertToEntityAttribute(final Byte[] bytes) { // 旧数据不会执行此代码,旧数据可正常读取且大小为51字节 log.info("Inside convertToEntityAttribute: {}", Array.getLength(bytes)); if (bytes == null) { return null; } try (final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(ArrayUtils.toPrimitive(bytes)); final ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)) { final LocalDateTime dateTime = LocalDateTime.parse(objectInputStream.readObject().toString()); return dateTime; } catch (Exception e) { // 异常处理省略 } }
已尝试的其他方案
方案1
不添加@JdbcType注解时,应用启动失败,报错:Failed to initialize JPA EntityManagerFactory: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Unable to determine SQL type name for column 'last_modified' of table 'resource': class org.hibernate.type.internal.CustomMutabilityConvertedBasicTypeImpl cannot be cast to class org.hibernate.type.BasicPluralType (org.hibernate.type.internal.CustomMutabilityConvertedBasicTypeImpl and org.hibernate.type.BasicPluralType are in unnamed module of loader org.springframework.boot.loader.launch.LaunchedClassLoader @37a71e93)
方案2
尝试添加columnDefinition = "bytea",运行时报错:
@Column(name = "last_modified", columnDefinition = "bytea") @Convert(converter = LocalDateTimeConverter.class) private LocalDateTime lastModified;
错误信息:org.hibernate.exception.SQLGrammarException: could not execute statement [ERROR: column "last_modified" is of type bytea but expression is of type smallint[]"} Hint: You will need to rewrite or cast the expression."}
版本信息
- org.springframework.boot:spring-boot:3.2.0
- org.postgresql:postgresql:42.5.4
- org.hibernate.orm:hibernate-core:6.4.0.Final
- Java 17
问题总结
- 新数据可正常写入和读取,但数据库存储大小过大
- 新旧数据存储大小不同,引发类型转换错误
内容的提问来源于stack exchange,提问作者PSKP

