ORA-01400或“Column cannot be null”报错源于数据库对空字符串处理差异:Oracle将''视作NULL,触发NOT NULL约束;而MySQL、PostgreSQL不等价转换。需验证字段定义、驱动行为及SQL模式,优先依据SQLState(如23502)和错误码(如1048)精准定位,而非依赖异常文本。

INSERT报ORA-01400或“Column cannot be null”时先确认数据库对空字符串的处理
Oracle 会把 '' 当成 NULL,而 MySQL、PostgreSQL 不会。Java 里传了 "",到 Oracle 就触发了 NOT NULL 约束失败——这不是你代码漏判 null,是数据库行为差异。
验证方法:在 SQL Developer 或 sqlplus 里直接执行 INSERT INTO t(col) VALUES ('');,如果报 ORA-01400,就坐实了这点。
- 业务层别用
""当“占位默认值”,改用明确有意义的字符串(如'N/A')或允许字段为 NULL - MyBatis 中可通过
@Options(useGeneratedKeys = false)配合<if test="email != null and email != ''"></if>过滤空串 - JDBC 层可加拦截器,在 setString 前把
""替换成null或跳过赋值(需同步调整 NOT NULL 约束)
NOT NULL 字段插入失败但值不为 NULL?检查隐式转换和驱动行为
MySQL 5.7+ 开启 STRICT_TRANS_TABLES 后,INSERT INTO t(a) VALUES ('abc') 往 INT 字段插字符串,不会转成 0,而是直接报错 Column 'a' cannot be null——因为转换失败后值变成 NULL,再撞上 NOT NULL 约束。
PostgreSQL 更严格:text 列能接 '',但往 INT 插 '123' 必须显式写 CAST('123' AS int),否则报错。
- 查当前 SQL mode:
SELECT @@sql_mode;,临时关 strict 模式验证是否为此原因:SET sql_mode = ''; - Java JDBC 中,
PreparedStatement.setString()对数字列传字符串,不同驱动行为不一致;稳妥做法是用setInt()/setLong()等类型匹配方法 - SQL Server 的
ANSI_WARNINGS OFF可能掩盖截断警告,导致插入后字段变 NULL,务必开ANSI_WARNINGS ON
外键字段插入 NULL 失败?确认约束是否允许 NULL
外键列本身可以为 NULL,但前提是定义时没加 NOT NULL。常见错误是:主表字段允许 NULL,子表外键字段却定义成 NOT NULL,结果插 NULL 直接被拦下。
查结构用:SELECT COLUMN_NAME, IS_NULLABLE, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'orders' AND COLUMN_NAME = 'customer_id';
- 如果
IS_NULLABLE = 'NO'但业务需要支持未关联状态,就得改列:ALTER TABLE orders ALTER COLUMN customer_id INT NULL; - 自引用外键(如课程表的先修课)必须允许
NULL,且插入时明确写NULL,不能写''或字符串'NULL' - SQL Server 中,外键约束名会出现在错误信息里,例如
FK_Orders_CustomerID,直接用它定位具体约束
唯一索引 + NOT NULL 导致的复合失败要拆开看
一个字段同时有 UNIQUE 和 NOT NULL,报错时容易误判。比如 email VARCHAR(100) UNIQUE NOT NULL,插 NULL 报 NOT NULL 错,插重复邮箱报唯一约束错,但两者错误码不同。
关键点:MySQL 的 SQLIntegrityConstraintViolationException 会带 SQLState 和错误号,必须读原始异常而不是日志摘要。
- MySQL 错误号
1048是 NOT NULL 违反,1062是唯一约束冲突 - PostgreSQL 的
SQLSTATE:23502表示 NOT NULL,23505表示唯一冲突 - 不要依赖异常消息文本(如 “cannot insert null”),它可能被本地化或截断;优先取
getSQLState()和getErrorCode()

















