CREATE TABLE test (
id VARCHAR2(64),
a VARCHAR2(3) DEFAULT '0',
name VARCHAR2(100)
);
INSERT INTO test (a, name) VALUES (NULL, 'test'); -- 顯式插入NULL
SELECT * FROM test;
-- 結果:A列為NULL,而非預期的'0'!
根因: Oracle默認值僅在列完全缺失於INSERT列清單時生效;顯式指定列並傳入NULL會覆蓋默認值。
INSERT INTO test (a, name) VALUES (DEFAULT, 'test');
-- 結果:A='0' ✓
INSERT INTO test (name) VALUES ('test');
-- 結果:A='0' ✓
Hibernate/MyBatis/JPA默認生成INSERT INTO table (col1, col2, ...) VALUES (?, ?, ...),若實體屬性為null則綁定NULL,導致默認值失效。
| 策略 | 實現方式 | 優點 | 缺點 |
|---|---|---|---|
| 實體層非空判斷 | Setter中if (val==null) return; | 簡單 | 侵入業務模型,無法區分「業務NULL」與「使用默認值」 |
| 動態SQL排除NULL列 | MyBatis<trim>、Hibernate@DynamicInsert | 非侵入,精準觸發默認值 | 批量插入可能生成大量不同SQL,影響解析緩存 |
| 數據庫觸發器兜底 | BEFORE INSERT ... IF :NEW.col IS NULL THEN :NEW.col := DEFAULT_VAL; | 絕對保障,兼容所有客戶端 | 隱藏邏輯,調試困難;性能微損 |
| 虛擬列(11g+) | col VARCHAR2(3) GENERATED ALWAYS AS (NVL(col_raw, '0')) | 邏輯可見、無觸發器開銷 | 需區分物理列與虛擬列,應用層適配成本高 |
-- 時間維度
DEFAULT SYSDATE -- 當前日期時間
DEFAULT SYSTIMESTAMP -- 含時區時間戳
DEFAULT CURRENT_TIMESTAMP -- 會話時區時間戳
-- 用戶/環境維度
DEFAULT USER -- 當前數據庫用戶
DEFAULT SYS_CONTEXT('USERENV','IP_ADDRESS') -- 客戶端IP
DEFAULT SYS_CONTEXT('USERENV','TERMINAL') -- 終端標識
DEFAULT SYS_CONTEXT('USERENV','MODULE') -- 應用模塊名
-- 唯一標識(12c+推薦)
DEFAULT SYS_GUID() -- 32位全局唯一標識,無序列爭用
-- 以下均報錯:ORA-00976: Specified pseudocolumn or operator not allowed here
DEFAULT LEVEL
DEFAULT PRIOR
DEFAULT ROWNUM
CREATE SEQUENCE seq_test;
CREATE OR REPLACE TRIGGER tri_test
BEFORE INSERT ON test FOR EACH ROW
BEGIN
IF :NEW.id IS NULL THEN
SELECT seq_test.NEXTVAL INTO :NEW.id FROM DUAL;
END IF;
END;
/
-- 適用:業務需連續號、可接受序列緩存間隙、并發中等
ALTER TABLE TEST MODIFY ID DEFAULT SYS_GUID();
INSERT INTO test (name) VALUES ('張三');
-- ID自動生成:7CDB1AF556F6474FABA74FA7A60F0822
-- 適用:無業務含義要求、極高並發寫入、分庫分表/多主同步
CREATE TABLE test (
id NUMBER GENERATED ALWAYS AS IDENTITY, -- 或 GENERATED BY DEFAULT
name VARCHAR2(100)
);
-- 內部自動創建序列,性能最優、語義最清晰
Oracle默認值機制看似簡單,實則暗藏NULL vs 缺失的語義陷阱。對於現代應用架構,建議採用「數據庫層虛擬列/Identity列作為邏輯基石 + ORM層動態SQL排除NULL列」的雙重保障策略,實現業務語義清晰、性能最優、跨框架兼容的默認值管理體系。