将 Spring Boot 从版本
2.7.14
升级到 3.1.2
后,我收到以下错误。
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)
at org.hibernate.engine.spi.ActionQueue.beforeTransactionCompletion(ActionQueue.java:548)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:1967)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:439)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:169)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:267)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:561)
... 143 common frames omitted
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)
at io.hypersistence.utils.hibernate.type.json.internal.JsonJdbcTypeDescriptor.sqlTypeDescriptor(JsonJdbcTypeDescriptor.java:72)
at io.hypersistence.utils.hibernate.type.json.internal.JsonJdbcTypeDescriptor$1.doBind(JsonJdbcTypeDescriptor.java:40)
at org.hibernate.type.descriptor.jdbc.BasicBinder.bind(BasicBinder.java:61)
<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进行审计,因此我将版本更改为
6.2.6.Final
:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-envers</artifactId>
<version>6.2.6.Final</version>
</dependency>
CUSTOMER_LOCALIZED_FULLNAME
将列 @Type(JsonType.class)
映射为 JSON,请参阅
CustomerDetailsEntity
。<dependency>
<groupId>io.hypersistence</groupId>
<artifactId>hypersistence-utils-hibernate-62</artifactId>
<version>3.5.1</version>
</dependency>
经过一些调试,我意识到问题与Hibernate Envers和Hypersistence Utils机制有关,用于在
Hibernate Envers生成的CCS_CUSTOMER_DETAILS_AUDIT_LOG实体中处理
@Type(JsonType.class)
。
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)
它使用 H2 工作,因此问题涉及到 Oracle 方言。
您在迁移到 Spring Boot 3 时是否遇到过类似的问题?如果是这样,你是如何解决的?
数据库表:
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;
我相信,该问题与 hipersistence-utils-hibernate-62 版本
3.5.1
和 hibernate-envers 版本 6.2.1.Final
之间的不兼容问题有关。
我已经解决了这个问题,为
private Map<Alphabet, FullName> localizedFullName;
字段实现了自定义转换器:
@Getter
@Setter
@Entity
@Audited
@Table(name = "CCS_CUSTOMER_DETAILS")
public class CustomerDetailsEntity {
// code omitted
@Convert(converter = FullNameMapToJsonConverter.class)
@Column(name = "CUSTOMER_LOCALIZED_FULLNAME", nullable = false)
private Map<Alphabet, FullName> localizedFullName;
}
@Converter
class FullNameMapToJsonConverter implements AttributeConverter<Map<Alphabet, FullName>, String> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(Map<Alphabet, FullName> customerInfo) {
if (customerInfo == null) return "{}";
try {
return objectMapper.writeValueAsString(customerInfo);
} catch (Exception ex) {
new IllegalArgumentException("JSON writing error", ex);
}
}
@Override
public Map<Alphabet, FullName> convertToEntityAttribute(String customerInfoJSON) {
if (customerInfoJSON == null || customerInfoJSON.isBLank()) return Map.of();
try {
return bjectMapper.readValue(customerInfoJSON, new TypeReference<Map<Alphabet, FullName>>() {
});
} catch (Exception ex) {
new IllegalArgumentException("JSON reading error", ex);
}
}
}