You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBootTest集成H2内存库执行data.sql报ID列不允许为NULL错误

SpringBoot单元测试H2模拟Oracle插入报ID非空错误

问题场景

使用SpringBoot 2.7.0做单元测试时,采用H2内存数据库模拟生产环境Oracle11g,通过data.sql初始化测试数据,执行插入操作时抛出NULL not allowed for column "ID"报错。已尝试在INSERT语句中显式指定Id字段并配置default默认值、在JDBC连接URL中设置Mode=LEGACY,均未解决问题。

相关代码与配置

data.sql 初始化脚本

INSERT INTO CSM_SECURITY.csm_person(EMAIL, PERSON_NAME, SYS_ADMIN, DEFAULT_LANGUAGE, WSO2_ID, SYS_ADM_COUNTRY_ID, SYS_ADM_COUNTRY) 
VALUES ('admin@wso2.com','admin','Y','es-ar',NULL,NULL,NULL);

Person 实体类定义

@Entity
@Table(name = "csm_person", uniqueConstraints = @UniqueConstraint(columnNames = "email"))
public class Person implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PERSON_SEQ")
    @SequenceGenerator(sequenceName = "CSM_PERSON_ID_SEQ", allocationSize = 1, name = "PERSON_SEQ")
    private Long id;
    @Column(name = "email")
    private String email;
    @Column(name = "person_name")
    private String personName;
    // 其余属性省略
    ...
}

测试环境配置 src/test/resources/application.properties

# SQL properties
spring.jpa.defer-datasource-initialization=true
spring.jpa.database-platform = org.hibernate.dialect.H2Dialect
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.format_sql = true
spring.jpa.properties.hibernate.default_schema = CSM_SECURITY
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.event.merge.entity_copy_observer = allow
spring.jpa.open-in-view = false
spring.datasource.driver-class-name = org.h2.Driver
spring.sql.init.platform = h2
spring.datasource.name = CSM_SECURITY
spring.datasource.url = jdbc:h2:mem:CSM_SECURITY;MODE=Oracle;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS CSM_SECURITY
spring.sql.init.mode = embedded
spring.datasource.username = sa

pom.xml 父依赖配置

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.0</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

完整报错信息

Caused by: org.springframework.jdbc.datasource.init.ScriptStatementFailedException: 
Failed to execute SQL script statement #1 of 
URL [file:/home/ainacio/Development/csm-security/target/test-classes/data.sql]: 
INSERT INTO CSM_SECURITY.csm_person(EMAIL, PERSON_NAME, SYS_ADMIN, DEFAULT_LANGUAGE, WSO2_ID, SYS_ADM_COUNTRY_ID, SYS_ADM_COUNTRY) 
VALUES ('admin@wso2.com','admin','Y','es-ar',NULL,NULL,NULL); 
nested exception is org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "ID"; SQL statement:

问题根因

报错核心是执行data.sql的原生INSERT时,不会触发JPA/Hibernate配置的主键生成逻辑:

  • 实体类上的@GeneratedValue序列主键策略,仅在Hibernate自身做持久化操作时生效
  • Spring执行data.sql是直接通过JDBC提交原生SQL,此时ID字段没有配置关联序列的默认值,插入时又未显式传入ID值,H2直接给ID赋值NULL,触发主键非空约束
  • 之前尝试的Mode=LEGACY是H2旧版本兼容参数,和Oracle模式下的序列默认值逻辑无关;配置default默认值时如果没有正确关联CSM_PERSON_ID_SEQ序列,也不会生效。

解决方案

两种方案任选其一即可,优先选第一种和生产Oracle行为对齐:

  • 方案1:通过schema.sql显式定义表结构,给ID字段绑定序列默认值
    在test/resources下新建schema.sql,手动创建序列和表,给ID列配置序列默认值,和生产Oracle逻辑保持一致:
    -- 建序列
    CREATE SEQUENCE IF NOT EXISTS CSM_SECURITY.CSM_PERSON_ID_SEQ START WITH 1 INCREMENT BY 1;
    -- 建表,ID字段默认取序列下一个值
    CREATE TABLE IF NOT EXISTS CSM_SECURITY.csm_person (
        id BIGINT DEFAULT CSM_SECURITY.CSM_PERSON_ID_SEQ.NEXTVAL NOT NULL PRIMARY KEY,
        email VARCHAR(255),
        person_name VARCHAR(255),
        sys_admin VARCHAR(1),
        default_language VARCHAR(20),
        wso2_id VARCHAR(255),
        sys_adm_country_id BIGINT,
        sys_adm_country VARCHAR(255),
        -- 其余字段按实体类定义补全即可
        CONSTRAINT uk_csm_person_email UNIQUE (email)
    );
    
    配置后不需要修改现有data.sql的INSERT语句,插入时不传ID会自动生成,和生产环境行为完全一致。
  • 方案2:修改INSERT语句,显式调用序列生成ID
    如果不想手写建表语句,直接修改data.sql的INSERT语句,插入时显式取序列值作为ID传入:
    INSERT INTO CSM_SECURITY.csm_person(ID, EMAIL, PERSON_NAME, SYS_ADMIN, DEFAULT_LANGUAGE, WSO2_ID, SYS_ADM_COUNTRY_ID, SYS_ADM_COUNTRY) 
    VALUES (CSM_SECURITY.CSM_PERSON_ID_SEQ.NEXTVAL, 'admin@wso2.com','admin','Y','es-ar',NULL,NULL,NULL);
    

注意:不要依赖Hibernate自动建表生成的结构,Hibernate在H2 Oracle兼容模式下自动建表时,不会给主键列自动绑定序列默认值,原生SQL执行时依然会报ID为空的错误。


内容的提问来源于stack exchange,提问作者Aldo Inácio da Silva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 20:48:17