Spring Boot 3升级后Hibernate Envers与Hypersistence Utils适配问题求助
Spring Boot 3.1.2升级后Hibernate Envers结合hypersistence-utils的JSON类型空指针问题
问题场景
将Spring Boot从2.7.14升级至3.1.2后,操作Oracle数据库时触发Hibernate空指针异常,H2环境下正常。异常栈核心信息如下:
Caused by: org.hibernate.HibernateException: Unable to perform beforeTransactionCompletion callback: Cannot read the array length because "array" is null at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:1016) ... Caused by: java.lang.NullPointerException: Cannot read the array length because "array" is null at java.base/java.util.Arrays.stream(Arrays.java:5428) at io.hypersistence.utils.hibernate.type.util.ParameterTypeUtils.getAnnotations(ParameterTypeUtils.java:76) at io.hypersistence.utils.hibernate.type.util.ParameterTypeUtils.getAnnotationOrNull(ParameterTypeUtils.java:53) at io.hypersistence.utils.hibernate.type.util.ParameterTypeUtils.getColumnType(ParameterTypeUtils.java:90) at io.hypersistence.utils.hibernate.type.json.internal.JsonJdbcTypeDescriptor.resolveJdbcTypeDescriptor(JsonJdbcTypeDescriptor.java:90)
经调试确认,问题出在Hibernate Envers生成的审计实体处理@Type(JsonType.class)注解时,因无法获取有效注解信息导致空指针。
环境配置
- 数据库:Oracle Database 19c Enterprise Edition(19.14.0.0.0)
JDBC依赖:<dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>21.1.0.0</version> <scope>runtime</scope> </dependency> - Hibernate:6.2.6.Final(Spring Boot管理),使用Hibernate Envers审计
依赖:<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-envers</artifactId> <version>6.2.6.Final</version> </dependency> - JSON类型映射:使用hypersistence-utils的
JsonType映射CUSTOMER_LOCALIZED_FULLNAME列
依赖:<dependency> <groupId>io.hypersistence</groupId> <artifactId>hypersistence-utils-hibernate-62</artifactId> <version>3.5.1</version> </dependency>
相关代码与表结构
数据库表结构
CREATE TABLE CCS_CUSTOMER_DETAILS ( CUSTOMER_NUMBER VARCHAR2(9) NOT NULL CONSTRAINT CCS_CUSTOMER_DETAILS_PK PRIMARY KEY, CUSTOMER_EGN VARCHAR2(10) NOT NULL CONSTRAINT CCS_CUSTOMER_DETAILS_EGN_UNIQUE UNIQUE, CUSTOMER_IS_AGREED_TO_USE_OF_DATA NUMBER(1, 0) NOT NULL, CUSTOMER_LOCALIZED_FULLNAME VARCHAR2(4000) NOT NULL CONSTRAINT LOCALIZED_FULLNAME_IS_JSON CHECK (CUSTOMER_LOCALIZED_FULLNAME IS JSON), DRIVING_LICENSE_NUMBER VARCHAR2(9), DRIVING_LICENSE_EXPIRATION_DATE DATE, CUSTOMER_STATUS SMALLINT NOT NULL, CREATED_ON TIMESTAMP NOT NULL, LAST_UPDATED_ON TIMESTAMP NOT NULL ); CREATE TABLE CCS_AUDIT_REVISIONS_INFO ( REVISION_NUMBER NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY CONSTRAINT CCS_SUBSCRIPTION_SERVICES_PK PRIMARY KEY, REVISION_TIMESTAMP NUMBER ); CREATE TABLE CCS_CUSTOMER_DETAILS_AUDIT_LOG ( REVISION_NUMBER_START NUMBER NOT NULL, REVISION_NUMBER_END NUMBER, REVISION_TYPE SMALLINT, CUSTOMER_NUMBER VARCHAR2(9), CUSTOMER_IS_AGREED_TO_USE_OF_DATA NUMBER(1, 0), CUSTOMER_LOCALIZED_FULLNAME VARCHAR2(4000), CUSTOMER_EGN VARCHAR2(10), DRIVING_LICENSE_NUMBER VARCHAR2(9), DRIVING_LICENSE_EXPIRATION_DATE DATE, CUSTOMER_STATUS SMALLINT, CREATED_ON TIMESTAMP, LAST_UPDATED_ON TIMESTAMP, CONSTRAINT CCS_AUDIT_REVISIONS_INFO_START_FK FOREIGN KEY (REVISION_NUMBER_START) REFERENCES CCS_AUDIT_REVISIONS_INFO, CONSTRAINT CCS_AUDIT_REVISIONS_INFO_END_FK FOREIGN KEY (REVISION_NUMBER_END) REFERENCES CCS_AUDIT_REVISIONS_INFO, CONSTRAINT CCS_CUSTOMER_DETAILS_AUDIT_LOG_PK PRIMARY KEY (CUSTOMER_NUMBER, REVISION_NUMBER_START) );
JPA实体代码
@Getter @Setter @Entity @Audited @Table(name = "CCS_CUSTOMER_DETAILS") public class CustomerDetailsEntity { @Id @Column(name = "CUSTOMER_NUMBER", nullable = false) private String customerNumber; @Column(name = "CUSTOMER_EGN", unique = true, nullable = false) private String egn; @Column(name = "DRIVING_LICENSE_NUMBER", unique = true, nullable = false) private String drivingLicenseNumber; @Column(name = "DRIVING_LICENSE_EXPIRATION_DATE", nullable = false) private LocalDate drivingLicenseExpirationDate; @Column(name = "CUSTOMER_IS_AGREED_TO_USE_OF_DATA", nullable = false) private boolean agreedToUseOfData; @Type(JsonType.class) @Column(name = "CUSTOMER_LOCALIZED_FULLNAME", nullable = false) private Map<Alphabet, FullName> localizedFullName; @Enumerated @Column(name = "CUSTOMER_STATUS", nullable = false) private Status status; @CreationTimestamp @Column(name = "CREATED_ON", updatable = false) private LocalDateTime createdOn; @UpdateTimestamp @Column(name = "LAST_UPDATED_ON") private LocalDateTime lastUpdatedOn; } @Getter @Setter @Entity @RevisionEntity @Table(name = "CCS_AUDIT_REVISIONS_INFO") public class RevisionInfoEntity { @Id @GeneratedValue(strategy = IDENTITY) @RevisionNumber @Column(name = "REVISION_NUMBER") private long id; @RevisionTimestamp @Column(name = "REVISION_TIMESTAMP") private long timestamp; }
解决方案
1. 升级hypersistence-utils版本
当前3.5.1版本存在Hibernate 6.2兼容问题,升级至3.6.0及以上稳定版,该版本修复了Envers审计实体处理自定义类型时的注解空指针问题。
修改依赖:
<dependency> <groupId>io.hypersistence</groupId> <artifactId>hypersistence-utils-hibernate-62</artifactId> <version>3.6.0</version> </dependency>
2. 显式指定JSON列的JdbcType
在实体类的JSON字段上添加@JdbcType注解,明确指定对应JDBC类型,避免Envers生成审计实体时无法识别类型:
@Type(JsonType.class) @JdbcType(VarcharJdbcType.class) // 对应Oracle的VARCHAR2类型 @Column(name = "CUSTOMER_LOCALIZED_FULLNAME", nullable = false) private Map<Alphabet, FullName> localizedFullName;
3. 配置Hibernate Envers的类型映射
在application.yml中添加Envers配置,确保自定义类型注解能被复制到审计实体:
spring: jpa: properties: org: hibernate: envers: replicate_annotations: true
4. 升级Oracle JDBC驱动版本
将ojdbc8升级至与Oracle 19c匹配的稳定版(如21.9.0.0),避免驱动与Hibernate 6.2的兼容性问题:
<dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>21.9.0.0</version> <scope>runtime</scope> </dependency>
内容的提问来源于stack exchange,提问作者eHayik
相关产品推荐
相关产品推荐

